diff --git a/plugins/action/__init__.py b/plugins/action/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/action/aireos.py b/plugins/action/aireos.py deleted file mode 100644 index 84daba2724..0000000000 --- a/plugins/action/aireos.py +++ /dev/null @@ -1,79 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import copy - -from ansible import constants as C -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule -from ansible_collections.community.general.plugins.module_utils.network.aireos.aireos import aireos_provider_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import load_provider -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 - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'aireos_config' else False - - if self._play_context.connection != 'local': - return dict( - failed=True, - msg='invalid connection specified, expected connection=local, ' - 'got %s' % self._play_context.connection - ) - - provider = load_provider(aireos_provider_spec, self._task.args) - - pc = copy.deepcopy(self._play_context) - pc.connection = 'network_cli' - pc.network_os = 'aireos' - 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 - command_timeout = int(provider['timeout'] or C.PERSISTENT_COMMAND_TIMEOUT) - - display.vvv('using connection plugin %s (was local)' % pc.connection, pc.remote_addr) - connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid) - 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 - - if self._play_context.become_method == 'enable': - self._play_context.become = False - self._play_context.become_method = None - - result = super(ActionModule, self).run(task_vars=task_vars) - - return result diff --git a/plugins/action/aruba.py b/plugins/action/aruba.py deleted file mode 100644 index 7ea03c8726..0000000000 --- a/plugins/action/aruba.py +++ /dev/null @@ -1,79 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import copy - -from ansible import constants as C -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule -from ansible_collections.community.general.plugins.module_utils.network.aruba.aruba import aruba_provider_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import load_provider -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 - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'aruba_config' else False - - if self._play_context.connection != 'local': - return dict( - failed=True, - msg='invalid connection specified, expected connection=local, ' - 'got %s' % self._play_context.connection - ) - - provider = load_provider(aruba_provider_spec, self._task.args) - - pc = copy.deepcopy(self._play_context) - pc.connection = 'network_cli' - pc.network_os = 'aruba' - 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) - - display.vvv('using connection plugin %s (was local)' % pc.connection, pc.remote_addr) - connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid) - 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 - - if self._play_context.become_method == 'enable': - self._play_context.become = False - self._play_context.become_method = None - - result = super(ActionModule, self).run(task_vars=task_vars) - return result diff --git a/plugins/action/ce.py b/plugins/action/ce.py deleted file mode 100644 index 31cf3b4e6d..0000000000 --- a/plugins/action/ce.py +++ /dev/null @@ -1,89 +0,0 @@ -# -# Copyright: (c) 2016, Red Hat Inc. - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import copy - -from ansible import constants as C -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_provider_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import load_provider -from ansible.utils.display import Display - -display = Display() - -CLI_SUPPORTED_MODULES = ['ce_rollback', 'ce_mlag_interface', 'ce_startup', 'ce_config', - 'ce_command', 'ce_facts', 'ce_evpn_global', 'ce_evpn_bgp_rr', - 'ce_mtu', 'ce_evpn_bgp', 'ce_snmp_location', 'ce_snmp_contact', - 'ce_snmp_traps', 'ce_netstream_global', 'ce_netstream_aging', - 'ce_netstream_export', 'ce_netstream_template', 'ce_ntp_auth', - 'ce_stp', 'ce_vxlan_global', 'ce_vxlan_arp', 'ce_vxlan_gateway', - 'ce_acl_interface'] - - -class ActionModule(ActionNetworkModule): - - def run(self, tmp=None, task_vars=None): - del tmp # tmp no longer has any effect - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'ce_config' else False - socket_path = None - persistent_connection = self._play_context.connection.split('.')[-1] - - if self._play_context.connection == 'local': - provider = load_provider(ce_provider_spec, self._task.args) - transport = provider['transport'] or 'cli' - - display.vvvv('connection transport is %s' % transport, self._play_context.remote_addr) - - if transport == 'cli': - pc = copy.deepcopy(self._play_context) - pc.connection = 'network_cli' - pc.network_os = 'ce' - 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 - command_timeout = int(provider['timeout'] or C.PERSISTENT_COMMAND_TIMEOUT) - self._task.args['provider'] = provider.update( - host=pc.remote_addr, - port=pc.port, - username=pc.remote_user, - password=pc.password - ) - if module_name in ['ce_netconf'] or module_name not in CLI_SUPPORTED_MODULES: - pc.connection = 'netconf' - display.vvv('using connection plugin %s (was local)' % pc.connection, pc.remote_addr) - connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid) - 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 a transport value is set in args - self._task.args['transport'] = transport - self._task.args['provider'] = provider - elif persistent_connection in ('netconf', 'network_cli'): - provider = self._task.args.get('provider', {}) - if any(provider.values()): - display.warning('provider is unnecessary when using %s and will be ignored' % self._play_context.connection) - del self._task.args['provider'] - - if (persistent_connection == 'network_cli' and module_name not in CLI_SUPPORTED_MODULES) or \ - (persistent_connection == 'netconf' and module_name in CLI_SUPPORTED_MODULES): - return {'failed': True, 'msg': "Connection type '%s' is not valid for '%s' module." - % (self._play_context.connection, self._task.action)} - - result = super(ActionModule, self).run(task_vars=task_vars) - return result diff --git a/plugins/action/ce_template.py b/plugins/action/ce_template.py deleted file mode 100644 index aaacf42b6b..0000000000 --- a/plugins/action/ce_template.py +++ /dev/null @@ -1,104 +0,0 @@ -# -# Copyright 2015 Peter Sprygada -# -# 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import os -import time -import glob -from ansible.module_utils.six.moves.urllib.parse import urlsplit - -from ansible.module_utils._text import to_text -from ansible_collections.community.general.plugins.action.ce import ActionModule as _ActionModule - - -class ActionModule(_ActionModule): - - def run(self, tmp=None, task_vars=None): - - try: - self._handle_template() - except (ValueError, AttributeError) as exc: - return dict(failed=True, msg=exc.message) - - result = super(ActionModule, self).run(tmp, task_vars) - del tmp # tmp no longer has any effect - - if self._task.args.get('backup') and result.get('__backup__'): - # User requested backup and no error occurred in module. - # NOTE: If there is a parameter error, __backup__ key may not be in results. - self._write_backup(task_vars['inventory_hostname'], result['__backup__']) - - if '__backup__' in result: - del result['__backup__'] - - return result - - def _get_working_path(self): - cwd = self._loader.get_basedir() - if self._task._role is not None: - cwd = self._task._role._role_path - return cwd - - def _write_backup(self, host, contents): - backup_path = self._get_working_path() + '/backup' - if not os.path.exists(backup_path): - os.mkdir(backup_path) - for fn in glob.glob('%s/%s*' % (backup_path, host)): - os.remove(fn) - tstamp = time.strftime("%Y-%m-%d@%H:%M:%S", time.localtime(time.time())) - filename = '%s/%s_config.%s' % (backup_path, host, tstamp) - open(filename, 'w').write(contents) - - def _handle_template(self): - src = self._task.args.get('src') - if not src: - raise ValueError('missing required arguments: src') - - working_path = self._get_working_path() - - if os.path.isabs(src) or urlsplit(src).scheme: - source = src - else: - source = self._loader.path_dwim_relative(working_path, 'templates', src) - if not source: - source = self._loader.path_dwim_relative(working_path, src) - - if not os.path.exists(source): - return - - try: - with open(source, 'r') as f: - template_data = to_text(f.read()) - except IOError: - return dict(failed=True, msg='unable to load src file') - - # Create a template search path in the following order: - # [working_path, self_role_path, dependent_role_paths, dirname(source)] - searchpath = [working_path] - if self._task._role is not None: - searchpath.append(self._task._role._role_path) - if hasattr(self._task, "_block:"): - dep_chain = self._task._block.get_dep_chain() - if dep_chain is not None: - for role in dep_chain: - searchpath.append(role._role_path) - searchpath.append(os.path.dirname(source)) - with self._templar.set_temporary_context(searchpath=searchpath): - self._task.args['src'] = self._templar.template(template_data) diff --git a/plugins/action/cnos.py b/plugins/action/cnos.py deleted file mode 100644 index fa0332da5e..0000000000 --- a/plugins/action/cnos.py +++ /dev/null @@ -1,69 +0,0 @@ -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Contains Action Plugin methods for CNOS Config Module -# Lenovo Networking -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import copy - -from ansible import constants as C -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_provider_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import load_provider -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 - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'cnos_config' else False - - if self._play_context.connection == 'local': - provider = load_provider(cnos_provider_spec, self._task.args) - pc = copy.deepcopy(self._play_context) - pc.connection = 'network_cli' - pc.network_os = 'cnos' - pc.remote_addr = provider['host'] or self._play_context.remote_addr - pc.port = 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 True - pc.become_pass = provider['auth_pass'] - pc.become_method = 'enable' - - display.vvv('using connection plugin %s (was local)' % pc.connection, pc.remote_addr) - connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid) - 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 - - result = super(ActionModule, self).run(task_vars=task_vars) - return result diff --git a/plugins/action/edgeos_config.py b/plugins/action/edgeos_config.py deleted file mode 100644 index e1145da6b8..0000000000 --- a/plugins/action/edgeos_config.py +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright 2018 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule - - -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._play_context.connection.split('.')[-1] != 'network_cli': - return {'failed': True, 'msg': 'Connection type %s is not valid for this module. Must use fully qualified' - ' name of network_cli connection type.' % self._play_context.connection} - - return super(ActionModule, self).run(task_vars=task_vars) diff --git a/plugins/action/enos.py b/plugins/action/enos.py deleted file mode 100644 index 5097d95749..0000000000 --- a/plugins/action/enos.py +++ /dev/null @@ -1,69 +0,0 @@ -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Contains Action Plugin methods for ENOS Config Module -# Lenovo Networking -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import copy - -from ansible import constants as C -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule -from ansible_collections.community.general.plugins.module_utils.network.enos.enos import enos_provider_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import load_provider -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 - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'enos_config' else False - - if self._play_context.connection == 'local': - provider = load_provider(enos_provider_spec, self._task.args) - pc = copy.deepcopy(self._play_context) - pc.connection = 'network_cli' - pc.network_os = 'enos' - pc.remote_addr = provider['host'] or self._play_context.remote_addr - pc.port = 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 True - pc.become_pass = provider['auth_pass'] - pc.become_method = 'enable' - - display.vvv('using connection plugin %s (was local)' % pc.connection, pc.remote_addr) - connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid) - 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 - - result = super(ActionModule, self).run(task_vars=task_vars) - return result diff --git a/plugins/action/exos.py b/plugins/action/exos.py deleted file mode 100644 index 5d91155c12..0000000000 --- a/plugins/action/exos.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright 2015 Peter Sprygada -# -# 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule - - -class ActionModule(ActionNetworkModule): - - EXOS_NETWORK_CLI_MODULES = ( - 'exos_facts', - 'exos_config', - 'exos_command') - - def run(self, tmp=None, task_vars=None): - del tmp # tmp no longer has any effect - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'exos_config' else False - persistent_connection = self._play_context.connection.split('.')[-1] - - if persistent_connection not in ('network_cli', 'httpapi'): - return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection} - - if persistent_connection == 'network_cli' and module_name not in self.EXOS_NETWORK_CLI_MODULES: - return {'failed': True, 'msg': "Connection type %s is not valid for this module" % self._play_context.connection} - - return super(ActionModule, self).run(task_vars=task_vars) diff --git a/plugins/action/ironware.py b/plugins/action/ironware.py deleted file mode 100644 index 0a75a2f365..0000000000 --- a/plugins/action/ironware.py +++ /dev/null @@ -1,80 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import copy - -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.community.general.plugins.module_utils.network.ironware.ironware import ironware_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 - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'ironware_config' else False - persistent_connection = self._play_context.connection.split('.')[-1] - - if persistent_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(ironware_provider_spec, self._task.args) - pc = copy.deepcopy(self._play_context) - pc.connection = 'network_cli' - pc.network_os = 'ironware' - 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 - 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 (was local)' % pc.connection, pc.remote_addr) - connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid) - - command_timeout = int(provider['timeout']) if provider['timeout'] else connection.get_option('persistent_command_timeout') - 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 - else: - return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection} - - result = super(ActionModule, self).run(task_vars=task_vars) - return result diff --git a/plugins/action/nos_config.py b/plugins/action/nos_config.py deleted file mode 100644 index 3f49e1afab..0000000000 --- a/plugins/action/nos_config.py +++ /dev/null @@ -1,31 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule - - -class ActionModule(ActionNetworkModule): - - def run(self, tmp=None, task_vars=None): - del tmp # tmp no longer has any effect - - self._config_module = True - return super(ActionModule, self).run(task_vars=task_vars) diff --git a/plugins/action/onyx_config.py b/plugins/action/onyx_config.py deleted file mode 100644 index b1c9089b49..0000000000 --- a/plugins/action/onyx_config.py +++ /dev/null @@ -1,31 +0,0 @@ -# -# (c) 2017, Red Hat, 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule - - -class ActionModule(ActionNetworkModule): - - def run(self, tmp=None, task_vars=None): - del tmp # tmp no longer has any effect - - self._config_module = True - return super(ActionModule, self).run(task_vars=task_vars) diff --git a/plugins/action/slxos.py b/plugins/action/slxos.py deleted file mode 100644 index cb0478ce87..0000000000 --- a/plugins/action/slxos.py +++ /dev/null @@ -1,40 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re - -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule - -PRIVATE_KEYS_RE = re.compile('__.+__') - - -class ActionModule(ActionNetworkModule): - - def run(self, tmp=None, task_vars=None): - del tmp # tmp no longer has any effect - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'slxos_config' else False - persistent_connection = self._play_context.connection.split('.')[-1] - - if persistent_connection not in ('network_cli'): - return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection} - return super(ActionModule, self).run(task_vars=task_vars) diff --git a/plugins/action/sros.py b/plugins/action/sros.py deleted file mode 100644 index 0de5947a71..0000000000 --- a/plugins/action/sros.py +++ /dev/null @@ -1,77 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import sys -import copy - -from ansible import constants as C -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule -from ansible_collections.community.general.plugins.module_utils.network.sros.sros import sros_provider_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import load_provider -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 - - module_name = self._task.action.split('.')[-1] - persistent_connection = self._play_context.connection.split('.')[-1] - - self._config_module = True if module_name == 'sros_config' else False - if persistent_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(sros_provider_spec, self._task.args) - - pc = copy.deepcopy(self._play_context) - pc.connection = 'network_cli' - pc.network_os = 'sros' - 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) - - display.vvv('using connection plugin %s (was local)' % pc.connection, pc.remote_addr) - connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid) - 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 - else: - return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection} - - result = super(ActionModule, self).run(task_vars=task_vars) - return result diff --git a/plugins/action/voss.py b/plugins/action/voss.py deleted file mode 100644 index e7e2ca452c..0000000000 --- a/plugins/action/voss.py +++ /dev/null @@ -1,36 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule - - -class ActionModule(ActionNetworkModule): - - def run(self, tmp=None, task_vars=None): - del tmp # tmp no longer has any effect - - module_name = self._task.action.split('.')[-1] - self._config_module = True if module_name == 'voss_config' else False - persistent_connection = self._play_context.connection.split('.')[-1] - - if persistent_connection not in ('network_cli'): - return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection} - return super(ActionModule, self).run(task_vars=task_vars) diff --git a/plugins/cliconf/__init__.py b/plugins/cliconf/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/cliconf/aireos.py b/plugins/cliconf/aireos.py deleted file mode 100644 index 438e4972cc..0000000000 --- a/plugins/cliconf/aireos.py +++ /dev/null @@ -1,95 +0,0 @@ -# -# (c) 2017 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: aireos -short_description: Use aireos cliconf to run command on Cisco WLC platform -description: - - This aireos plugin provides low level abstraction apis for - sending and receiving CLI commands from Cisco WLC network devices. -''' - -import re -import json - -from itertools import chain - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import 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'] = 'aireos' - reply = self.get('show sysinfo') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'Product Version\.* (.*)', data) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'System Name\.* (.*)', data, re.M) - if match: - device_info['network_os_hostname'] = match.group(1) - - reply = self.get('show inventory') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'DESCR: \"(.*)\"', data, re.M) - if match: - device_info['network_os_model'] = 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 run-config commands' - else: - cmd = 'show run-config startup-commands' - return self.send_command(cmd) - - @enable_mode - def edit_config(self, command): - for cmd in chain(['config'], to_list(command), ['end']): - self.send_command(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) - - def set_cli_prompt_context(self): - """ - Make sure we are in the operational cli mode - :return: None - """ - if self._connection.connected: - self._update_cli_prompt_context(config_context=')#') diff --git a/plugins/cliconf/apconos.py b/plugins/cliconf/apconos.py deleted file mode 100644 index 03c7e7a1de..0000000000 --- a/plugins/cliconf/apconos.py +++ /dev/null @@ -1,72 +0,0 @@ -# (C) 2018 Red Hat Inc. -# Copyright (C) 2019 APCON. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Contains CLIConf Plugin methods for apconos Modules -# APCON Networking - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -author: "David Li (@davidlee-ap)" -cliconf: apconos -short_description: Use apconos cliconf to run command on APCON network devices -description: - - This apconos plugin provides low level abstraction apis for - sending and receiving CLI commands from APCON 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'] = 'apconos' - reply = self.get(b'show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - if data: - device_info['network_os_version'] = self.parse_version(data) - device_info['network_os_model'] = self.parse_model(data) - - return device_info - - def parse_version(self, data): - return "" - - def parse_model(self, data): - return "" - - @enable_mode - def get_config(self, source='running', format='text'): - pass - - @enable_mode - def edit_config(self, command): - for cmd in chain([b'configure terminal'], to_list(command), [b'end']): - self.send_command(cmd) - - def get(self, command, prompt=None, answer=None, sendonly=False, check_all=False): - return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, check_all=check_all) - - def get_capabilities(self): - return json.dumps(self.get_device_info()) diff --git a/plugins/cliconf/aruba.py b/plugins/cliconf/aruba.py deleted file mode 100644 index 52c0a974a4..0000000000 --- a/plugins/cliconf/aruba.py +++ /dev/null @@ -1,95 +0,0 @@ -# -# (c) 2017 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: aruba -short_description: Use aruba cliconf to run command on Aruba platform -description: - - This aruba plugin provides low level abstraction apis for - sending and receiving CLI commands from Aruba 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'] = 'aruba' - reply = self.get('show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'Version (\S+)', data) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'^MODEL: (\S+)\),', data, re.M) - if match: - device_info['network_os_model'] = match.group(1) - - reply = self.get('show hostname') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'^Hostname is (.+)', 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 configuration' - 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(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) - - def set_cli_prompt_context(self): - """ - Make sure we are in the operational cli mode - :return: None - """ - if self._connection.connected: - self._update_cli_prompt_context(config_context=')#') diff --git a/plugins/cliconf/ce.py b/plugins/cliconf/ce.py deleted file mode 100644 index 4af008c085..0000000000 --- a/plugins/cliconf/ce.py +++ /dev/null @@ -1,121 +0,0 @@ -# -# (c) 2017 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: ce -short_description: Use ce cliconf to run command on HUAWEI CloudEngine platform -description: - - This ce plugin provides low level abstraction apis for - sending and receiving CLI commands from HUAWEI CloudEngine network devices. -''' - -import re -import json - -from itertools import chain - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import 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'] = 'ce' - reply = self.get('display version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'^Huawei.+\n.+\Version\s+(\S+)', data) - if match: - device_info['network_os_version'] = match.group(1).strip(',') - - match = re.search(r'^Huawei(.+)\n.+\(\S+\s+\S+\)', data, re.M) - if match: - device_info['network_os_model'] = match.group(1) - - match = re.search(r'HUAWEI\s+(\S+)\s+uptime', 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'): - return self.invalid_params("fetching configuration from %s is not supported" % source) - - if not flags: - flags = [] - - cmd = 'display current-configuration' - - return self.send_command(cmd) - - @enable_mode - def edit_config(self, command): - results = [] - for cmd in chain(['configure terminal'], to_list(command), ['end']): - if isinstance(cmd, dict): - command = cmd['command'] - prompt = cmd['prompt'] - answer = cmd['answer'] - newline = cmd.get('newline', True) - else: - command = cmd - prompt = None - answer = None - newline = True - - results.append(self.send_command(command, prompt, answer, False, newline)) - return results[1:-1] - - 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) - - def set_cli_prompt_context(self): - """ - Make sure we are in the operational cli mode - :return: None - """ - if self._connection.connected: - out = self._connection.get_prompt() - - if out is None: - raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received' - u' response window: %s' % self._connection._last_recv_window) - - prompt = to_text(out, errors='surrogate_then_replace').strip() - while prompt.endswith(']'): - self._connection.queue_message('vvvv', 'wrong context, sending return to device') - if prompt.startswith('[*'): - self._connection.exec_command('clear configuration candidate') - self._connection.exec_command('return') - out = self._connection.get_prompt() - prompt = to_text(out, errors='surrogate_then_replace').strip() diff --git a/plugins/cliconf/cnos.py b/plugins/cliconf/cnos.py deleted file mode 100644 index 51541861c3..0000000000 --- a/plugins/cliconf/cnos.py +++ /dev/null @@ -1,135 +0,0 @@ -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Contains CLIConf Plugin methods for CNOS Modules -# Lenovo Networking -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: cnos -short_description: Use cnos cliconf to run command on Lenovo CNOS platform -description: - - This cnos plugin provides low level abstraction apis for - sending and receiving CLI commands from Lenovo CNOS network devices. -''' - -import re -import json - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils.common._collections_compat import Mapping -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'] = 'cnos' - reply = self.get('show sys-info') - data = to_text(reply, errors='surrogate_or_strict').strip() - host = self.get('show hostname') - hostname = to_text(host, errors='surrogate_or_strict').strip() - if data: - device_info['network_os_version'] = self.parse_version(data) - device_info['network_os_model'] = self.parse_model(data) - device_info['network_os_hostname'] = hostname - - return device_info - - def parse_version(self, data): - for line in data.split('\n'): - line = line.strip() - match = re.match(r'System Software Revision (.*?)', - line, re.M | re.I) - if match: - vers = line.split(':') - ver = vers[1].strip() - return ver - return "NA" - - def parse_model(self, data): - for line in data.split('\n'): - line = line.strip() - match = re.match(r'System Model (.*?)', line, re.M | re.I) - if match: - mdls = line.split(':') - mdl = mdls[1].strip() - return mdl - return "NA" - - @enable_mode - def get_config(self, source='running', format='text', flags=None): - if source not in ('running', 'startup'): - msg = "fetching configuration from %s is not supported" - return self.invalid_params(msg % source) - if source == 'running': - cmd = 'show running-config' - else: - cmd = 'show startup-config' - return self.send_command(cmd) - - @enable_mode - def edit_config(self, candidate=None, commit=True, - replace=None, comment=None): - resp = {} - results = [] - requests = [] - if commit: - self.send_command('configure terminal') - for line in to_list(candidate): - if not isinstance(line, Mapping): - line = {'command': line} - - cmd = line['command'] - if cmd != 'end' and cmd[0] != '!': - results.append(self.send_command(**line)) - requests.append(cmd) - - self.send_command('end') - else: - raise ValueError('check mode is not supported') - - resp['request'] = requests - resp['response'] = results - return resp - - 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) - - def set_cli_prompt_context(self): - """ - Make sure we are in the operational cli mode - :return: None - """ - if self._connection.connected: - out = self._connection.get_prompt() - - if out is None: - raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received' - u' response window: %s' % self._connection._last_recv_window) - - if to_text(out, errors='surrogate_then_replace').strip().endswith(')#'): - self._connection.queue_message('vvvv', 'In Config mode, sending exit to device') - self._connection.send_command('exit') - else: - self._connection.send_command('enable') diff --git a/plugins/cliconf/edgeos.py b/plugins/cliconf/edgeos.py deleted file mode 100644 index ef55889046..0000000000 --- a/plugins/cliconf/edgeos.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: edgeos -short_description: Use edgeos cliconf to run command on EdgeOS platform -description: - - This edgeos plugin provides low level abstraction apis for - sending and receiving CLI commands from Ubiquiti EdgeOS network devices. -''' - -import re -import json - -from itertools import chain - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text -from ansible.module_utils.common._collections_compat import Mapping -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.plugins.cliconf import CliconfBase - - -class Cliconf(CliconfBase): - - def get_device_info(self): - device_info = {} - - device_info['network_os'] = 'edgeos' - reply = self.get('show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'Version:\s*v?(\S+)', data) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'HW model:\s*(\S+)', data) - if match: - device_info['network_os_model'] = match.group(1) - - reply = self.get('show host name') - device_info['network_os_hostname'] = to_text(reply, errors='surrogate_or_strict').strip() - - return device_info - - def get_config(self, source='running', format='text', flags=None): - return self.send_command('show configuration commands') - - def edit_config(self, candidate=None, commit=True, replace=False, comment=None): - for cmd in chain(['configure'], to_list(candidate)): - self.send_command(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 commit(self, comment=None): - if comment: - command = 'commit comment "{0}"'.format(comment) - else: - command = 'commit' - self.send_command(command) - - def discard_changes(self, *args, **kwargs): - self.send_command('discard') - - def run_commands(self, commands=None, check_rc=True): - if commands is None: - raise ValueError("'commands' value is required") - - responses = list() - for cmd in to_list(commands): - if not isinstance(cmd, Mapping): - cmd = {'command': cmd} - - output = cmd.pop('output', None) - if output: - raise ValueError("'output' value %s is not supported for run_commands" % output) - - try: - out = self.send_command(**cmd) - except AnsibleConnectionFailure as e: - if check_rc: - raise - out = getattr(e, 'err', e) - - responses.append(out) - - return responses - - def get_device_operations(self): - return { - 'supports_diff_replace': False, - 'supports_commit': True, - 'supports_rollback': False, - 'supports_defaults': False, - 'supports_onbox_diff': False, - 'supports_commit_comment': True, - 'supports_multiline_delimiter': False, - 'supports_diff_match': False, - 'supports_diff_ignore_lines': False, - 'supports_generate_diff': False, - 'supports_replace': False - } - - def get_capabilities(self): - result = super(Cliconf, self).get_capabilities() - result['rpc'] += ['commit', 'discard_changes', 'run_commands'] - result['device_operations'] = self.get_device_operations() - return json.dumps(result) diff --git a/plugins/cliconf/edgeswitch.py b/plugins/cliconf/edgeswitch.py deleted file mode 100644 index eb99f23674..0000000000 --- a/plugins/cliconf/edgeswitch.py +++ /dev/null @@ -1,141 +0,0 @@ -# -# (c) 2018 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: edgeswitch -short_description: Use edgeswitch cliconf to run command on EdgeSwitch platform -description: - - This edgeswitch plugin provides low level abstraction apis for - sending and receiving CLI commands from Ubiquiti EdgeSwitch network devices. -''' - -import re -import time -import json - -from itertools import chain - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import dumps -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.plugins.cliconf import CliconfBase, enable_mode -from ansible.module_utils.common._collections_compat import Mapping - - -class Cliconf(CliconfBase): - - def get_device_info(self): - device_info = {} - - device_info['network_os'] = 'edgeswitch' - reply = self.get(command='show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'Software Version\.+ (.*)', data) - if match: - device_info['network_os_version'] = match.group(1).strip(',') - - match = re.search(r'^Machine Model\.+ (.*)', data, re.M) - if match: - device_info['network_os_model'] = match.group(1) - - match = re.search(r'System Name\.+ (.*)', data, re.M) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - @enable_mode - def get_config(self, source='running', flags=None): - if source not in ('running', 'startup'): - raise ValueError("fetching configuration from %s is not supported" % source) - - if source == 'running': - cmd = 'show running-config ' - else: - cmd = 'show startup-config ' - - if flags: - cmd += ' '.join(to_list(flags)) - cmd = cmd.strip() - - return self.send_command(cmd) - - @enable_mode - def edit_config(self, commands): - resp = {} - - results = [] - requests = [] - self.send_command('configure') - for line in to_list(commands): - if not isinstance(line, Mapping): - line = {'command': line} - - cmd = line['command'] - if cmd != 'end' and cmd[0] != '!': - results.append(self.send_command(**line)) - requests.append(cmd) - - self.send_command('end') - - resp['request'] = requests - resp['response'] = results - return resp - - def get(self, command=None, prompt=None, answer=None, sendonly=False, output=None, newline=True, check_all=False): - if not command: - raise ValueError('must provide value of command to execute') - if output: - raise ValueError("'output' value %s is not supported for get" % output) - - 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() - result['rpc'] += ['run_commands'] - return json.dumps(result) - - def run_commands(self, commands=None, check_rc=True): - if commands is None: - raise ValueError("'commands' value is required") - - responses = list() - for cmd in to_list(commands): - if not isinstance(cmd, Mapping): - cmd = {'command': cmd} - - output = cmd.pop('output', None) - if output: - raise ValueError("'output' value %s is not supported for run_commands" % output) - - try: - out = self.send_command(**cmd) - except AnsibleConnectionFailure as e: - if check_rc: - raise - out = getattr(e, 'err', e) - - responses.append(out) - - return responses diff --git a/plugins/cliconf/enos.py b/plugins/cliconf/enos.py deleted file mode 100644 index 225409f5cd..0000000000 --- a/plugins/cliconf/enos.py +++ /dev/null @@ -1,103 +0,0 @@ -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Contains CLIConf Plugin methods for ENOS Modules -# Lenovo Networking -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: enos -short_description: Use enos cliconf to run command on Lenovo ENOS platform -description: - - This enos plugin provides low level abstraction apis for - sending and receiving CLI commands from Lenovo ENOS network devices. -''' - -import re -import json - -from itertools import chain - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import 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'] = 'enos' - reply = self.get('show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'^Software Version (.*?) ', data, re.M | re.I) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'^Lenovo RackSwitch (\S+)', data, re.M | re.I) - if match: - device_info['network_os_model'] = match.group(1) - - match = re.search(r'^(.+) uptime', data, re.M) - if match: - device_info['network_os_hostname'] = match.group(1) - else: - device_info['network_os_hostname'] = "NA" - - return device_info - - @enable_mode - def get_config(self, source='running', format='text', flags=None): - if source not in ('running', 'startup'): - msg = "fetching configuration from %s is not supported" - return self.invalid_params(msg % source) - if source == 'running': - cmd = 'show running-config' - 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(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) - - def set_cli_prompt_context(self): - """ - Make sure we are in the operational cli mode - :return: None - """ - if self._connection.connected: - out = self._connection.get_prompt() - - if out is None: - raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received' - u' response window: %s' % self._connection._last_recv_window) - - if to_text(out, errors='surrogate_then_replace').strip().endswith(')#'): - self._connection.queue_message('vvvv', 'In Config mode, sending exit to device') - self._connection.send_command('exit') - else: - self._connection.send_command('enable') diff --git a/plugins/cliconf/eric_eccli.py b/plugins/cliconf/eric_eccli.py deleted file mode 100644 index 13c5f9edc3..0000000000 --- a/plugins/cliconf/eric_eccli.py +++ /dev/null @@ -1,97 +0,0 @@ -# -# Copyright (c) 2019 Ericsson AB. -# -# 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 . -# - - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -author: Ericsson IPOS OAM team -cliconf: eccli -short_description: Use eccli cliconf to run command on Ericsson ECCLI platform -description: - - This eccli plugin provides low level abstraction APIs for - sending and receiving CLI commands from Ericsson ECCLI network devices. -''' - -from ansible.module_utils.common._collections_compat import Mapping -import collections -import re -import time -import json - -from itertools import chain - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps -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_config(self, source='running', flags=None, format=None): - return - - def edit_config(self, candidate=None, commit=True, replace=None, comment=None): - return - - def get(self, command=None, prompt=None, answer=None, sendonly=False, output=None, newline=True, check_all=False): - if not command: - raise ValueError('must provide value of command to execute') - if output: - raise ValueError("'output' value %s is not supported for get" % output) - - return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all) - - def get_device_info(self): - device_info = {} - device_info['network_os'] = 'eric_eccli' - return device_info - - def get_capabilities(self): - result = dict() - result['rpc'] = self.get_base_rpc() + ['run_commands'] - result['network_api'] = 'cliconf' - result['device_info'] = self.get_device_info() - return json.dumps(result) - - def run_commands(self, commands=None, check_rc=True): - if commands is None: - raise ValueError("'commands' value is required") - - responses = list() - for cmd in to_list(commands): - if not isinstance(cmd, Mapping): - cmd = {'command': cmd} - - output = cmd.pop('output', None) - if output: - raise ValueError("'output' value %s is not supported for run_commands" % output) - try: - out = self.send_command(**cmd) - except AnsibleConnectionFailure as e: - if check_rc: - raise - out = getattr(e, 'err', e) - - responses.append(out) - - return responses diff --git a/plugins/cliconf/exos.py b/plugins/cliconf/exos.py deleted file mode 100644 index c9ca494688..0000000000 --- a/plugins/cliconf/exos.py +++ /dev/null @@ -1,229 +0,0 @@ -# -# (c) 2017 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: exos -short_description: Use exos cliconf to run command on Extreme EXOS platform -description: - - This exos plugin provides low level abstraction apis for - sending and receiving CLI commands from Extreme EXOS network devices. -''' - -import re -import json - -from ansible.errors import AnsibleConnectionFailure -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.module_utils.connection import ConnectionError -from ansible.module_utils.common._collections_compat import Mapping -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps -from ansible.plugins.cliconf import CliconfBase - - -class Cliconf(CliconfBase): - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - diff = {} - device_operations = self.get_device_operations() - option_values = self.get_option_values() - - if candidate is None and device_operations['supports_generate_diff']: - raise ValueError("candidate configuration is required to generate diff") - - if diff_match not in option_values['diff_match']: - raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match']))) - - if diff_replace not in option_values['diff_replace']: - raise ValueError("'replace' value %s in invalid, valid values are %s" % (diff_replace, ', '.join(option_values['diff_replace']))) - - # prepare candidate configuration - candidate_obj = NetworkConfig(indent=1) - candidate_obj.load(candidate) - - if running and diff_match != 'none' and diff_replace != 'config': - # running configuration - running_obj = NetworkConfig(indent=1, contents=running, ignore_lines=diff_ignore_lines) - configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace) - - else: - configdiffobjs = candidate_obj.items - - diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else '' - return diff - - def get_device_info(self): - device_info = {} - device_info['network_os'] = 'exos' - - reply = self.run_commands({'command': 'show switch detail', 'output': 'text'}) - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'ExtremeXOS version (\S+)', data) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'System Type: +(\S+)', data) - if match: - device_info['network_os_model'] = match.group(1) - - match = re.search(r'SysName: +(\S+)', data) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - def get_default_flag(self): - # The flag to modify the command to collect configuration with defaults - return 'detail' - - def get_config(self, source='running', format='text', flags=None): - options_values = self.get_option_values() - if format not in options_values['format']: - raise ValueError("'format' value %s is invalid. Valid values are %s" % (format, ','.join(options_values['format']))) - - lookup = {'running': 'show configuration', 'startup': 'debug cfgmgr show configuration file'} - if source not in lookup: - raise ValueError("fetching configuration from %s is not supported" % source) - - cmd = {'command': lookup[source], 'output': 'text'} - - if source == 'startup': - reply = self.run_commands({'command': 'show switch', 'format': 'text'}) - data = to_text(reply, errors='surrogate_or_strict').strip() - match = re.search(r'Config Selected: +(\S+)\.cfg', data, re.MULTILINE) - if match: - cmd['command'] += match.group(1) - else: - # No Startup(/Selected) Config - return {} - - cmd['command'] += ' '.join(to_list(flags)) - cmd['command'] = cmd['command'].strip() - - return self.run_commands(cmd)[0] - - def edit_config(self, candidate=None, commit=True, replace=None, diff=False, comment=None): - resp = {} - operations = self.get_device_operations() - self.check_edit_config_capability(operations, candidate, commit, replace, comment) - results = [] - requests = [] - - if commit: - for line in to_list(candidate): - if not isinstance(line, Mapping): - line = {'command': line} - results.append(self.send_command(**line)) - requests.append(line['command']) - else: - raise ValueError('check mode is not supported') - - resp['request'] = requests - resp['response'] = results - return resp - - def get(self, command, prompt=None, answer=None, sendonly=False, output=None, newline=True, check_all=False): - if output: - command = self._get_command_with_output(command, output) - return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all) - - def run_commands(self, commands=None, check_rc=True): - if commands is None: - raise ValueError("'commands' value is required") - - responses = list() - for cmd in to_list(commands): - if not isinstance(cmd, Mapping): - cmd = {'command': cmd} - - output = cmd.pop('output', None) - if output: - cmd['command'] = self._get_command_with_output(cmd['command'], output) - - try: - out = self.send_command(**cmd) - except AnsibleConnectionFailure as e: - if check_rc is True: - raise - out = getattr(e, 'err', e) - - if out is not None: - try: - out = to_text(out, errors='surrogate_or_strict').strip() - except UnicodeError: - raise ConnectionError(message=u'Failed to decode output from %s: %s' % (cmd, to_text(out))) - - if output and output == 'json': - try: - out = json.loads(out) - except ValueError: - raise ConnectionError('Response was not valid JSON, got {0}'.format( - to_text(out) - )) - responses.append(out) - - return responses - - def get_device_operations(self): - return { - 'supports_diff_replace': False, # identify if config should be merged or replaced is supported - 'supports_commit': False, # identify if commit is supported by device or not - 'supports_rollback': False, # identify if rollback is supported or not - 'supports_defaults': True, # identify if fetching running config with default is supported - 'supports_commit_comment': False, # identify if adding comment to commit is supported of not - 'supports_onbox_diff': False, # identify if on box diff capability is supported or not - 'supports_generate_diff': True, # identify if diff capability is supported within plugin - 'supports_multiline_delimiter': False, # identify if multiline delimiter is supported within config - 'supports_diff_match': True, # identify if match is supported - 'supports_diff_ignore_lines': True, # identify if ignore line in diff is supported - 'supports_config_replace': False, # identify if running config replace with candidate config is supported - 'supports_admin': False, # identify if admin configure mode is supported or not - 'supports_commit_label': False, # identify if commit label is supported or not - 'supports_replace': False - } - - def get_option_values(self): - return { - 'format': ['text', 'json'], - 'diff_match': ['line', 'strict', 'exact', 'none'], - 'diff_replace': ['line', 'block'], - 'output': ['text', 'json'] - } - - def get_capabilities(self): - result = super(Cliconf, self).get_capabilities() - result['rpc'] += ['run_commmands', 'get_default_flag', 'get_diff'] - result['device_operations'] = self.get_device_operations() - result['device_info'] = self.get_device_info() - result.update(self.get_option_values()) - return json.dumps(result) - - def _get_command_with_output(self, command, output): - if output not in self.get_option_values().get('output'): - raise ValueError("'output' value is %s is invalid. Valid values are %s" % (output, ','.join(self.get_option_values().get('output')))) - - if output == 'json' and not command.startswith('run script cli2json.py'): - cmd = 'run script cli2json.py %s' % command - else: - cmd = command - return cmd diff --git a/plugins/cliconf/icx.py b/plugins/cliconf/icx.py deleted file mode 100644 index 4ce6e5d130..0000000000 --- a/plugins/cliconf/icx.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright: (c) 2019, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -author: Ruckus Wireless (@Commscope) -cliconf: icx -short_description: Use icx cliconf to run command on Ruckus ICX platform -description: - - This icx plugin provides low level abstraction APIs for - sending and receiving CLI commands from Ruckus ICX network devices. -''' - - -import re -import time -import json -import os - -from itertools import chain -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.plugins.cliconf import CliconfBase, enable_mode -from ansible.module_utils.common._collections_compat import Mapping - - -class Cliconf(CliconfBase): - - @enable_mode - def get_config(self, source='running', flags=None, format=None, compare=None): - if source not in ('running', 'startup'): - raise ValueError("fetching configuration from %s is not supported" % source) - - if format: - raise ValueError("'format' value %s is not supported for get_config" % format) - - if not flags: - flags = [] - - if compare is False: - return '' - else: - if source == 'running': - cmd = 'show running-config ' - else: - cmd = 'show configuration ' - - cmd += ' '.join(to_list(flags)) - cmd = cmd.strip() - - return self.send_command(cmd) - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - """ - Generate diff between candidate and running configuration. If the - remote host supports onbox diff capabilities ie. supports_onbox_diff in that case - candidate and running configurations are not required to be passed as argument. - In case if onbox diff capability is not supported candidate argument is mandatory - and running argument is optional. - :param candidate: The configuration which is expected to be present on remote host. - :param running: The base configuration which is used to generate diff. - :param diff_match: Instructs how to match the candidate configuration with current device configuration - Valid values are 'line', 'strict', 'exact', 'none'. - 'line' - commands are matched line by line - 'strict' - command lines are matched with respect to position - 'exact' - command lines must be an equal match - 'none' - will not compare the candidate configuration with the running configuration - :param diff_ignore_lines: Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - :param path: 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. - :param diff_replace: Instructs 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. - :return: Configuration diff in json format. - { - 'config_diff': '', - 'banner_diff': {} - } - - """ - diff = {} - device_operations = self.get_device_operations() - option_values = self.get_option_values() - - if candidate is None and device_operations['supports_generate_diff']: - raise ValueError("candidate configuration is required to generate diff") - - if diff_match not in option_values['diff_match']: - raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match']))) - - if diff_replace not in option_values['diff_replace']: - raise ValueError("'replace' value %s in invalid, valid values are %s" % (diff_replace, ', '.join(option_values['diff_replace']))) - - # prepare candidate configuration - candidate_obj = NetworkConfig(indent=1) - want_src, want_banners = self._extract_banners(candidate) - candidate_obj.load(want_src) - - if running and diff_match != 'none': - # running configuration - have_src, have_banners = self._extract_banners(running) - - running_obj = NetworkConfig(indent=1, contents=have_src, ignore_lines=diff_ignore_lines) - configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace) - - else: - configdiffobjs = candidate_obj.items - have_banners = {} - - diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else '' - - banners = self._diff_banners(want_banners, have_banners) - diff['banner_diff'] = banners if banners else {} - return diff - - @enable_mode - def edit_config(self, candidate=None, commit=True, replace=None, comment=None): - resp = {} - operations = self.get_device_operations() - self.check_edit_config_capability(operations, candidate, commit, replace, comment) - - results = [] - requests = [] - if commit: - prompt = self._connection.get_prompt() - if (b'(config-if' in prompt) or (b'(config' in prompt) or (b'(config-lag-if' in prompt): - self.send_command('end') - - self.send_command('configure terminal') - - for line in to_list(candidate): - if not isinstance(line, Mapping): - line = {'command': line} - - cmd = line['command'] - if cmd != 'end' and cmd[0] != '!': - results.append(self.send_command(**line)) - requests.append(cmd) - - self.send_command('end') - else: - raise ValueError('check mode is not supported') - - resp['request'] = requests - resp['response'] = results - return resp - - def get(self, command=None, prompt=None, answer=None, sendonly=False, output=None, check_all=False): - if not command: - raise ValueError('must provide value of command to execute') - if output: - raise ValueError("'output' value %s is not supported for get" % output) - - return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, check_all=check_all) - - def scp(self, command=None, scp_user=None, scp_pass=None): - if not command: - raise ValueError('must provide value of command to execute') - prompt = ["User name:", "Password:"] - if(scp_pass is None): - answer = [scp_user, self._connection._play_context.password] - else: - answer = [scp_user, scp_pass] - return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=False, check_all=True) - - def get_device_info(self): - device_info = {} - - device_info['network_os'] = 'icx' - reply = self.get(command='show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'Version (\S+)', data) - if match: - device_info['network_os_version'] = match.group(1).strip(',') - - match = re.search(r'^Cisco (.+) \(revision', data, re.M) - if match: - device_info['network_os_model'] = match.group(1) - - match = re.search(r'^(.+) uptime', data, re.M) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - def get_device_operations(self): - return { - 'supports_diff_replace': True, - 'supports_commit': False, - 'supports_rollback': False, - 'supports_defaults': True, - 'supports_onbox_diff': False, - 'supports_commit_comment': False, - 'supports_multiline_delimiter': True, - 'supports_diff_match': True, - 'supports_diff_ignore_lines': True, - 'supports_generate_diff': True, - 'supports_replace': False - } - - def get_option_values(self): - return { - 'format': ['text'], - 'diff_match': ['line', 'strict', 'exact', 'none'], - 'diff_replace': ['line', 'block'], - 'output': [] - } - - def get_capabilities(self): - result = dict() - result['rpc'] = self.get_base_rpc() + ['edit_banner', 'get_diff', 'run_commands', 'get_defaults_flag'] - result['network_api'] = 'cliconf' - result['device_operations'] = self.get_device_operations() - result.update(self.get_option_values()) - return json.dumps(result) - - def edit_banner(self, candidate=None, multiline_delimiter="@", commit=True): - """ - Edit banner on remote device - :param banners: Banners to be loaded in json format - :param multiline_delimiter: Line delimiter for banner - :param commit: Boolean value that indicates if the device candidate - configuration should be pushed in the running configuration or discarded. - :param diff: Boolean flag to indicate if configuration that is applied on remote host should - generated and returned in response or not - :return: Returns response of executing the configuration command received - from remote host - """ - resp = {} - banners_obj = json.loads(candidate) - results = [] - requests = [] - if commit: - for key, value in iteritems(banners_obj): - key += ' %s' % multiline_delimiter - self.send_command('config terminal', sendonly=True) - for cmd in [key, value, multiline_delimiter]: - obj = {'command': cmd, 'sendonly': True} - results.append(self.send_command(**obj)) - requests.append(cmd) - - self.send_command('end', sendonly=True) - time.sleep(0.1) - results.append(self.send_command('\n')) - requests.append('\n') - - resp['request'] = requests - resp['response'] = results - - return resp - - def run_commands(self, commands=None, check_rc=True): - if commands is None: - raise ValueError("'commands' value is required") - - responses = list() - for cmd in to_list(commands): - if not isinstance(cmd, Mapping): - cmd = {'command': cmd} - - output = cmd.pop('output', None) - if output: - raise ValueError("'output' value %s is not supported for run_commands" % output) - - try: - out = self.send_command(**cmd) - except AnsibleConnectionFailure as e: - if check_rc: - raise - out = getattr(e, 'err', to_text(e)) - - responses.append(out) - - return responses - - def _extract_banners(self, config): - banners = {} - banner_cmds = re.findall(r'^banner (\w+)', config, re.M) - for cmd in banner_cmds: - regex = r'banner %s \$(.+?)(?=\$)' % cmd - match = re.search(regex, config, re.S) - if match: - key = 'banner %s' % cmd - banners[key] = match.group(1).strip() - - for cmd in banner_cmds: - regex = r'banner %s \$(.+?)(?=\$)' % cmd - match = re.search(regex, config, re.S) - if match: - config = config.replace(str(match.group(1)), '') - - config = re.sub(r'banner \w+ \$\$', '!! banner removed', config) - return config, banners - - def _diff_banners(self, want, have): - candidate = {} - for key, value in iteritems(want): - if value != have.get(key): - candidate[key] = value - return candidate diff --git a/plugins/cliconf/ironware.py b/plugins/cliconf/ironware.py deleted file mode 100644 index cb476f924d..0000000000 --- a/plugins/cliconf/ironware.py +++ /dev/null @@ -1,95 +0,0 @@ -# -# (c) 2017 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: ironware -short_description: Use ironware cliconf to run command on Extreme Ironware platform -description: - - This ironware plugin provides low level abstraction apis for - sending and receiving CLI commands from Extreme Ironware 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'] = 'ironware' - reply = self.send_command('show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'IronWare : Version (\S+),', data) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'^(?:System Mode\:|System\:) (CES|CER|MLX|XMR)', data, re.M) - if match: - device_info['network_os_model'] = match.group(1) - - return device_info - - @enable_mode - def get_config(self, source='running', format='text', flags=None): - if source not in ('running', 'startup'): - raise ValueError("fetching configuration from %s is not supported" % source) - - if source == 'running': - cmd = 'show running-config' - if flags is not None: - cmd += ' ' + ' '.join(flags) - - else: - cmd = 'show configuration' - if flags is not None: - raise ValueError("flags are only supported with running-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(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) - - def set_cli_prompt_context(self): - """ - Make sure we are in the operational cli mode - :return: None - """ - if self._connection.connected: - self._update_cli_prompt_context(config_context=')#') diff --git a/plugins/cliconf/netvisor.py b/plugins/cliconf/netvisor.py deleted file mode 100644 index 51301531ce..0000000000 --- a/plugins/cliconf/netvisor.py +++ /dev/null @@ -1,74 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: netvisor -short_description: Use netvisor cliconf to run command on Pluribus netvisor platform -description: - - This netvisor plugin provides low level abstraction apis for - sending and receiving CLI commands from Pluribus netvisor devices. -''' - -import json -from ansible.plugins.cliconf import CliconfBase - - -class Cliconf(CliconfBase): - - def get_config(self, source='running', format='text', flags=None): - if source not in ('running'): - return self.invalid_params("fetching configuration from %s is not supported" % source) - cmd = 'show running-config' - return self.send_command(cmd) - - def edit_config(self, command): - return - - def get(self, command=None, prompt=None, answer=None, sendonly=False, output=None, newline=True, check_all=False): - if not command: - raise ValueError('must provide value of command to execute') - if output: - raise ValueError("'output' value %s is not supported for get" % output) - - return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all) - - def get_option_values(self): - return { - 'format': ['text'], - 'diff_match': ['line', 'strict', 'exact', 'none'], - 'diff_replace': ['line', 'block'], - 'output': [] - } - - def get_capabilities(self): - result = dict() - result['rpc'] = self.get_base_rpc() - result['network_api'] = 'cliconf' - result['device_info'] = self.get_device_info() - result.update(self.get_option_values()) - return json.dumps(result) - - def get_device_info(self): - device_info = {} - device_info['network_os'] = 'netvisor' - - return device_info diff --git a/plugins/cliconf/nos.py b/plugins/cliconf/nos.py deleted file mode 100644 index b1f922ac55..0000000000 --- a/plugins/cliconf/nos.py +++ /dev/null @@ -1,112 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: nos -short_description: Use nos cliconf to run command on Extreme NOS platform -description: - - This nos plugin provides low level abstraction apis for - sending and receiving CLI commands from Extreme NOS network devices. -''' - -import re -import json - -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.plugins.cliconf import CliconfBase - - -class Cliconf(CliconfBase): - - def get_device_info(self): - device_info = {} - - device_info['network_os'] = 'nos' - reply = self.get('show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'Network Operating System Version: (\S+)', data) - if match: - device_info['network_os_version'] = match.group(1) - - reply = self.get('show chassis') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'^Chassis Name:(\s+)(\S+)', data, re.M) - if match: - device_info['network_os_model'] = match.group(2) - - reply = self.get('show running-config | inc "switch-attributes host-name"') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'switch-attributes host-name (\S+)', data, re.M) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - def get_config(self, source='running', flags=None): - if source not in 'running': - raise ValueError("fetching configuration from %s is not supported" % source) - if source == 'running': - cmd = 'show running-config' - - flags = [] if flags is None else flags - cmd += ' '.join(flags) - cmd = cmd.strip() - - return self.send_command(cmd) - - def edit_config(self, command): - resp = {} - results = [] - requests = [] - self.send_command('configure terminal') - for cmd in to_list(command): - if isinstance(cmd, dict): - command = cmd['command'] - prompt = cmd['prompt'] - answer = cmd['answer'] - newline = cmd.get('newline', True) - else: - command = cmd - prompt = None - answer = None - newline = True - - if cmd != 'end' and cmd[0] != '!': - results.append(self.send_command(command, prompt, answer, False, newline)) - requests.append(cmd) - - self.send_command('end') - - resp['request'] = requests - resp['response'] = results - return resp - - 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/plugins/cliconf/onyx.py b/plugins/cliconf/onyx.py deleted file mode 100644 index af7690efb5..0000000000 --- a/plugins/cliconf/onyx.py +++ /dev/null @@ -1,77 +0,0 @@ -# -# (c) 2017 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: onyx -short_description: Use onyx cliconf to run command on Mellanox ONYX platform -description: - - This onyx plugin provides low level abstraction apis for - sending and receiving CLI commands from Mellanox ONYX network devices. -''' - -import json - -from itertools import chain - -from ansible.module_utils._text import 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 = {} - - reply = self.get('show version | json-print') - data = json.loads(reply) - device_info['network_os'] = data['Product name'] - device_info['network_os_version'] = data['Product release'] - device_info['network_os_version_summary'] = data['Version summary'] - device_info['network_os_model'] = data['Product model'] - - reply = self.get('show hosts | include Hostname') - data = to_text(reply, errors='surrogate_or_strict').strip() - hostname = data.split(':')[1] - hostname = hostname.strip() - device_info['network_os_hostname'] = hostname - - return device_info - - @enable_mode - def get_config(self, source='running', format='text', flags=None): - if source not in ('running',): - return self.invalid_params("fetching configuration from %s is not supported" % source) - cmd = 'show running-config' - return self.send_command(cmd) - - @enable_mode - def edit_config(self, command): - for cmd in chain(['configure terminal'], to_list(command), ['exit']): - self.send_command(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/plugins/cliconf/routeros.py b/plugins/cliconf/routeros.py deleted file mode 100644 index 9e020a1b03..0000000000 --- a/plugins/cliconf/routeros.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# (c) 2017 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: routeros -short_description: Use routeros cliconf to run command on MikroTik RouterOS platform -description: - - This routeros plugin provides low level abstraction apis for - sending and receiving CLI commands from MikroTik RouterOS 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'] = 'RouterOS' - - resource = self.get('/system resource print') - data = to_text(resource, errors='surrogate_or_strict').strip() - match = re.search(r'version: (\S+)', data) - if match: - device_info['network_os_version'] = match.group(1) - - routerboard = self.get('/system routerboard print') - data = to_text(routerboard, errors='surrogate_or_strict').strip() - match = re.search(r'model: (.+)$', data, re.M) - if match: - device_info['network_os_model'] = match.group(1) - - identity = self.get('/system identity print') - data = to_text(identity, errors='surrogate_or_strict').strip() - match = re.search(r'name: (.+)$', data, re.M) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - def get_config(self, source='running', format='text', flags=None): - return - - def edit_config(self, command): - return - - 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/plugins/cliconf/slxos.py b/plugins/cliconf/slxos.py deleted file mode 100644 index a7809a3b95..0000000000 --- a/plugins/cliconf/slxos.py +++ /dev/null @@ -1,104 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: slxos -short_description: Use slxos cliconf to run command on Extreme SLX-OS platform -description: - - This slxos plugin provides low level abstraction apis for - sending and receiving CLI commands from Extreme SLX-OS 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 - - -class Cliconf(CliconfBase): - - def get_device_info(self): - device_info = {} - - device_info['network_os'] = 'slxos' - reply = self.get('show version') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'SLX\-OS Operating System Version: (\S+)', data) - if match: - device_info['network_os_version'] = match.group(1) - - reply = self.get('show chassis') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'^Chassis Name:(\s+)(\S+)', data, re.M) - if match: - device_info['network_os_model'] = match.group(2) - - reply = self.get('show running-config | inc "switch-attributes host-name"') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'switch-attributes host-name (\S+)', data, re.M) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - def get_config(self, source='running', flags=None): - if source not in ('running', 'startup'): - raise ValueError("fetching configuration from %s is not supported" % source) - if source == 'running': - cmd = 'show running-config' - else: - cmd = 'show startup-config' - - flags = [] if flags is None else flags - cmd += ' '.join(flags) - cmd = cmd.strip() - - return self.send_command(cmd) - - def edit_config(self, command): - for cmd in chain(['configure terminal'], to_list(command), ['end']): - if isinstance(cmd, dict): - command = cmd['command'] - prompt = cmd['prompt'] - answer = cmd['answer'] - newline = cmd.get('newline', True) - else: - command = cmd - prompt = None - answer = None - newline = True - - self.send_command(command, prompt, answer, False, newline) - - 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/plugins/cliconf/voss.py b/plugins/cliconf/voss.py deleted file mode 100644 index 7d3d26eb7d..0000000000 --- a/plugins/cliconf/voss.py +++ /dev/null @@ -1,235 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -cliconf: voss -short_description: Use voss cliconf to run command on Extreme VOSS platform -description: - - This voss plugin provides low level abstraction apis for - sending and receiving CLI commands from Extreme VOSS network devices. -''' - -import re -import json - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text -from ansible.module_utils.common._collections_compat import Mapping -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible_collections.community.general.plugins.module_utils.network.voss.voss import VossNetworkConfig -from ansible.plugins.cliconf import CliconfBase, enable_mode - - -class Cliconf(CliconfBase): - - @enable_mode - def get_config(self, source='running', flags=None, format=None): - if source not in ('running', 'startup'): - raise ValueError("fetching configuration from %s is not supported" % source) - - if format: - raise ValueError("'format' value %s is not supported for get_config" % format) - - if not flags: - flags = [] - if source == 'running': - cmd = 'show running-config ' - cmd += ' '.join(to_list(flags)) - cmd = cmd.strip() - else: - cmd = 'more /intflash/config.cfg' - - return self.send_command(cmd) - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - """ - Generate diff between candidate and running configuration. If the - remote host supports onbox diff capabilities ie. supports_onbox_diff in that case - candidate and running configurations are not required to be passed as argument. - In case if onbox diff capability is not supported candidate argument is mandatory - and running argument is optional. - :param candidate: The configuration which is expected to be present on remote host. - :param running: The base configuration which is used to generate diff. - :param diff_match: Instructs how to match the candidate configuration with current device configuration - Valid values are 'line', 'strict', 'exact', 'none'. - 'line' - commands are matched line by line - 'strict' - command lines are matched with respect to position - 'exact' - command lines must be an equal match - 'none' - will not compare the candidate configuration with the running configuration - :param diff_ignore_lines: Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - :param path: 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. - :param diff_replace: Instructs 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. - :return: Configuration diff in json format. - { - 'config_diff': '', - } - - """ - diff = {} - - device_operations = self.get_device_operations() - option_values = self.get_option_values() - - if candidate is None and device_operations['supports_generate_diff']: - raise ValueError("candidate configuration is required to generate diff") - - if diff_match not in option_values['diff_match']: - raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match']))) - - if diff_replace not in option_values['diff_replace']: - raise ValueError("'replace' value %s in invalid, valid values are %s" % (diff_replace, ', '.join(option_values['diff_replace']))) - - # prepare candidate configuration - candidate_obj = VossNetworkConfig(indent=0, ignore_lines=diff_ignore_lines) - candidate_obj.load(candidate) - - if running and diff_match != 'none': - # running configuration - running_obj = VossNetworkConfig(indent=0, contents=running, ignore_lines=diff_ignore_lines) - configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace) - - else: - configdiffobjs = candidate_obj.items - - diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else '' - diff['diff_path'] = path - diff['diff_replace'] = diff_replace - return diff - - @enable_mode - def edit_config(self, candidate=None, commit=True, replace=None, comment=None): - resp = {} - operations = self.get_device_operations() - self.check_edit_config_capability(operations, candidate, commit, replace, comment) - - results = [] - requests = [] - if commit: - self.send_command('configure terminal') - for line in to_list(candidate): - if not isinstance(line, Mapping): - line = {'command': line} - - cmd = line['command'] - if cmd != 'end' and cmd[0] != '!': - results.append(self.send_command(**line)) - requests.append(cmd) - - self.send_command('end') - else: - raise ValueError('check mode is not supported') - - resp['request'] = requests - resp['response'] = results - return resp - - 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_device_info(self): - device_info = {} - - device_info['network_os'] = 'voss' - reply = self.get(command='show sys-info') - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'SysDescr\s+: \S+ \((\S+)\)', data) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'Chassis\s+: (\S+)', data) - if match: - device_info['network_os_model'] = match.group(1) - - match = re.search(r'SysName\s+: (\S+)', data) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - def get_device_operations(self): - return { - 'supports_diff_replace': True, - 'supports_commit': False, - 'supports_rollback': False, - 'supports_defaults': True, - 'supports_onbox_diff': False, - 'supports_commit_comment': False, - 'supports_multiline_delimiter': False, - 'supports_diff_match': True, - 'supports_diff_ignore_lines': True, - 'supports_generate_diff': True, - 'supports_replace': False - } - - def get_option_values(self): - return { - 'format': ['text'], - 'diff_match': ['line', 'strict', 'exact', 'none'], - 'diff_replace': ['line', 'block'], - 'output': [] - } - - def get_capabilities(self): - result = super(Cliconf, self).get_capabilities() - result['rpc'] += ['get_diff', 'run_commands', 'get_defaults_flag'] - result['device_operations'] = self.get_device_operations() - result.update(self.get_option_values()) - return json.dumps(result) - - def run_commands(self, commands=None, check_rc=True): - if commands is None: - raise ValueError("'commands' value is required") - - responses = list() - for cmd in to_list(commands): - if not isinstance(cmd, Mapping): - cmd = {'command': cmd} - - output = cmd.pop('output', None) - if output: - raise ValueError("'output' value %s is not supported for run_commands" % output) - - try: - out = self.send_command(**cmd) - except AnsibleConnectionFailure as e: - if check_rc: - raise - out = getattr(e, 'err', e) - - responses.append(out) - - return responses - - def get_defaults_flag(self): - return 'verbose' diff --git a/plugins/doc_fragments/a10.py b/plugins/doc_fragments/a10.py deleted file mode 100644 index d2d6cb502f..0000000000 --- a/plugins/doc_fragments/a10.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, John Barker -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - host: - description: - - Hostname or IP of the A10 Networks device. - type: str - required: true - username: - description: - - An account with administrator privileges. - type: str - required: true - aliases: [ admin, user ] - password: - description: - - Password for the C(username) account. - type: str - required: true - aliases: [ pass, pwd ] - write_config: - description: - - If C(yes), any changes will cause a write of the running configuration - to non-volatile memory. This will save I(all) configuration changes, - including those that may have been made manually or through other modules, - so care should be taken when specifying C(yes). - type: bool - default: no - validate_certs: - description: - - If C(no), SSL certificates will not be validated. - - This should only be used on personally controlled devices using self-signed certificates. - type: bool - default: yes -notes: - - Requires A10 Networks aXAPI 2.1. -''' diff --git a/plugins/doc_fragments/aireos.py b/plugins/doc_fragments/aireos.py deleted file mode 100644 index b3e734a06c..0000000000 --- a/plugins/doc_fragments/aireos.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, James Mighion <@jmighion> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - provider: - description: - - A dict object containing connection details. - 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 - required: true - port: - description: - - Specifies the port to use when building the connection to the remote device. - type: int - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to the remote device. - - This value is used to authenticate the SSH session. - - 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: - - Specifies the password to use to authenticate the connection to the remote device. - - This value is used to authenticate the SSH session. - - If the value is not specified in the task, the value of environment variable - C(ANSIBLE_NET_PASSWORD) will be used instead. - type: str - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. - - If the timeout is exceeded before the operation is completed, the module will error. - type: int - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to the remote device. - - This value is the path to the key used to authenticate the SSH session. - - 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 -''' diff --git a/plugins/doc_fragments/aruba.py b/plugins/doc_fragments/aruba.py deleted file mode 100644 index 6bd49ddf9f..0000000000 --- a/plugins/doc_fragments/aruba.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, James Mighion <@jmighion> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - provider: - description: - - A dict object containing connection details. - 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 - required: true - port: - description: - - Specifies the port to use when building the connection to the remote. - device. - type: int - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. 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: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead. - type: str - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. If the timeout is - exceeded before the operation is completed, the module will error. - type: int - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. 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 -''' diff --git a/plugins/doc_fragments/avi.py b/plugins/doc_fragments/avi.py deleted file mode 100644 index 2692e11888..0000000000 --- a/plugins/doc_fragments/avi.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- - -# Created on December 12, 2016 -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Avi Version: 16.3.4 -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - # Avi common documentation fragment - DOCUMENTATION = r''' -options: - controller: - description: - - IP address or hostname of the controller. The default value is the environment variable C(AVI_CONTROLLER). - type: str - default: '' - username: - description: - - Username used for accessing Avi controller. The default value is the environment variable C(AVI_USERNAME). - type: str - default: '' - password: - description: - - Password of Avi user in Avi controller. The default value is the environment variable C(AVI_PASSWORD). - type: str - default: '' - tenant: - description: - - Name of tenant used for all Avi API calls and context of object. - type: str - default: admin - tenant_uuid: - description: - - UUID of tenant used for all Avi API calls and context of object. - type: str - default: '' - api_version: - description: - - Avi API version of to use for Avi API and objects. - type: str - default: 16.4.4 - avi_credentials: - description: - - Avi Credentials dictionary which can be used in lieu of enumerating Avi Controller login details. - suboptions: - controller: - description: - - Avi controller IP or SQDN - username: - description: - - Avi controller username - password: - description: - - Avi controller password - api_version: - description: - - Avi controller version - default: 16.4.4 - tenant: - description: - - Avi controller tenant - default: admin - tenant_uuid: - description: - - Avi controller tenant UUID - port: - description: - - Avi controller port - token: - description: - - Avi controller API token - timeout: - description: - - Avi controller request timeout - default: 300 - session_id: - description: - - Avi controller API session id to reuse existing session with csrftoken - csrftoken: - description: - - Avi controller API csrftoken to reuse existing session with session id - type: dict - api_context: - description: - - Avi API context that includes current session ID and CSRF Token. - - This allows user to perform single login and re-use the session. - type: dict - avi_disable_session_cache_as_fact: - description: - - It disables avi session information to be cached as a fact. - type: bool - -notes: - - For more information on using Ansible to manage Avi Network devices see U(https://www.ansible.com/ansible-avi-networks). -''' diff --git a/plugins/doc_fragments/ce.py b/plugins/doc_fragments/ce.py deleted file mode 100644 index 0709ab26e5..0000000000 --- a/plugins/doc_fragments/ce.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - provider: - description: - - A dict object containing connection details. - 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 - required: true - port: - description: - - Specifies the port to use when building the connection to the remote - device. This value applies to either I(cli) or I(netconf). The port - value will default to the appropriate transport common port if - none is provided in the task. (cli=22, netconf=22). - type: int - default: 0 (use common port) - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate the CLI login. - 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: - - Specifies the password to use to authenticate the connection to - the remote device. This is a common argument used for cli - transports. 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: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This argument is used for the I(cli) - transport. 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 - transport: - description: - - Configures the transport connection to use when connecting to the - remote device. The transport argument supports connectivity to the - device over cli (ssh). - type: str - required: true - choices: [ cli, netconf ] - default: cli -''' diff --git a/plugins/doc_fragments/cnos.py b/plugins/doc_fragments/cnos.py deleted file mode 100644 index 11f59e95de..0000000000 --- a/plugins/doc_fragments/cnos.py +++ /dev/null @@ -1,78 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Lenovo, Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - # Standard CNOS documentation fragment - DOCUMENTATION = r''' -options: - outputfile: - description: - - This specifies the file path where the output of each command - execution is saved. Each command that is specified in the merged - template file and each response from the device are saved here. - Usually the location is the results folder, but you can - choose another location based on your write permission. - type: str - required: true - host: - description: - - This is the variable used to search the hosts file at - /etc/ansible/hosts and identify the IP address of the device on - which the template is going to be applied. Usually the Ansible - keyword {{ inventory_hostname }} is specified in the playbook as - an abstraction of the group of network elements that need to be - configured. - type: str - required: true - username: - description: - - Configures the username used to authenticate the connection to - the remote device. The value of the username parameter is used to - authenticate the SSH session. While generally the value should - come from the inventory file, you can also specify it as a - variable. This parameter is optional. If it is not specified, no - default value will be used. - type: str - required: true - password: - description: - - Configures the password used to authenticate the connection to - the remote device. The value of the password parameter is used to - authenticate the SSH session. While generally the value should - come from the inventory file, you can also specify it as a - variable. This parameter is optional. If it is not specified, no - default value will be used. - type: str - required: true - enablePassword: - description: - - Configures the password used to enter Global Configuration - command mode on the switch. If the switch does not request this - password, the parameter is ignored.While generally the value - should come from the inventory file, you can also specify it as a - variable. This parameter is optional. If it is not specified, - no default value will be used. - type: str - deviceType: - description: - - This specifies the type of device where the method is executed. - The choices NE1072T,NE1032,NE1032T,NE10032,NE2572 are added - since Ansible 2.4. The choice NE0152T is added since 2.8 - type: str - required: true - choices: - - g8272_cnos - - g8296_cnos - - g8332_cnos - - NE0152T - - NE1072T - - NE1032 - - NE1032T - - NE10032 - - NE2572 -notes: - - For more information on using Ansible to manage Lenovo Network devices see U(https://www.ansible.com/ansible-lenovo). -''' diff --git a/plugins/doc_fragments/enos.py b/plugins/doc_fragments/enos.py deleted file mode 100644 index 7b26c9056d..0000000000 --- a/plugins/doc_fragments/enos.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Red Hat Inc. -# Copyright: (c) 2017, Lenovo. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - 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: no - 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. - 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 - required: true - port: - description: - - Specifies the port to use when building the connection to the remote device. - type: int - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. 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: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead. - type: str - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. If the timeout is - exceeded before the operation is completed, the module will error. - type: int - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. 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 - 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: no - 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 -''' diff --git a/plugins/doc_fragments/ingate.py b/plugins/doc_fragments/ingate.py deleted file mode 100644 index ed1882d5c1..0000000000 --- a/plugins/doc_fragments/ingate.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Ingate Systems AB -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - DOCUMENTATION = r''' -options: - client: - description: - - A dict object containing connection details. - suboptions: - version: - description: - - REST API version. - type: str - choices: [ v1 ] - default: v1 - scheme: - description: - - Which HTTP protocol to use. - type: str - required: true - choices: [ http, https ] - address: - description: - - The hostname or IP address to the unit. - type: str - required: true - username: - description: - - The username of the REST API user. - type: str - required: true - password: - description: - - The password for the REST API user. - type: str - required: true - port: - description: - - Which HTTP(S) port to connect to. - type: int - timeout: - description: - - The timeout (in seconds) for REST API requests. - type: int - validate_certs: - description: - - Verify the unit's HTTPS certificate. - type: bool - default: yes - aliases: [ verify_ssl ] -notes: - - This module requires that the Ingate Python SDK is installed on the - host. To install the SDK use the pip command from your shell - C(pip install ingatesdk). -requirements: - - ingatesdk >= 1.0.6 -''' diff --git a/plugins/doc_fragments/ironware.py b/plugins/doc_fragments/ironware.py deleted file mode 100644 index afdc431038..0000000000 --- a/plugins/doc_fragments/ironware.py +++ /dev/null @@ -1,93 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Paul Baker <@paulquack> -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - authorize: - description: - - B(Deprecated) - - "Starting with Ansible 2.7 we recommend using C(connection: network_cli) and C(become: yes)." - - For more information please see the L(IronWare Platform Options guide, ../network/user_guide/platform_ironware.html). - - HORIZONTALLINE - - 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: no - provider: - description: - - B(Deprecated) - - "Starting with Ansible 2.7 we recommend using C(connection: network_cli) and C(become: yes)." - - For more information please see the L(IronWare Platform Options guide, ../network/user_guide/platform_ironware.html). - - HORIZONTALLINE - - 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 - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. 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: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. 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: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. 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 - 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: no - 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 - timeout: - description: - - Specifies idle timeout in seconds for the connection, in seconds. Useful - if the console freezes before continuing. For example when saving - configurations. - type: int - default: 10 -notes: - - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide ` -''' diff --git a/plugins/doc_fragments/netscaler.py b/plugins/doc_fragments/netscaler.py deleted file mode 100644 index 98d464e36f..0000000000 --- a/plugins/doc_fragments/netscaler.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - DOCUMENTATION = r''' - -options: - nsip: - description: - - The ip address of the netscaler appliance where the nitro API calls will be made. - - "The port can be specified with the colon (:). E.g. 192.168.1.1:555." - type: str - required: True - - nitro_user: - description: - - The username with which to authenticate to the netscaler node. - type: str - required: True - - nitro_pass: - description: - - The password with which to authenticate to the netscaler node. - type: str - required: True - - nitro_protocol: - description: - - Which protocol to use when accessing the nitro API objects. - type: str - choices: [ http, https ] - default: http - - validate_certs: - description: - - If C(no), SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates. - type: bool - default: yes - - nitro_timeout: - description: - - Time in seconds until a timeout error is thrown when establishing a new session with Netscaler - type: float - default: 310 - - state: - description: - - The state of the resource being configured by the module on the netscaler node. - - When present the resource will be created if needed and configured according to the module's parameters. - - When absent the resource will be deleted from the netscaler node. - type: str - choices: [ absent, present ] - default: present - - save_config: - description: - - If C(yes) the module will save the configuration on the netscaler node if it makes any changes. - - The module will not save the configuration on the netscaler node if it made no changes. - type: bool - default: yes -notes: - - For more information on using Ansible to manage Citrix NetScaler Network devices see U(https://www.ansible.com/ansible-netscaler). -''' diff --git a/plugins/doc_fragments/nso.py b/plugins/doc_fragments/nso.py deleted file mode 100644 index 47e9acf2db..0000000000 --- a/plugins/doc_fragments/nso.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Cisco and/or its affiliates. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - - DOCUMENTATION = r''' -options: - url: - description: NSO JSON-RPC URL, http://localhost:8080/jsonrpc - type: str - required: true - username: - description: NSO username - type: str - required: true - password: - description: NSO password - type: str - required: true - timeout: - description: JSON-RPC request timeout in seconds - type: int - default: 300 - validate_certs: - description: When set to true, validates the SSL certificate of NSO when - using SSL - type: bool - required: false - default: false -''' diff --git a/plugins/doc_fragments/onyx.py b/plugins/doc_fragments/onyx.py deleted file mode 100644 index 86bc543530..0000000000 --- a/plugins/doc_fragments/onyx.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -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 - required: true - port: - description: - - Specifies the port to use when building the connection to the remote device. - type: int - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. 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: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead. - type: str - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. If the timeout is - exceeded before the operation is completed, the module will error. - type: int - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. 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 - 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: no - 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 -''' diff --git a/plugins/doc_fragments/panos.py b/plugins/doc_fragments/panos.py deleted file mode 100644 index 857f02d677..0000000000 --- a/plugins/doc_fragments/panos.py +++ /dev/null @@ -1,245 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, techbizdev -# Copyright: (c) 2018, Kevin Breit (@kbreit) -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - # Standard files documentation fragment - DOCUMENTATION = r''' -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device. - type: str - required: true - password: - description: - - Password for authentication. - type: str - required: true - username: - description: - - Username for authentication. - type: str - default: admin -''' - - PROVIDER = r''' -options: - provider: - description: - - A dict object containing connection details. - version_added: '2.8' - required: true - suboptions: - ip_address: - description: - - The IP address or hostname of the PAN-OS device being configured. - type: str - required: true - username: - description: - - The username to use for authentication. This is ignored if - I(api_key) is specified. - type: str - default: 'admin' - password: - description: - - The password to use for authentication. This is ignored if - I(api_key) is specified. - type: str - api_key: - description: - - The API key to use instead of generating it using - I(username) / I(password). - type: str - port: - description: - - The port number to connect to the PAN-OS device on. - type: int - default: 443 - serial_number: - description: - - The serial number of a firewall to use for targeted commands. - If I(ip_address) is not a Panorama PAN-OS device, then - this param is ignored. - type: str -''' - - TRANSITIONAL_PROVIDER = r''' -options: - provider: - description: - - A dict object containing connection details. - version_added: '2.8' - suboptions: - ip_address: - description: - - The IP address or hostname of the PAN-OS device being configured. - type: str - username: - description: - - The username to use for authentication. This is ignored if - I(api_key) is specified. - type: str - default: 'admin' - password: - description: - - The password to use for authentication. This is ignored if - I(api_key) is specified. - type: str - api_key: - description: - - The API key to use instead of generating it using - I(username) / I(password). - type: str - port: - description: - - The port number to connect to the PAN-OS device on. - type: int - default: 443 - serial_number: - description: - - The serial number of a firewall to use for targeted commands. - If I(ip_address) is not a Panorama PAN-OS device, then - this param is ignored. - type: str - ip_address: - description: - - B(Deprecated) - - Use I(provider) to specify PAN-OS connectivity instead. - - HORIZONTALLINE - - The IP address or hostname of the PAN-OS device being configured. - type: str - username: - description: - - B(Deprecated) - - Use I(provider) to specify PAN-OS connectivity instead. - - HORIZONTALLINE - - The username to use for authentication. This is ignored if - I(api_key) is specified. - type: str - default: 'admin' - password: - description: - - B(Deprecated) - - Use I(provider) to specify PAN-OS connectivity instead. - - HORIZONTALLINE - - The password to use for authentication. This is ignored if - I(api_key) is specified. - type: str - api_key: - description: - - B(Deprecated) - - Use I(provider) to specify PAN-OS connectivity instead. - - HORIZONTALLINE - - The API key to use instead of generating it using - I(username) / I(password). - type: str - port: - description: - - B(Deprecated) - - Use I(provider) to specify PAN-OS connectivity instead. - - HORIZONTALLINE - - The port number to connect to the PAN-OS device on. - type: int - default: 443 -notes: - - PAN-OS connectivity should be specified using I(provider) or the - classic PAN-OS connectivity params (I(ip_address), I(username), - I(password), I(api_key), and I(port)). If both are present, then the - classic params are ignored. -''' - - STATE = r''' -options: - state: - description: - - The state. - type: str - default: present - choices: - - present - - absent -''' - - RULEBASE = r''' -options: - rulebase: - description: - - The rulebase in which the rule is to exist. If left unspecified, - this defaults to I(rulebase=pre-rulebase) for Panorama. For - NGFW, this is always set to be I(rulebase=rulebase). - type: str - choices: - - pre-rulebase - - rulebase - - post-rulebase -''' - - VSYS_DG = r''' -options: - vsys_dg: - description: - - The vsys (for NGFW) or device group (for Panorama) this - operation should target. If left unspecified, this defaults to - I(vsys_dg=vsys1) for NGFW or I(vsys_dg=shared) for Panorama. - type: str -''' - - DEVICE_GROUP = r''' -options: - device_group: - description: - - (Panorama only) The device group the operation should target. - type: str - default: shared -''' - - VSYS_IMPORT = r''' -options: - vsys: - description: - - The vsys this object should be imported into. Objects that are - imported include interfaces, virtual routers, virtual wires, and - VLANs. Interfaces are typically imported into vsys1 if no vsys - is specified. - type: str -''' - - VSYS = r''' -options: - vsys: - description: - - The vsys this object belongs to. - type: str - default: vsys1 -''' - - TEMPLATE_ONLY = r''' -options: - template: - description: - - (Panorama only) The template this operation should target. This - param is required if the PAN-OS device is Panorama. - type: str -''' - - FULL_TEMPLATE_SUPPORT = r''' -options: - template: - description: - - (Panorama only) The template this operation should target. - Mutually exclusive with I(template_stack). - type: str - template_stack: - description: - - (Panorama only) The template stack this operation should target. - Mutually exclusive with I(template). - type: str -notes: - - If the PAN-OS to be configured is Panorama, either I(template) or - I(template_stack) must be specified. -''' diff --git a/plugins/doc_fragments/sros.py b/plugins/doc_fragments/sros.py deleted file mode 100644 index be66404368..0000000000 --- a/plugins/doc_fragments/sros.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Peter Sprygada -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -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 - required: true - port: - description: - - Specifies the port to use when building the connection to the remote - device. - type: int - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. 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: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead. - type: str - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. If the timeout is - exceeded before the operation is completed, the module will error. - type: int - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. 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 -notes: - - For more information on using Ansible to manage Nokia SR OS Network devices see U(https://www.ansible.com/ansible-nokia). -''' diff --git a/plugins/httpapi/__init__.py b/plugins/httpapi/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/httpapi/exos.py b/plugins/httpapi/exos.py deleted file mode 100644 index 10d25dd4e9..0000000000 --- a/plugins/httpapi/exos.py +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright (c) 2019 Extreme Networks. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -author: - - "Ujwal Komarla (@ujwalkomarla)" -httpapi: exos -short_description: Use EXOS REST APIs to communicate with EXOS platform -description: - - This plugin provides low level abstraction api's to send REST API - requests to EXOS network devices and receive JSON responses. -''' - -import json -import re -from ansible.module_utils._text import to_text -from ansible.module_utils.connection import ConnectionError -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.plugins.httpapi import HttpApiBase -import ansible.module_utils.six.moves.http_cookiejar as cookiejar -from ansible.module_utils.common._collections_compat import Mapping -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - - -class HttpApi(HttpApiBase): - - def __init__(self, *args, **kwargs): - super(HttpApi, self).__init__(*args, **kwargs) - self._device_info = None - self._auth_token = cookiejar.CookieJar() - - def login(self, username, password): - auth_path = '/auth/token' - credentials = {'username': username, 'password': password} - self.send_request(path=auth_path, data=json.dumps(credentials), method='POST') - - def logout(self): - pass - - def handle_httperror(self, exc): - return False - - def send_request(self, path, data=None, method='GET', **message_kwargs): - headers = {'Content-Type': 'application/json'} - response, response_data = self.connection.send(path, data, method=method, cookies=self._auth_token, headers=headers, **message_kwargs) - try: - if response.status == 204: - response_data = {} - else: - response_data = json.loads(to_text(response_data.getvalue())) - except ValueError: - raise ConnectionError('Response was not valid JSON, got {0}'.format( - to_text(response_data.getvalue()) - )) - return response_data - - def run_commands(self, commands, check_rc=True): - if commands is None: - raise ValueError("'commands' value is required") - - headers = {'Content-Type': 'application/json'} - responses = list() - for cmd in to_list(commands): - if not isinstance(cmd, Mapping): - cmd = {'command': cmd} - - cmd['command'] = strip_run_script_cli2json(cmd['command']) - - output = cmd.pop('output', None) - if output and output not in self.get_option_values().get('output'): - raise ValueError("'output' value is %s is invalid. Valid values are %s" % (output, ','.join(self.get_option_values().get('output')))) - - data = request_builder(cmd['command']) - - response, response_data = self.connection.send('/jsonrpc', data, cookies=self._auth_token, headers=headers, method='POST') - try: - response_data = json.loads(to_text(response_data.getvalue())) - except ValueError: - raise ConnectionError('Response was not valid JSON, got {0}'.format( - to_text(response_data.getvalue()) - )) - - if response_data.get('error', None): - raise ConnectionError("Request Error, got {0}".format(response_data['error'])) - if not response_data.get('result', None): - raise ConnectionError("Request Error, got {0}".format(response_data)) - - response_data = response_data['result'] - - if output and output == 'text': - statusOut = getKeyInResponse(response_data, 'status') - cliOut = getKeyInResponse(response_data, 'CLIoutput') - if statusOut == "ERROR": - raise ConnectionError("Command error({1}) for request {0}".format(cmd['command'], cliOut)) - if cliOut is None: - raise ValueError("Response for request {0} doesn't have the CLIoutput field, got {1}".format(cmd['command'], response_data)) - response_data = cliOut - - responses.append(response_data) - return responses - - def get_device_info(self): - device_info = {} - device_info['network_os'] = 'exos' - - reply = self.run_commands({'command': 'show switch detail', 'output': 'text'}) - data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'ExtremeXOS version (\S+)', data) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'System Type: +(\S+)', data) - if match: - device_info['network_os_model'] = match.group(1) - - match = re.search(r'SysName: +(\S+)', data) - if match: - device_info['network_os_hostname'] = match.group(1) - - return device_info - - def get_device_operations(self): - return { - 'supports_diff_replace': False, # identify if config should be merged or replaced is supported - 'supports_commit': False, # identify if commit is supported by device or not - 'supports_rollback': False, # identify if rollback is supported or not - 'supports_defaults': True, # identify if fetching running config with default is supported - 'supports_commit_comment': False, # identify if adding comment to commit is supported of not - 'supports_onbox_diff': False, # identify if on box diff capability is supported or not - 'supports_generate_diff': True, # identify if diff capability is supported within plugin - 'supports_multiline_delimiter': False, # identify if multiline demiliter is supported within config - 'supports_diff_match': True, # identify if match is supported - 'supports_diff_ignore_lines': True, # identify if ignore line in diff is supported - 'supports_config_replace': False, # identify if running config replace with candidate config is supported - 'supports_admin': False, # identify if admin configure mode is supported or not - 'supports_commit_label': False # identify if commit label is supported or not - } - - def get_option_values(self): - return { - 'format': ['text', 'json'], - 'diff_match': ['line', 'strict', 'exact', 'none'], - 'diff_replace': ['line', 'block'], - 'output': ['text', 'json'] - } - - def get_capabilities(self): - result = {} - result['rpc'] = ['get_default_flag', 'run_commands', 'get_config', 'send_request', 'get_capabilities', 'get_diff'] - result['device_info'] = self.get_device_info() - result['device_operations'] = self.get_device_operations() - result.update(self.get_option_values()) - result['network_api'] = 'exosapi' - return json.dumps(result) - - def get_default_flag(self): - # The flag to modify the command to collect configuration with defaults - return 'detail' - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - diff = {} - device_operations = self.get_device_operations() - option_values = self.get_option_values() - - if candidate is None and device_operations['supports_generate_diff']: - raise ValueError("candidate configuration is required to generate diff") - - if diff_match not in option_values['diff_match']: - raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match']))) - - if diff_replace not in option_values['diff_replace']: - raise ValueError("'replace' value %s in invalid, valid values are %s" % (diff_replace, ', '.join(option_values['diff_replace']))) - - # prepare candidate configuration - candidate_obj = NetworkConfig(indent=1) - candidate_obj.load(candidate) - - if running and diff_match != 'none' and diff_replace != 'config': - # running configuration - running_obj = NetworkConfig(indent=1, contents=running, ignore_lines=diff_ignore_lines) - configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace) - - else: - configdiffobjs = candidate_obj.items - - diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else '' - return diff - - def get_config(self, source='running', format='text', flags=None): - options_values = self.get_option_values() - if format not in options_values['format']: - raise ValueError("'format' value %s is invalid. Valid values are %s" % (format, ','.join(options_values['format']))) - - lookup = {'running': 'show configuration', 'startup': 'debug cfgmgr show configuration file'} - if source not in lookup: - raise ValueError("fetching configuration from %s is not supported" % source) - - cmd = {'command': lookup[source], 'output': 'text'} - - if source == 'startup': - reply = self.run_commands({'command': 'show switch', 'format': 'text'}) - data = to_text(reply, errors='surrogate_or_strict').strip() - match = re.search(r'Config Selected: +(\S+)\.cfg', data, re.MULTILINE) - if match: - cmd['command'] += match.group(1) - else: - # No Startup(/Selected) Config - return {} - - cmd['command'] += ' '.join(to_list(flags)) - cmd['command'] = cmd['command'].strip() - - return self.run_commands(cmd)[0] - - -def request_builder(command, reqid=""): - return json.dumps(dict(jsonrpc='2.0', id=reqid, method='cli', params=to_list(command))) - - -def strip_run_script_cli2json(command): - if to_text(command, errors="surrogate_then_replace").startswith('run script cli2json.py'): - command = str(command).replace('run script cli2json.py', '') - return command - - -def getKeyInResponse(response, key): - keyOut = None - for item in response: - if key in item: - keyOut = item[key] - break - return keyOut diff --git a/plugins/httpapi/fortianalyzer.py b/plugins/httpapi/fortianalyzer.py deleted file mode 100644 index 1e870140a2..0000000000 --- a/plugins/httpapi/fortianalyzer.py +++ /dev/null @@ -1,453 +0,0 @@ -# Copyright (c) 2018 Fortinet and/or its affiliates. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -httpapi : fortianalyzer -short_description: HttpApi Plugin for Fortinet FortiAnalyzer Appliance or VM. -description: - - This HttpApi plugin provides methods to connect to Fortinet FortiAnalyzer Appliance or VM via JSON RPC API. - -''' - -import json -from ansible.plugins.httpapi import HttpApiBase -from ansible.module_utils.basic import to_text -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import BASE_HEADERS -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZBaseException -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZCommon -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZMethods - - -class HttpApi(HttpApiBase): - def __init__(self, connection): - super(HttpApi, self).__init__(connection) - self._req_id = 0 - self._sid = None - self._url = "/jsonrpc" - self._host = None - self._tools = FAZCommon - self._debug = False - self._connected_faz = None - self._last_response_msg = None - self._last_response_code = None - self._last_data_payload = None - self._last_url = None - self._last_response_raw = None - self._locked_adom_list = list() - self._locked_adoms_by_user = list() - self._uses_workspace = False - self._uses_adoms = False - self._adom_list = list() - self._logged_in_user = None - - def set_become(self, become_context): - """ - ELEVATION IS NOT REQUIRED ON FORTINET DEVICES - SKIPPED - :param become_context: Unused input. - :return: None - """ - return None - - def update_auth(self, response, response_data): - """ - TOKENS ARE NOT USED SO NO NEED TO UPDATE AUTH - :param response: Unused input. - :param response_data Unused_input. - :return: None - """ - return None - - def login(self, username, password): - """ - This function will log the plugin into FortiAnalyzer, and return the results. - :param username: Username of FortiAnalyzer Admin - :param password: Password of FortiAnalyzer Admin - - :return: Dictionary of status if it logged in or not. - """ - - self._logged_in_user = username - self.send_request(FAZMethods.EXEC, self._tools.format_request(FAZMethods.EXEC, "sys/login/user", - passwd=password, user=username,)) - - if "FortiAnalyzer object connected to FortiAnalyzer" in self.__str__(): - # If Login worked then inspect the FortiAnalyzer for Workspace Mode, and it's system information. - self.inspect_faz() - return - else: - raise FAZBaseException(msg="Unknown error while logging in...connection was lost during login operation..." - " Exiting") - - def inspect_faz(self): - # CHECK FOR WORKSPACE MODE TO SEE IF WE HAVE TO ENABLE ADOM LOCKS - status = self.get_system_status() - if status[0] == -11: - # THE CONNECTION GOT LOST SOMEHOW, REMOVE THE SID AND REPORT BAD LOGIN - self.logout() - raise FAZBaseException(msg="Error -11 -- the Session ID was likely malformed somehow. Contact authors." - " Exiting") - elif status[0] == 0: - try: - self.check_mode() - if self._uses_adoms: - self.get_adom_list() - if self._uses_workspace: - self.get_locked_adom_list() - self._connected_faz = status[1] - self._host = self._connected_faz["Hostname"] - except Exception: - pass - return - - def logout(self): - """ - This function will logout of the FortiAnalyzer. - """ - if self.sid is not None: - # IF WE WERE USING WORKSPACES, THEN CLEAN UP OUR LOCKS IF THEY STILL EXIST - if self.uses_workspace: - self.get_lock_info() - self.run_unlock() - ret_code, response = self.send_request(FAZMethods.EXEC, - self._tools.format_request(FAZMethods.EXEC, "sys/logout")) - self.sid = None - return ret_code, response - - def send_request(self, method, params): - """ - Responsible for actual sending of data to the connection httpapi base plugin. Does some formatting as well. - :param params: A formatted dictionary that was returned by self.common_datagram_params() - before being called here. - :param method: The preferred API Request method (GET, ADD, POST, etc....) - :type method: basestring - - :return: Dictionary of status if it logged in or not. - """ - - try: - if self.sid is None and params[0]["url"] != "sys/login/user": - try: - self.connection._connect() - except Exception as err: - raise FAZBaseException( - msg="An problem happened with the httpapi plugin self-init connection process. " - "Error: " + to_text(err)) - except IndexError: - raise FAZBaseException("An attempt was made at communicating with a FAZ with " - "no valid session and an incorrectly formatted request.") - except Exception: - raise FAZBaseException("An attempt was made at communicating with a FAZ with " - "no valid session and an unexpected error was discovered.") - - self._update_request_id() - json_request = { - "method": method, - "params": params, - "session": self.sid, - "id": self.req_id, - "verbose": 1 - } - data = json.dumps(json_request, ensure_ascii=False).replace('\\\\', '\\') - try: - # Sending URL and Data in Unicode, per Ansible Specifications for Connection Plugins - response, response_data = self.connection.send(path=to_text(self._url), data=to_text(data), - headers=BASE_HEADERS) - # Get Unicode Response - Must convert from StringIO to unicode first so we can do a replace function below - result = json.loads(to_text(response_data.getvalue())) - self._update_self_from_response(result, self._url, data) - return self._handle_response(result) - except Exception as err: - raise FAZBaseException(err) - - def _handle_response(self, response): - self._set_sid(response) - if isinstance(response["result"], list): - result = response["result"][0] - else: - result = response["result"] - if "data" in result: - return result["status"]["code"], result["data"] - else: - return result["status"]["code"], result - - def _update_self_from_response(self, response, url, data): - self._last_response_raw = response - if isinstance(response["result"], list): - result = response["result"][0] - else: - result = response["result"] - if "status" in result: - self._last_response_code = result["status"]["code"] - self._last_response_msg = result["status"]["message"] - self._last_url = url - self._last_data_payload = data - - def _set_sid(self, response): - if self.sid is None and "session" in response: - self.sid = response["session"] - - def return_connected_faz(self): - """ - Returns the data stored under self._connected_faz - - :return: dict - """ - try: - if self._connected_faz: - return self._connected_faz - except Exception: - raise FAZBaseException("Couldn't Retrieve Connected FAZ Stats") - - def get_system_status(self): - """ - Returns the system status page from the FortiAnalyzer, for logging and other uses. - return: status - """ - status = self.send_request(FAZMethods.GET, self._tools.format_request(FAZMethods.GET, "sys/status")) - return status - - @property - def debug(self): - return self._debug - - @debug.setter - def debug(self, val): - self._debug = val - - @property - def req_id(self): - return self._req_id - - @req_id.setter - def req_id(self, val): - self._req_id = val - - def _update_request_id(self, reqid=0): - self.req_id = reqid if reqid != 0 else self.req_id + 1 - - @property - def sid(self): - return self._sid - - @sid.setter - def sid(self, val): - self._sid = val - - def __str__(self): - if self.sid is not None and self.connection._url is not None: - return "FortiAnalyzer object connected to FortiAnalyzer: " + to_text(self.connection._url) - return "FortiAnalyzer object with no valid connection to a FortiAnalyzer appliance." - - ################################## - # BEGIN DATABASE LOCK CONTEXT CODE - ################################## - - @property - def uses_workspace(self): - return self._uses_workspace - - @uses_workspace.setter - def uses_workspace(self, val): - self._uses_workspace = val - - @property - def uses_adoms(self): - return self._uses_adoms - - @uses_adoms.setter - def uses_adoms(self, val): - self._uses_adoms = val - - def add_adom_to_lock_list(self, adom): - if adom not in self._locked_adom_list: - self._locked_adom_list.append(adom) - - def remove_adom_from_lock_list(self, adom): - if adom in self._locked_adom_list: - self._locked_adom_list.remove(adom) - - def check_mode(self): - """ - Checks FortiAnalyzer for the use of Workspace mode - """ - url = "/cli/global/system/global" - code, resp_obj = self.send_request(FAZMethods.GET, - self._tools.format_request(FAZMethods.GET, - url, - fields=["workspace-mode", "adom-status"])) - try: - if resp_obj["workspace-mode"] == "workflow": - self.uses_workspace = True - elif resp_obj["workspace-mode"] == "disabled": - self.uses_workspace = False - except KeyError: - self.uses_workspace = False - except Exception: - raise FAZBaseException(msg="Couldn't determine workspace-mode in the plugin") - try: - if resp_obj["adom-status"] in [1, "enable"]: - self.uses_adoms = True - else: - self.uses_adoms = False - except KeyError: - self.uses_adoms = False - except Exception: - raise FAZBaseException(msg="Couldn't determine adom-status in the plugin") - - def run_unlock(self): - """ - Checks for ADOM status, if locked, it will unlock - """ - for adom_locked in self._locked_adoms_by_user: - adom = adom_locked["adom"] - self.unlock_adom(adom) - - def lock_adom(self, adom=None, *args, **kwargs): - """ - Locks an ADOM for changes - """ - if adom: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/lock/" - else: - url = "/dvmdb/adom/{adom}/workspace/lock/".format(adom=adom) - else: - url = "/dvmdb/adom/root/workspace/lock" - code, respobj = self.send_request(FAZMethods.EXEC, self._tools.format_request(FAZMethods.EXEC, url)) - if code == 0 and respobj["status"]["message"].lower() == "ok": - self.add_adom_to_lock_list(adom) - return code, respobj - - def unlock_adom(self, adom=None, *args, **kwargs): - """ - Unlocks an ADOM after changes - """ - if adom: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/unlock/" - else: - url = "/dvmdb/adom/{adom}/workspace/unlock/".format(adom=adom) - else: - url = "/dvmdb/adom/root/workspace/unlock" - code, respobj = self.send_request(FAZMethods.EXEC, self._tools.format_request(FAZMethods.EXEC, url)) - if code == 0 and respobj["status"]["message"].lower() == "ok": - self.remove_adom_from_lock_list(adom) - return code, respobj - - def commit_changes(self, adom=None, aux=False, *args, **kwargs): - """ - Commits changes to an ADOM - """ - if adom: - if aux: - url = "/pm/config/adom/{adom}/workspace/commit".format(adom=adom) - else: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/commit/" - else: - url = "/dvmdb/adom/{adom}/workspace/commit".format(adom=adom) - else: - url = "/dvmdb/adom/root/workspace/commit" - return self.send_request(FAZMethods.EXEC, self._tools.format_request(FAZMethods.EXEC, url)) - - def get_lock_info(self, adom=None): - """ - Gets ADOM lock info so it can be displayed with the error messages. Or if determined to be locked by ansible - for some reason, then unlock it. - """ - if not adom or adom == "root": - url = "/dvmdb/adom/root/workspace/lockinfo" - else: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/lockinfo/" - else: - url = "/dvmdb/adom/{adom}/workspace/lockinfo/".format(adom=adom) - datagram = {} - data = self._tools.format_request(FAZMethods.GET, url, **datagram) - resp_obj = self.send_request(FAZMethods.GET, data) - code = resp_obj[0] - if code != 0: - self._module.fail_json(msg=("An error occurred trying to get the ADOM Lock Info. Error: " + to_text(resp_obj))) - elif code == 0: - try: - if resp_obj[1]["status"]["message"] == "OK": - self._lock_info = None - except Exception: - self._lock_info = resp_obj[1] - return resp_obj - - def get_adom_list(self): - """ - Gets the list of ADOMs for the FortiAnalyzer - """ - if self.uses_adoms: - url = "/dvmdb/adom" - datagram = {} - data = self._tools.format_request(FAZMethods.GET, url, **datagram) - resp_obj = self.send_request(FAZMethods.GET, data) - code = resp_obj[0] - if code != 0: - self._module.fail_json(msg=("An error occurred trying to get the ADOM Info. Error: " + to_text(resp_obj))) - elif code == 0: - num_of_adoms = len(resp_obj[1]) - append_list = ['root', ] - for adom in resp_obj[1]: - if adom["tab_status"] != "": - append_list.append(to_text(adom["name"])) - self._adom_list = append_list - return resp_obj - - def get_locked_adom_list(self): - """ - Gets the list of locked adoms - """ - try: - locked_list = list() - locked_by_user_list = list() - for adom in self._adom_list: - adom_lock_info = self.get_lock_info(adom=adom) - try: - if adom_lock_info[1]["status"]["message"] == "OK": - continue - except Exception: - pass - try: - if adom_lock_info[1][0]["lock_user"]: - locked_list.append(to_text(adom)) - if adom_lock_info[1][0]["lock_user"] == self._logged_in_user: - locked_by_user_list.append({"adom": to_text(adom), "user": to_text(adom_lock_info[1][0]["lock_user"])}) - except Exception as err: - raise FAZBaseException(err) - self._locked_adom_list = locked_list - self._locked_adoms_by_user = locked_by_user_list - - except Exception as err: - raise FAZBaseException(msg=("An error occurred while trying to get the locked adom list. Error: " - + to_text(err))) - - ################################# - # END DATABASE LOCK CONTEXT CODE - ################################# diff --git a/plugins/httpapi/fortimanager.py b/plugins/httpapi/fortimanager.py deleted file mode 100644 index 1c80c2808e..0000000000 --- a/plugins/httpapi/fortimanager.py +++ /dev/null @@ -1,451 +0,0 @@ -# Copyright (c) 2018 Fortinet and/or its affiliates. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -httpapi : fortimanager -short_description: HttpApi Plugin for Fortinet FortiManager Appliance or VM. -description: - - This HttpApi plugin provides methods to connect to Fortinet FortiManager Appliance or VM via JSON RPC API. -''' - -import json -from ansible.plugins.httpapi import HttpApiBase -from ansible.module_utils.basic import to_text -from ansible_collections.fortinet.fortios.plugins.module_utils.network.fortimanager.common import BASE_HEADERS -from ansible_collections.fortinet.fortios.plugins.module_utils.network.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.network.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.network.fortimanager.common import FMGRMethods - - -class HttpApi(HttpApiBase): - def __init__(self, connection): - super(HttpApi, self).__init__(connection) - self._req_id = 0 - self._sid = None - self._url = "/jsonrpc" - self._host = None - self._tools = FMGRCommon - self._debug = False - self._connected_fmgr = None - self._last_response_msg = None - self._last_response_code = None - self._last_data_payload = None - self._last_url = None - self._last_response_raw = None - self._locked_adom_list = list() - self._locked_adoms_by_user = list() - self._uses_workspace = False - self._uses_adoms = False - self._adom_list = list() - self._logged_in_user = None - - def set_become(self, become_context): - """ - ELEVATION IS NOT REQUIRED ON FORTINET DEVICES - SKIPPED. - :param become_context: Unused input. - :return: None - """ - return None - - def update_auth(self, response, response_data): - """ - TOKENS ARE NOT USED SO NO NEED TO UPDATE AUTH. - :param response: Unused input. - :param response_data Unused_input. - :return: None - """ - return None - - def login(self, username, password): - - """ - This function will log the plugin into FortiManager, and return the results. - :param username: Username of FortiManager Admin - :param password: Password of FortiManager Admin - - :return: Dictionary of status if it logged in or not. - """ - self._logged_in_user = username - self.send_request(FMGRMethods.EXEC, self._tools.format_request(FMGRMethods.EXEC, "sys/login/user", - passwd=password, user=username, )) - - if "FortiManager object connected to FortiManager" in self.__str__(): - # If Login worked, then inspect the FortiManager for Workspace Mode, and it's system information. - self.inspect_fmgr() - return - else: - raise FMGBaseException(msg="Unknown error while logging in...connection was lost during login operation...." - " Exiting") - - def inspect_fmgr(self): - # CHECK FOR WORKSPACE MODE TO SEE IF WE HAVE TO ENABLE ADOM LOCKS - status = self.get_system_status() - if status[0] == -11: - # THE CONNECTION GOT LOST SOMEHOW, REMOVE THE SID AND REPORT BAD LOGIN - self.logout() - raise FMGBaseException(msg="Error -11 -- the Session ID was likely malformed somehow. Contact authors." - " Exiting") - elif status[0] == 0: - try: - self.check_mode() - if self._uses_adoms: - self.get_adom_list() - if self._uses_workspace: - self.get_locked_adom_list() - self._connected_fmgr = status[1] - self._host = self._connected_fmgr["Hostname"] - except BaseException: - pass - return - - def logout(self): - """ - This function will logout of the FortiManager. - """ - if self.sid is not None: - # IF WE WERE USING WORKSPACES, THEN CLEAN UP OUR LOCKS IF THEY STILL EXIST - if self.uses_workspace: - self.get_lock_info() - self.run_unlock() - ret_code, response = self.send_request(FMGRMethods.EXEC, - self._tools.format_request(FMGRMethods.EXEC, "sys/logout")) - self.sid = None - return ret_code, response - - def send_request(self, method, params): - """ - Responsible for actual sending of data to the connection httpapi base plugin. Does some formatting too. - :param params: A formatted dictionary that was returned by self.common_datagram_params() - before being called here. - :param method: The preferred API Request method (GET, ADD, POST, etc....) - :type method: basestring - - :return: Dictionary of status, if it logged in or not. - """ - try: - if self.sid is None and params[0]["url"] != "sys/login/user": - try: - self.connection._connect() - except Exception as err: - raise FMGBaseException( - msg="An problem happened with the httpapi plugin self-init connection process. " - "Error: " + to_text(err)) - except IndexError: - raise FMGBaseException("An attempt was made at communicating with a FMG with " - "no valid session and an incorrectly formatted request.") - except Exception as err: - raise FMGBaseException("An attempt was made at communicating with a FMG with " - "no valid session and an unexpected error was discovered. \n Error: " + to_text(err)) - - self._update_request_id() - json_request = { - "method": method, - "params": params, - "session": self.sid, - "id": self.req_id, - "verbose": 1 - } - data = json.dumps(json_request, ensure_ascii=False).replace('\\\\', '\\') - try: - # Sending URL and Data in Unicode, per Ansible Specifications for Connection Plugins - response, response_data = self.connection.send(path=to_text(self._url), data=to_text(data), - headers=BASE_HEADERS) - # Get Unicode Response - Must convert from StringIO to unicode first so we can do a replace function below - result = json.loads(to_text(response_data.getvalue())) - self._update_self_from_response(result, self._url, data) - return self._handle_response(result) - except Exception as err: - raise FMGBaseException(err) - - def _handle_response(self, response): - self._set_sid(response) - if isinstance(response["result"], list): - result = response["result"][0] - else: - result = response["result"] - if "data" in result: - return result["status"]["code"], result["data"] - else: - return result["status"]["code"], result - - def _update_self_from_response(self, response, url, data): - self._last_response_raw = response - if isinstance(response["result"], list): - result = response["result"][0] - else: - result = response["result"] - if "status" in result: - self._last_response_code = result["status"]["code"] - self._last_response_msg = result["status"]["message"] - self._last_url = url - self._last_data_payload = data - - def _set_sid(self, response): - if self.sid is None and "session" in response: - self.sid = response["session"] - - def return_connected_fmgr(self): - """ - Returns the data stored under self._connected_fmgr - - :return: dict - """ - try: - if self._connected_fmgr: - return self._connected_fmgr - except Exception: - raise FMGBaseException("Couldn't Retrieve Connected FMGR Stats") - - def get_system_status(self): - """ - Returns the system status page from the FortiManager, for logging and other uses. - return: status - """ - status = self.send_request(FMGRMethods.GET, self._tools.format_request(FMGRMethods.GET, "sys/status")) - return status - - @property - def debug(self): - return self._debug - - @debug.setter - def debug(self, val): - self._debug = val - - @property - def req_id(self): - return self._req_id - - @req_id.setter - def req_id(self, val): - self._req_id = val - - def _update_request_id(self, reqid=0): - self.req_id = reqid if reqid != 0 else self.req_id + 1 - - @property - def sid(self): - return self._sid - - @sid.setter - def sid(self, val): - self._sid = val - - def __str__(self): - if self.sid is not None and self.connection._url is not None: - return "FortiManager object connected to FortiManager: " + to_text(self.connection._url) - return "FortiManager object with no valid connection to a FortiManager appliance." - - ################################## - # BEGIN DATABASE LOCK CONTEXT CODE - ################################## - - @property - def uses_workspace(self): - return self._uses_workspace - - @uses_workspace.setter - def uses_workspace(self, val): - self._uses_workspace = val - - @property - def uses_adoms(self): - return self._uses_adoms - - @uses_adoms.setter - def uses_adoms(self, val): - self._uses_adoms = val - - def add_adom_to_lock_list(self, adom): - if adom not in self._locked_adom_list: - self._locked_adom_list.append(adom) - - def remove_adom_from_lock_list(self, adom): - if adom in self._locked_adom_list: - self._locked_adom_list.remove(adom) - - def check_mode(self): - """ - Checks FortiManager for the use of Workspace mode - """ - url = "/cli/global/system/global" - code, resp_obj = self.send_request(FMGRMethods.GET, - self._tools.format_request(FMGRMethods.GET, - url, - fields=["workspace-mode", "adom-status"])) - try: - if resp_obj["workspace-mode"] == "workflow": - self.uses_workspace = True - elif resp_obj["workspace-mode"] == "disabled": - self.uses_workspace = False - except KeyError: - raise FMGBaseException(msg="Couldn't determine workspace-mode in the plugin") - try: - if resp_obj["adom-status"] in [1, "enable"]: - self.uses_adoms = True - else: - self.uses_adoms = False - except KeyError: - raise FMGBaseException(msg="Couldn't determine adom-status in the plugin") - - def run_unlock(self): - """ - Checks for ADOM status, if locked, it will unlock - """ - for adom_locked in self._locked_adoms_by_user: - adom = adom_locked["adom"] - self.unlock_adom(adom) - - def lock_adom(self, adom=None, *args, **kwargs): - """ - Locks an ADOM for changes - """ - if adom: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/lock/" - else: - url = "/dvmdb/adom/{adom}/workspace/lock/".format(adom=adom) - else: - url = "/dvmdb/adom/root/workspace/lock" - code, respobj = self.send_request(FMGRMethods.EXEC, self._tools.format_request(FMGRMethods.EXEC, url)) - if code == 0 and respobj["status"]["message"].lower() == "ok": - self.add_adom_to_lock_list(adom) - return code, respobj - - def unlock_adom(self, adom=None, *args, **kwargs): - """ - Unlocks an ADOM after changes - """ - if adom: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/unlock/" - else: - url = "/dvmdb/adom/{adom}/workspace/unlock/".format(adom=adom) - else: - url = "/dvmdb/adom/root/workspace/unlock" - code, respobj = self.send_request(FMGRMethods.EXEC, self._tools.format_request(FMGRMethods.EXEC, url)) - if code == 0 and respobj["status"]["message"].lower() == "ok": - self.remove_adom_from_lock_list(adom) - return code, respobj - - def commit_changes(self, adom=None, aux=False, *args, **kwargs): - """ - Commits changes to an ADOM - """ - if adom: - if aux: - url = "/pm/config/adom/{adom}/workspace/commit".format(adom=adom) - else: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/commit/" - else: - url = "/dvmdb/adom/{adom}/workspace/commit".format(adom=adom) - else: - url = "/dvmdb/adom/root/workspace/commit" - return self.send_request(FMGRMethods.EXEC, self._tools.format_request(FMGRMethods.EXEC, url)) - - def get_lock_info(self, adom=None): - """ - Gets ADOM lock info so it can be displayed with the error messages. Or if determined to be locked by ansible - for some reason, then unlock it. - """ - if not adom or adom == "root": - url = "/dvmdb/adom/root/workspace/lockinfo" - else: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/lockinfo/" - else: - url = "/dvmdb/adom/{adom}/workspace/lockinfo/".format(adom=adom) - datagram = {} - data = self._tools.format_request(FMGRMethods.GET, url, **datagram) - resp_obj = self.send_request(FMGRMethods.GET, data) - code = resp_obj[0] - if code != 0: - self._module.fail_json(msg=("An error occurred trying to get the ADOM Lock Info. " - "Error: " + to_text(resp_obj))) - elif code == 0: - try: - if resp_obj[1]["status"]["message"] == "OK": - self._lock_info = None - except Exception: - self._lock_info = resp_obj[1] - return resp_obj - - def get_adom_list(self): - """ - Gets the list of ADOMs for the FortiManager - """ - if self.uses_adoms: - url = "/dvmdb/adom" - datagram = {} - data = self._tools.format_request(FMGRMethods.GET, url, **datagram) - resp_obj = self.send_request(FMGRMethods.GET, data) - code = resp_obj[0] - if code != 0: - self._module.fail_json(msg=("An error occurred trying to get the ADOM Info. " - "Error: " + to_text(resp_obj))) - elif code == 0: - num_of_adoms = len(resp_obj[1]) - append_list = ['root', ] - for adom in resp_obj[1]: - if adom["tab_status"] != "": - append_list.append(to_text(adom["name"])) - self._adom_list = append_list - return resp_obj - - def get_locked_adom_list(self): - """ - Gets the list of locked adoms - """ - try: - locked_list = list() - locked_by_user_list = list() - for adom in self._adom_list: - adom_lock_info = self.get_lock_info(adom=adom) - try: - if adom_lock_info[1]["status"]["message"] == "OK": - continue - except IndexError as err: - pass - try: - if adom_lock_info[1][0]["lock_user"]: - locked_list.append(to_text(adom)) - if adom_lock_info[1][0]["lock_user"] == self._logged_in_user: - locked_by_user_list.append({"adom": to_text(adom), - "user": to_text(adom_lock_info[1][0]["lock_user"])}) - except Exception as err: - raise FMGBaseException(err) - self._locked_adom_list = locked_list - self._locked_adoms_by_user = locked_by_user_list - - except Exception as err: - raise FMGBaseException(msg=("An error occurred while trying to get the locked adom list. Error: " - + to_text(err))) - - ################################ - # END DATABASE LOCK CONTEXT CODE - ################################ diff --git a/plugins/httpapi/ftd.py b/plugins/httpapi/ftd.py deleted file mode 100644 index 73db5ef79d..0000000000 --- a/plugins/httpapi/ftd.py +++ /dev/null @@ -1,386 +0,0 @@ -# Copyright (c) 2018 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) - -__metaclass__ = type - -DOCUMENTATION = ''' ---- -author: Ansible Networking Team -httpapi : ftd -short_description: HttpApi Plugin for Cisco ASA Firepower device -description: - - This HttpApi plugin provides methods to connect to Cisco ASA firepower - devices over a HTTP(S)-based api. -options: - token_path: - type: str - description: - - Specifies the api token path of the FTD device - vars: - - name: ansible_httpapi_ftd_token_path - spec_path: - type: str - description: - - Specifies the api spec path of the FTD device - default: '/apispec/ngfw.json' - vars: - - name: ansible_httpapi_ftd_spec_path -''' - -import json -import os -import re - -from ansible import __version__ as ansible_version - -from ansible.module_utils.basic import to_text -from ansible.errors import AnsibleConnectionFailure -from ansible_collections.community.general.plugins.module_utils.network.ftd.fdm_swagger_client import FdmSwaggerParser, SpecProp, FdmSwaggerValidator -from ansible_collections.community.general.plugins.module_utils.network.ftd.common import HTTPMethod, ResponseParams -from ansible.module_utils.six.moves.urllib.error import HTTPError -from ansible.module_utils.six.moves.urllib.parse import urlencode -from ansible.plugins.httpapi import HttpApiBase -from urllib3 import encode_multipart_formdata -from urllib3.fields import RequestField -from ansible.module_utils.connection import ConnectionError - -BASE_HEADERS = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'FTD Ansible/%s' % ansible_version -} - -TOKEN_EXPIRATION_STATUS_CODE = 408 -UNAUTHORIZED_STATUS_CODE = 401 -API_TOKEN_PATH_OPTION_NAME = 'token_path' -TOKEN_PATH_TEMPLATE = '/api/fdm/{0}/fdm/token' -GET_API_VERSIONS_PATH = '/api/versions' -DEFAULT_API_VERSIONS = ['v2', 'v1'] - -INVALID_API_TOKEN_PATH_MSG = ('The API token path is incorrect. Please, check correctness of ' - 'the `ansible_httpapi_ftd_token_path` variable in the inventory file.') -MISSING_API_TOKEN_PATH_MSG = ('Ansible could not determine the API token path automatically. Please, ' - 'specify the `ansible_httpapi_ftd_token_path` variable in the inventory file.') - - -class HttpApi(HttpApiBase): - def __init__(self, connection): - super(HttpApi, self).__init__(connection) - self.connection = connection - self.access_token = None - self.refresh_token = None - self._api_spec = None - self._api_validator = None - self._ignore_http_errors = False - - def login(self, username, password): - def request_token_payload(username, password): - return { - 'grant_type': 'password', - 'username': username, - 'password': password - } - - def refresh_token_payload(refresh_token): - return { - 'grant_type': 'refresh_token', - 'refresh_token': refresh_token - } - - if self.refresh_token: - payload = refresh_token_payload(self.refresh_token) - elif username and password: - payload = request_token_payload(username, password) - else: - raise AnsibleConnectionFailure('Username and password are required for login in absence of refresh token') - - response = self._lookup_login_url(payload) - - try: - self.refresh_token = response['refresh_token'] - self.access_token = response['access_token'] - self.connection._auth = {'Authorization': 'Bearer %s' % self.access_token} - except KeyError: - raise ConnectionError( - 'Server returned response without token info during connection authentication: %s' % response) - - def _lookup_login_url(self, payload): - """ Try to find correct login URL and get api token using this URL. - - :param payload: Token request payload - :type payload: dict - :return: token generation response - """ - preconfigured_token_path = self._get_api_token_path() - if preconfigured_token_path: - token_paths = [preconfigured_token_path] - else: - token_paths = self._get_known_token_paths() - - for url in token_paths: - try: - response = self._send_login_request(payload, url) - - except ConnectionError as e: - self.connection.queue_message('vvvv', 'REST:request to %s failed because of connection error: %s ' % ( - url, e)) - # In the case of ConnectionError caused by HTTPError we should check response code. - # Response code 400 returned in case of invalid credentials so we should stop attempts to log in and - # inform the user. - if hasattr(e, 'http_code') and e.http_code == 400: - raise - else: - if not preconfigured_token_path: - self._set_api_token_path(url) - return response - - raise ConnectionError(INVALID_API_TOKEN_PATH_MSG if preconfigured_token_path else MISSING_API_TOKEN_PATH_MSG) - - def _send_login_request(self, payload, url): - self._display(HTTPMethod.POST, 'login', url) - response, response_data = self._send_auth_request( - url, json.dumps(payload), method=HTTPMethod.POST, headers=BASE_HEADERS - ) - self._display(HTTPMethod.POST, 'login:status_code', response.getcode()) - - response = self._response_to_json(self._get_response_value(response_data)) - return response - - def logout(self): - auth_payload = { - 'grant_type': 'revoke_token', - 'access_token': self.access_token, - 'token_to_revoke': self.refresh_token - } - - url = self._get_api_token_path() - - self._display(HTTPMethod.POST, 'logout', url) - response, dummy = self._send_auth_request(url, json.dumps(auth_payload), method=HTTPMethod.POST, - headers=BASE_HEADERS) - self._display(HTTPMethod.POST, 'logout:status_code', response.getcode()) - - self.refresh_token = None - self.access_token = None - - def _send_auth_request(self, path, data, **kwargs): - error_msg_prefix = 'Server returned an error during authentication request' - return self._send_service_request(path, error_msg_prefix, data=data, **kwargs) - - def _send_service_request(self, path, error_msg_prefix, data=None, **kwargs): - try: - self._ignore_http_errors = True - return self.connection.send(path, data, **kwargs) - except HTTPError as e: - # HttpApi connection does not read the error response from HTTPError, so we do it here and wrap it up in - # ConnectionError, so the actual error message is displayed to the user. - error_msg = self._response_to_json(to_text(e.read())) - raise ConnectionError('%s: %s' % (error_msg_prefix, error_msg), http_code=e.code) - finally: - self._ignore_http_errors = False - - def update_auth(self, response, response_data): - # With tokens, authentication should not be checked and updated on each request - return None - - def send_request(self, url_path, http_method, body_params=None, path_params=None, query_params=None): - url = construct_url_path(url_path, path_params, query_params) - data = json.dumps(body_params) if body_params else None - try: - self._display(http_method, 'url', url) - if data: - self._display(http_method, 'data', data) - - response, response_data = self.connection.send(url, data, method=http_method, headers=BASE_HEADERS) - - value = self._get_response_value(response_data) - self._display(http_method, 'response', value) - - return { - ResponseParams.SUCCESS: True, - ResponseParams.STATUS_CODE: response.getcode(), - ResponseParams.RESPONSE: self._response_to_json(value) - } - # Being invoked via JSON-RPC, this method does not serialize and pass HTTPError correctly to the method caller. - # Thus, in order to handle non-200 responses, we need to wrap them into a simple structure and pass explicitly. - except HTTPError as e: - error_msg = to_text(e.read()) - self._display(http_method, 'error', error_msg) - return { - ResponseParams.SUCCESS: False, - ResponseParams.STATUS_CODE: e.code, - ResponseParams.RESPONSE: self._response_to_json(error_msg) - } - - def upload_file(self, from_path, to_url): - url = construct_url_path(to_url) - self._display(HTTPMethod.POST, 'upload', url) - with open(from_path, 'rb') as src_file: - rf = RequestField('fileToUpload', src_file.read(), os.path.basename(src_file.name)) - rf.make_multipart() - body, content_type = encode_multipart_formdata([rf]) - - headers = dict(BASE_HEADERS) - headers['Content-Type'] = content_type - headers['Content-Length'] = len(body) - - dummy, response_data = self.connection.send(url, data=body, method=HTTPMethod.POST, headers=headers) - value = self._get_response_value(response_data) - self._display(HTTPMethod.POST, 'upload:response', value) - return self._response_to_json(value) - - def download_file(self, from_url, to_path, path_params=None): - url = construct_url_path(from_url, path_params=path_params) - self._display(HTTPMethod.GET, 'download', url) - response, response_data = self.connection.send(url, data=None, method=HTTPMethod.GET, headers=BASE_HEADERS) - - if os.path.isdir(to_path): - filename = extract_filename_from_headers(response.info()) - to_path = os.path.join(to_path, filename) - - with open(to_path, "wb") as output_file: - output_file.write(response_data.getvalue()) - self._display(HTTPMethod.GET, 'downloaded', to_path) - - def handle_httperror(self, exc): - is_auth_related_code = exc.code == TOKEN_EXPIRATION_STATUS_CODE or exc.code == UNAUTHORIZED_STATUS_CODE - if not self._ignore_http_errors and is_auth_related_code: - self.connection._auth = None - self.login(self.connection.get_option('remote_user'), self.connection.get_option('password')) - return True - # False means that the exception will be passed further to the caller - return False - - def _display(self, http_method, title, msg=''): - self.connection.queue_message('vvvv', 'REST:%s:%s:%s\n%s' % (http_method, self.connection._url, title, msg)) - - @staticmethod - def _get_response_value(response_data): - return to_text(response_data.getvalue()) - - def _get_api_spec_path(self): - return self.get_option('spec_path') - - def _get_known_token_paths(self): - """Generate list of token generation urls based on list of versions supported by device(if exposed via API) or - default list of API versions. - - :returns: list of token generation urls - :rtype: generator - """ - try: - api_versions = self._get_supported_api_versions() - except ConnectionError: - # API versions API is not supported we need to check all known version - api_versions = DEFAULT_API_VERSIONS - - return [TOKEN_PATH_TEMPLATE.format(version) for version in api_versions] - - def _get_supported_api_versions(self): - """ - Fetch list of API versions supported by device. - - :return: list of API versions suitable for device - :rtype: list - """ - # Try to fetch supported API version - http_method = HTTPMethod.GET - response, response_data = self._send_service_request( - path=GET_API_VERSIONS_PATH, - error_msg_prefix="Can't fetch list of supported api versions", - method=http_method, - headers=BASE_HEADERS - ) - - value = self._get_response_value(response_data) - self._display(http_method, 'response', value) - api_versions_info = self._response_to_json(value) - return api_versions_info["supportedVersions"] - - def _get_api_token_path(self): - return self.get_option(API_TOKEN_PATH_OPTION_NAME) - - def _set_api_token_path(self, url): - return self.set_option(API_TOKEN_PATH_OPTION_NAME, url) - - @staticmethod - def _response_to_json(response_text): - try: - return json.loads(response_text) if response_text else {} - # JSONDecodeError only available on Python 3.5+ - except getattr(json.decoder, 'JSONDecodeError', ValueError): - raise ConnectionError('Invalid JSON response: %s' % response_text) - - def get_operation_spec(self, operation_name): - return self.api_spec[SpecProp.OPERATIONS].get(operation_name, None) - - def get_operation_specs_by_model_name(self, model_name): - if model_name: - return self.api_spec[SpecProp.MODEL_OPERATIONS].get(model_name, None) - else: - return None - - def get_model_spec(self, model_name): - return self.api_spec[SpecProp.MODELS].get(model_name, None) - - def validate_data(self, operation_name, data): - return self.api_validator.validate_data(operation_name, data) - - def validate_query_params(self, operation_name, params): - return self.api_validator.validate_query_params(operation_name, params) - - def validate_path_params(self, operation_name, params): - return self.api_validator.validate_path_params(operation_name, params) - - @property - def api_spec(self): - if self._api_spec is None: - spec_path_url = self._get_api_spec_path() - response = self.send_request(url_path=spec_path_url, http_method=HTTPMethod.GET) - if response[ResponseParams.SUCCESS]: - self._api_spec = FdmSwaggerParser().parse_spec(response[ResponseParams.RESPONSE]) - else: - raise ConnectionError('Failed to download API specification. Status code: %s. Response: %s' % ( - response[ResponseParams.STATUS_CODE], response[ResponseParams.RESPONSE])) - return self._api_spec - - @property - def api_validator(self): - if self._api_validator is None: - self._api_validator = FdmSwaggerValidator(self.api_spec) - return self._api_validator - - -def construct_url_path(path, path_params=None, query_params=None): - url = path - if path_params: - url = url.format(**path_params) - if query_params: - url += "?" + urlencode(query_params) - return url - - -def extract_filename_from_headers(response_info): - content_header_regex = r'attachment; ?filename="?([^"]+)' - match = re.match(content_header_regex, response_info.get('Content-Disposition')) - if match: - return match.group(1) - else: - raise ValueError("No appropriate Content-Disposition header is specified.") diff --git a/plugins/lookup/avi.py b/plugins/lookup/avi.py deleted file mode 100644 index f19c036a28..0000000000 --- a/plugins/lookup/avi.py +++ /dev/null @@ -1,127 +0,0 @@ -# python 3 headers, required if submitting to Ansible -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' -lookup: avi -author: Sandeep Bandi -short_description: Look up ``Avi`` objects. -description: - - Given an object_type, fetch all the objects of that type or fetch - the specific object that matches the name/uuid given via options. - - For single object lookup. If you want the output to be a list, you may - want to pass option wantlist=True to the plugin. - -options: - obj_type: - description: - - type of object to query - required: True - obj_name: - description: - - name of the object to query - obj_uuid: - description: - - UUID of the object to query -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -# Lookup query for all the objects of a specific type. -- debug: msg="{{ lookup('avi', avi_credentials=avi_credentials, obj_type='virtualservice') }}" -# Lookup query for an object with the given name and type. -- debug: msg="{{ lookup('avi', avi_credentials=avi_credentials, obj_name='vs1', obj_type='virtualservice', wantlist=True) }}" -# Lookup query for an object with the given UUID and type. -- debug: msg="{{ lookup('avi', obj_uuid='virtualservice-5c0e183a-690a-45d8-8d6f-88c30a52550d', obj_type='virtualservice') }}" -# We can replace lookup with query function to always the get the output as list. -# This is helpful for looping. -- debug: msg="{{ query('avi', obj_uuid='virtualservice-5c0e183a-690a-45d8-8d6f-88c30a52550d', obj_type='virtualservice') }}" -""" - -RETURN = """ - _raw: - description: - - One ore more objects returned from ``Avi`` API. - type: list - elements: dictionary -""" - -from ansible.module_utils._text import to_native -from ansible.errors import AnsibleError, AnsibleParserError -from ansible.plugins.lookup import LookupBase -from ansible.utils.display import Display -from ansible_collections.community.general.plugins.module_utils.network.avi.avi_api import (ApiSession, - AviCredentials, - AviServerError, - ObjectNotFound, - APIError) - -display = Display() - - -def _api(avi_session, path, **kwargs): - ''' - Generic function to handle both // and / - API resource endpoints. - ''' - rsp = [] - try: - rsp_data = avi_session.get(path, **kwargs).json() - if 'results' in rsp_data: - rsp = rsp_data['results'] - else: - rsp.append(rsp_data) - except ObjectNotFound as e: - display.warning('Resource not found. Please check obj_name/' - 'obj_uuid/obj_type are spelled correctly.') - display.v(to_native(e)) - except (AviServerError, APIError) as e: - raise AnsibleError(to_native(e)) - except Exception as e: - # Generic excption handling for connection failures - raise AnsibleError('Unable to communicate with controller' - 'due to error: %s' % to_native(e)) - - return rsp - - -class LookupModule(LookupBase): - def run(self, terms, variables=None, avi_credentials=None, **kwargs): - - api_creds = AviCredentials(**avi_credentials) - # Create the session using avi_credentials - try: - avi = ApiSession(avi_credentials=api_creds) - except Exception as e: - raise AnsibleError(to_native(e)) - - # Return an empty list if the object is not found - rsp = [] - try: - path = kwargs.pop('obj_type') - except KeyError: - raise AnsibleError("Please pass the obj_type for lookup") - - if kwargs.get('obj_name', None): - name = kwargs.pop('obj_name') - try: - display.v("Fetching obj: %s of type: %s" % (name, path)) - rsp_data = avi.get_object_by_name(path, name, **kwargs) - if rsp_data: - # Append the return data only if it is not None. i.e object - # with specified name is present - rsp.append(rsp_data) - except AviServerError as e: - raise AnsibleError(to_native(e)) - elif kwargs.get('obj_uuid', None): - obj_uuid = kwargs.pop('obj_uuid') - obj_path = "%s/%s" % (path, obj_uuid) - display.v("Fetching obj: %s of type: %s" % (obj_uuid, path)) - rsp = _api(avi, obj_path, **kwargs) - else: - display.v("Fetching all objects of type: %s" % path) - rsp = _api(avi, path, **kwargs) - - return rsp diff --git a/plugins/module_utils/f5_utils.py b/plugins/module_utils/f5_utils.py deleted file mode 100644 index 17994f99cf..0000000000 --- a/plugins/module_utils/f5_utils.py +++ /dev/null @@ -1,383 +0,0 @@ -# -# Copyright 2016 F5 Networks 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 . - - -# Legacy - -try: - import bigsuds - bigsuds_found = True -except ImportError: - bigsuds_found = False - - -from ansible.module_utils.basic import env_fallback - - -def f5_argument_spec(): - return dict( - server=dict( - type='str', - required=True, - fallback=(env_fallback, ['F5_SERVER']) - ), - user=dict( - type='str', - required=True, - fallback=(env_fallback, ['F5_USER']) - ), - password=dict( - type='str', - aliases=['pass', 'pwd'], - required=True, - no_log=True, - fallback=(env_fallback, ['F5_PASSWORD']) - ), - validate_certs=dict( - default='yes', - type='bool', - fallback=(env_fallback, ['F5_VALIDATE_CERTS']) - ), - server_port=dict( - type='int', - default=443, - fallback=(env_fallback, ['F5_SERVER_PORT']) - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ), - partition=dict( - type='str', - default='Common', - fallback=(env_fallback, ['F5_PARTITION']) - ) - ) - - -def f5_parse_arguments(module): - if not bigsuds_found: - module.fail_json(msg="the python bigsuds module is required") - - if module.params['validate_certs']: - import ssl - if not hasattr(ssl, 'SSLContext'): - module.fail_json( - msg="bigsuds does not support verifying certificates with python < 2.7.9." - "Either update python or set validate_certs=False on the task'") - - return ( - module.params['server'], - module.params['user'], - module.params['password'], - module.params['state'], - module.params['partition'], - module.params['validate_certs'], - module.params['server_port'] - ) - - -def bigip_api(bigip, user, password, validate_certs, port=443): - try: - if bigsuds.__version__ >= '1.0.4': - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password, verify=validate_certs, port=port) - elif bigsuds.__version__ == '1.0.3': - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password, verify=validate_certs) - else: - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password) - except TypeError: - # bigsuds < 1.0.3, no verify param - if validate_certs: - # Note: verified we have SSLContext when we parsed params - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password) - else: - import ssl - if hasattr(ssl, 'SSLContext'): - # Really, you should never do this. It disables certificate - # verification *globally*. But since older bigip libraries - # don't give us a way to toggle verification we need to - # disable it at the global level. - # From https://www.python.org/dev/peps/pep-0476/#id29 - ssl._create_default_https_context = ssl._create_unverified_context - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password) - - return api - - -# Fully Qualified name (with the partition) -def fq_name(partition, name): - if name is not None and not name.startswith('/'): - return '/%s/%s' % (partition, name) - return name - - -# Fully Qualified name (with partition) for a list -def fq_list_names(partition, list_names): - if list_names is None: - return None - return map(lambda x: fq_name(partition, x), list_names) - - -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_then_replace'), rc=rc) - responses.append(to_text(out, errors='surrogate_then_replace')) - return responses - - -# New style - -from abc import ABCMeta, abstractproperty -from collections import defaultdict - -try: - from f5.bigip import ManagementRoot as BigIpMgmt - from f5.bigip.contexts import TransactionContextManager as BigIpTxContext - - from f5.bigiq import ManagementRoot as BigIqMgmt - - from f5.iworkflow import ManagementRoot as iWorkflowMgmt - from icontrol.exceptions import iControlUnexpectedHTTPError - HAS_F5SDK = True -except ImportError: - HAS_F5SDK = False - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems, with_metaclass -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import exec_command -from ansible.module_utils._text import to_text - - -F5_COMMON_ARGS = dict( - server=dict( - type='str', - required=True, - fallback=(env_fallback, ['F5_SERVER']) - ), - user=dict( - type='str', - required=True, - fallback=(env_fallback, ['F5_USER']) - ), - password=dict( - type='str', - aliases=['pass', 'pwd'], - required=True, - no_log=True, - fallback=(env_fallback, ['F5_PASSWORD']) - ), - validate_certs=dict( - default='yes', - type='bool', - fallback=(env_fallback, ['F5_VALIDATE_CERTS']) - ), - server_port=dict( - type='int', - default=443, - fallback=(env_fallback, ['F5_SERVER_PORT']) - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ), - partition=dict( - type='str', - default='Common', - fallback=(env_fallback, ['F5_PARTITION']) - ) -) - - -class AnsibleF5Client(object): - def __init__(self, argument_spec=None, supports_check_mode=False, - mutually_exclusive=None, required_together=None, - required_if=None, required_one_of=None, add_file_common_args=False, - f5_product_name='bigip', sans_state=False, sans_partition=False): - - self.f5_product_name = f5_product_name - - merged_arg_spec = dict() - merged_arg_spec.update(F5_COMMON_ARGS) - if argument_spec: - merged_arg_spec.update(argument_spec) - if sans_state: - del merged_arg_spec['state'] - if sans_partition: - del merged_arg_spec['partition'] - self.arg_spec = merged_arg_spec - - mutually_exclusive_params = [] - if mutually_exclusive: - mutually_exclusive_params += mutually_exclusive - - required_together_params = [] - if required_together: - required_together_params += required_together - - self.module = AnsibleModule( - argument_spec=merged_arg_spec, - supports_check_mode=supports_check_mode, - mutually_exclusive=mutually_exclusive_params, - required_together=required_together_params, - required_if=required_if, - required_one_of=required_one_of, - add_file_common_args=add_file_common_args - ) - - self.check_mode = self.module.check_mode - self._connect_params = self._get_connect_params() - - if 'transport' not in self.module.params or self.module.params['transport'] != 'cli': - try: - self.api = self._get_mgmt_root( - f5_product_name, **self._connect_params - ) - except iControlUnexpectedHTTPError as exc: - self.fail(str(exc)) - - def fail(self, msg): - self.module.fail_json(msg=msg) - - def _get_connect_params(self): - params = dict( - user=self.module.params['user'], - password=self.module.params['password'], - server=self.module.params['server'], - server_port=self.module.params['server_port'], - validate_certs=self.module.params['validate_certs'] - ) - return params - - def _get_mgmt_root(self, type, **kwargs): - if type == 'bigip': - return BigIpMgmt( - kwargs['server'], - kwargs['user'], - kwargs['password'], - port=kwargs['server_port'], - token='tmos' - ) - elif type == 'iworkflow': - return iWorkflowMgmt( - kwargs['server'], - kwargs['user'], - kwargs['password'], - port=kwargs['server_port'], - token='local' - ) - elif type == 'bigiq': - return BigIqMgmt( - kwargs['server'], - kwargs['user'], - kwargs['password'], - port=kwargs['server_port'], - auth_provider='local' - ) - - def reconnect(self): - """Attempts to reconnect to a device - - The existing token from a ManagementRoot can become invalid if you, - for example, upgrade the device (such as is done in the *_software - module. - - This method can be used to reconnect to a remote device without - having to re-instantiate the ArgumentSpec and AnsibleF5Client classes - it will use the same values that were initially provided to those - classes - - :return: - :raises iControlUnexpectedHTTPError - """ - self.api = self._get_mgmt_root( - self.f5_product_name, **self._connect_params - ) - - -class AnsibleF5Parameters(object): - def __init__(self, params=None): - self._values = defaultdict(lambda: None) - self._values['__warnings'] = [] - if params: - self.update(params=params) - - def update(self, params=None): - if params: - for k, v in iteritems(params): - if self.api_map is not None and k in self.api_map: - dict_to_use = self.api_map - map_key = self.api_map[k] - else: - dict_to_use = self._values - map_key = k - - # Handle weird API parameters like `dns.proxy.__iter__` by - # using a map provided by the module developer - class_attr = getattr(type(self), map_key, None) - if isinstance(class_attr, property): - # There is a mapped value for the api_map key - if class_attr.fset is None: - # If the mapped value does not have an associated setter - self._values[map_key] = v - else: - # The mapped value has a setter - setattr(self, map_key, v) - else: - # If the mapped value is not a @property - self._values[map_key] = v - - def __getattr__(self, item): - # Ensures that properties that weren't defined, and therefore stashed - # in the `_values` dict, will be retrievable. - return self._values[item] - - @property - def partition(self): - if self._values['partition'] is None: - return 'Common' - return self._values['partition'].strip('/') - - @partition.setter - def partition(self, value): - self._values['partition'] = value - - def _filter_params(self, params): - return dict((k, v) for k, v in iteritems(params) if v is not None) - - -class F5ModuleError(Exception): - pass diff --git a/plugins/module_utils/network/__init__.py b/plugins/module_utils/network/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/a10/__init__.py b/plugins/module_utils/network/a10/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/a10/a10.py b/plugins/module_utils/network/a10/a10.py deleted file mode 100644 index bf713702e4..0000000000 --- a/plugins/module_utils/network/a10/a10.py +++ /dev/null @@ -1,153 +0,0 @@ -# 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. -# -# Copyright (c), Michael DeHaan , 2012-2013 -# All rights reserved. -# -# 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. - -import json - -from ansible.module_utils.urls import fetch_url - - -AXAPI_PORT_PROTOCOLS = { - 'tcp': 2, - 'udp': 3, -} - -AXAPI_VPORT_PROTOCOLS = { - 'tcp': 2, - 'udp': 3, - 'fast-http': 9, - 'http': 11, - 'https': 12, -} - - -def a10_argument_spec(): - return dict( - host=dict(type='str', required=True), - username=dict(type='str', aliases=['user', 'admin'], required=True), - password=dict(type='str', aliases=['pass', 'pwd'], required=True, no_log=True), - write_config=dict(type='bool', default=False) - ) - - -def axapi_failure(result): - if 'response' in result and result['response'].get('status') == 'fail': - return True - return False - - -def axapi_call(module, url, post=None): - ''' - Returns a datastructure based on the result of the API call - ''' - rsp, info = fetch_url(module, url, data=post) - if not rsp or info['status'] >= 400: - module.fail_json(msg="failed to connect (status code %s), error was %s" % (info['status'], info.get('msg', 'no error given'))) - try: - raw_data = rsp.read() - data = json.loads(raw_data) - except ValueError: - # at least one API call (system.action.write_config) returns - # XML even when JSON is requested, so do some minimal handling - # here to prevent failing even when the call succeeded - if 'status="ok"' in raw_data.lower(): - data = {"response": {"status": "OK"}} - else: - data = {"response": {"status": "fail", "err": {"msg": raw_data}}} - except Exception: - module.fail_json(msg="could not read the result from the host") - finally: - rsp.close() - return data - - -def axapi_authenticate(module, base_url, username, password): - url = '%s&method=authenticate&username=%s&password=%s' % (base_url, username, password) - result = axapi_call(module, url) - if axapi_failure(result): - return module.fail_json(msg=result['response']['err']['msg']) - sessid = result['session_id'] - return base_url + '&session_id=' + sessid - - -def axapi_authenticate_v3(module, base_url, username, password): - url = base_url - auth_payload = {"credentials": {"username": username, "password": password}} - result = axapi_call_v3(module, url, method='POST', body=json.dumps(auth_payload)) - if axapi_failure(result): - return module.fail_json(msg=result['response']['err']['msg']) - signature = result['authresponse']['signature'] - return signature - - -def axapi_call_v3(module, url, method=None, body=None, signature=None): - ''' - Returns a datastructure based on the result of the API call - ''' - if signature: - headers = {'content-type': 'application/json', 'Authorization': 'A10 %s' % signature} - else: - headers = {'content-type': 'application/json'} - rsp, info = fetch_url(module, url, method=method, data=body, headers=headers) - if not rsp or info['status'] >= 400: - module.fail_json(msg="failed to connect (status code %s), error was %s" % (info['status'], info.get('msg', 'no error given'))) - try: - raw_data = rsp.read() - data = json.loads(raw_data) - except ValueError: - # at least one API call (system.action.write_config) returns - # XML even when JSON is requested, so do some minimal handling - # here to prevent failing even when the call succeeded - if 'status="ok"' in raw_data.lower(): - data = {"response": {"status": "OK"}} - else: - data = {"response": {"status": "fail", "err": {"msg": raw_data}}} - except Exception: - module.fail_json(msg="could not read the result from the host") - finally: - rsp.close() - return data - - -def axapi_enabled_disabled(flag): - ''' - The axapi uses 0/1 integer values for flags, rather than strings - or booleans, so convert the given flag to a 0 or 1. For now, params - are specified as strings only so thats what we check. - ''' - if flag == 'enabled': - return 1 - else: - return 0 - - -def axapi_get_port_protocol(protocol): - return AXAPI_PORT_PROTOCOLS.get(protocol.lower(), None) - - -def axapi_get_vport_protocol(protocol): - return AXAPI_VPORT_PROTOCOLS.get(protocol.lower(), None) diff --git a/plugins/module_utils/network/aci/__init__.py b/plugins/module_utils/network/aci/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/aireos/__init__.py b/plugins/module_utils/network/aireos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/aireos/aireos.py b/plugins/module_utils/network/aireos/aireos.py deleted file mode 100644 index e5db446ad9..0000000000 --- a/plugins/module_utils/network/aireos/aireos.py +++ /dev/null @@ -1,129 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2016 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import exec_command - -_DEVICE_CONFIGS = {} - -aireos_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'), - 'timeout': dict(type='int'), -} -aireos_argument_spec = { - 'provider': dict(type='dict', options=aireos_provider_spec) -} - -aireos_top_spec = { - 'host': dict(removed_in_version=2.9), - 'port': dict(removed_in_version=2.9, type='int'), - 'username': dict(removed_in_version=2.9), - 'password': dict(removed_in_version=2.9, no_log=True), - 'ssh_keyfile': dict(removed_in_version=2.9, type='path'), - 'timeout': dict(removed_in_version=2.9, type='int'), -} -aireos_argument_spec.update(aireos_top_spec) - - -def sanitize(resp): - # Takes response from device and strips whitespace from all lines - # Aireos adds in extra preceding whitespace which netcfg parses as children/parents, which Aireos does not do - # Aireos also adds in trailing whitespace that is unused - cleaned = [] - for line in resp.splitlines(): - cleaned.append(line.strip()) - return '\n'.join(cleaned).strip() - - -def get_provider_argspec(): - return aireos_provider_spec - - -def check_args(module, warnings): - pass - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - cmd = 'show run-config commands ' - 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_then_replace')) - cfg = sanitize(to_text(out, errors='surrogate_then_replace').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_then_replace'), rc=rc) - responses.append(sanitize(to_text(out, errors='surrogate_then_replace'))) - return responses - - -def load_config(module, commands): - - rc, out, err = exec_command(module, 'config') - if rc != 0: - module.fail_json(msg='unable to enter configuration mode', err=to_text(out, errors='surrogate_then_replace')) - - 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_then_replace'), command=command, rc=rc) - - exec_command(module, 'end') diff --git a/plugins/module_utils/network/aos/__init__.py b/plugins/module_utils/network/aos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/aos/aos.py b/plugins/module_utils/network/aos/aos.py deleted file mode 100644 index 092bbf5b4a..0000000000 --- a/plugins/module_utils/network/aos/aos.py +++ /dev/null @@ -1,180 +0,0 @@ -# -# Copyright (c) 2017 Apstra 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. -# - -""" -This module adds shared support for Apstra AOS modules - -In order to use this module, include it as part of your module - -from ansible.module_utils.network.aos.aos import (check_aos_version, get_aos_session, find_collection_item, - content_to_dict, do_load_resource) - -""" -import json - -from distutils.version import LooseVersion - -try: - import yaml - HAS_YAML = True -except ImportError: - HAS_YAML = False - -try: - from apstra.aosom.session import Session - - HAS_AOS_PYEZ = True -except ImportError: - HAS_AOS_PYEZ = False - -from ansible.module_utils._text import to_native - - -def check_aos_version(module, min=False): - """ - Check if the library aos-pyez is present. - If provided, also check if the minimum version requirement is met - """ - if not HAS_AOS_PYEZ: - module.fail_json(msg='aos-pyez is not installed. Please see details ' - 'here: https://github.com/Apstra/aos-pyez') - - elif min: - import apstra.aosom - AOS_PYEZ_VERSION = apstra.aosom.__version__ - - if LooseVersion(AOS_PYEZ_VERSION) < LooseVersion(min): - module.fail_json(msg='aos-pyez >= %s is required for this module' % min) - - return True - - -def get_aos_session(module, auth): - """ - Resume an existing session and return an AOS object. - - Args: - auth (dict): An AOS session as obtained by aos_login module blocks:: - - dict( token=, - server=, - port= - ) - - Return: - Aos object - """ - - check_aos_version(module) - - aos = Session() - aos.session = auth - - return aos - - -def find_collection_item(collection, item_name=False, item_id=False): - """ - Find collection_item based on name or id from a collection object - Both Collection_item and Collection Objects are provided by aos-pyez library - - Return - collection_item: object corresponding to the collection type - """ - my_dict = None - - if item_name: - my_dict = collection.find(label=item_name) - elif item_id: - my_dict = collection.find(uid=item_id) - - if my_dict is None: - return collection[''] - else: - return my_dict - - -def content_to_dict(module, content): - """ - Convert 'content' into a Python Dict based on 'content_format' - """ - - # if not HAS_YAML: - # module.fail_json(msg="Python Library Yaml is not present, mandatory to use 'content'") - - content_dict = None - - # try: - # content_dict = json.loads(content.replace("\'", '"')) - # except: - # module.fail_json(msg="Unable to convert 'content' from JSON, please check if valid") - # - # elif format in ['yaml', 'var']: - - try: - content_dict = yaml.safe_load(content) - - if not isinstance(content_dict, dict): - raise Exception() - - # Check if dict is empty and return an error if it's - if not content_dict: - raise Exception() - - except Exception: - module.fail_json(msg="Unable to convert 'content' to a dict, please check if valid") - - # replace the string with the dict - module.params['content'] = content_dict - - return content_dict - - -def do_load_resource(module, collection, name): - """ - Create a new object (collection.item) by loading a datastructure directly - """ - - try: - item = find_collection_item(collection, name, '') - except Exception: - module.fail_json(msg="An error occurred while running 'find_collection_item'") - - if item.exists: - module.exit_json(changed=False, name=item.name, id=item.id, value=item.value) - - # If not in check mode, apply the changes - if not module.check_mode: - try: - item.datum = module.params['content'] - item.write() - except Exception as e: - module.fail_json(msg="Unable to write item content : %r" % to_native(e)) - - module.exit_json(changed=True, name=item.name, id=item.id, value=item.value) diff --git a/plugins/module_utils/network/apconos/__init__.py b/plugins/module_utils/network/apconos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/apconos/apconos.py b/plugins/module_utils/network/apconos/apconos.py deleted file mode 100644 index 1b9eebcda8..0000000000 --- a/plugins/module_utils/network/apconos/apconos.py +++ /dev/null @@ -1,113 +0,0 @@ -# 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. -# -# Copyright (C) 2019 APCON, Inc. -# All rights reserved. -# -# 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. -# -# Contains utility methods -# APCON Networking - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import EntityCollection -from ansible.module_utils.connection import Connection, exec_command -from ansible.module_utils.connection import ConnectionError - -_DEVICE_CONFIGS = {} -_CONNECTION = None - - -command_spec = { - 'command': dict(key=True), -} - - -def check_args(module, warnings): - pass - - -def get_connection(module): - global _CONNECTION - if _CONNECTION: - return _CONNECTION - _CONNECTION = Connection(module._socket_path) - - return _CONNECTION - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - cmd = ' '.join(flags).strip() - - try: - return _DEVICE_CONFIGS[cmd] - except KeyError: - conn = get_connection(module) - out = conn.get(cmd) - cfg = to_text(out, errors='surrogate_then_replace').strip() - _DEVICE_CONFIGS[cmd] = cfg - return cfg - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - transform = EntityCollection(module, command_spec) - commands = transform(commands) - - responses = list() - - for cmd in commands: - out = connection.get(**cmd) - responses.append(to_text(out, errors='surrogate_then_replace')) - - return responses - - -def load_config(module, config): - try: - conn = get_connection(module) - conn.edit_config(config) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def get_defaults_flag(module): - rc, out, err = exec_command(module, 'display running-config ?') - out = to_text(out, errors='surrogate_then_replace') - - commands = set() - for line in out.splitlines(): - if line: - commands.add(line.strip().split()[0]) - - if 'all' in commands: - return 'all' - else: - return 'full' diff --git a/plugins/module_utils/network/aruba/__init__.py b/plugins/module_utils/network/aruba/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/aruba/aruba.py b/plugins/module_utils/network/aruba/aruba.py deleted file mode 100644 index 0499a44315..0000000000 --- a/plugins/module_utils/network/aruba/aruba.py +++ /dev/null @@ -1,131 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2016 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -import re - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import exec_command - -_DEVICE_CONFIGS = {} - -aruba_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'), - 'timeout': dict(type='int'), -} -aruba_argument_spec = { - 'provider': dict(type='dict', options=aruba_provider_spec) -} - -aruba_top_spec = { - 'host': dict(removed_in_version=2.9), - 'port': dict(removed_in_version=2.9, type='int'), - 'username': dict(removed_in_version=2.9), - 'password': dict(removed_in_version=2.9, no_log=True), - 'ssh_keyfile': dict(removed_in_version=2.9, type='path'), - 'timeout': dict(removed_in_version=2.9, type='int'), -} - -aruba_argument_spec.update(aruba_top_spec) - - -def get_provider_argspec(): - return aruba_provider_spec - - -def check_args(module, warnings): - pass - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - cmd = 'show running-config ' - 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_then_replace')) - cfg = sanitize(to_text(out, errors='surrogate_then_replace').strip()) - _DEVICE_CONFIGS[cmd] = cfg - return cfg - - -def sanitize(resp): - # Takes response from device and adjusts leading whitespace to just 1 space - cleaned = [] - for line in resp.splitlines(): - cleaned.append(re.sub(r"^\s+", " ", line)) - return '\n'.join(cleaned).strip() - - -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_then_replace'), rc=rc) - responses.append(to_text(out, errors='surrogate_then_replace')) - 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(out, errors='surrogate_then_replace')) - - 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_then_replace'), command=command, rc=rc) - - exec_command(module, 'end') diff --git a/plugins/module_utils/network/avi/__init__.py b/plugins/module_utils/network/avi/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/avi/ansible_utils.py b/plugins/module_utils/network/avi/ansible_utils.py deleted file mode 100644 index 2dea53319a..0000000000 --- a/plugins/module_utils/network/avi/ansible_utils.py +++ /dev/null @@ -1,572 +0,0 @@ -from __future__ import absolute_import - -""" -Created on Aug 16, 2016 - -@author: Gaurav Rastogi (grastogi@avinetworks.com) -""" -import os -import re -import logging -import sys -from copy import deepcopy -from ansible.module_utils.basic import env_fallback - -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi_api import ( - ApiSession, ObjectNotFound, avi_sdk_syslog_logger, AviCredentials, HAS_AVI) -except ImportError: - HAS_AVI = False - - -if os.environ.get('AVI_LOG_HANDLER', '') != 'syslog': - log = logging.getLogger(__name__) -else: - # Ansible does not allow logging from the modules. - log = avi_sdk_syslog_logger() - - -def _check_type_string(x): - """ - :param x: - :return: True if it is of type string - """ - if isinstance(x, str): - return True - if sys.version_info[0] < 3: - try: - return isinstance(x, unicode) - except NameError: - return False - - -class AviCheckModeResponse(object): - """ - Class to support ansible check mode. - """ - - def __init__(self, obj, status_code=200): - self.obj = obj - self.status_code = status_code - - def json(self): - return self.obj - - -def ansible_return(module, rsp, changed, req=None, existing_obj=None, - api_context=None): - """ - :param module: AnsibleModule - :param rsp: ApiResponse from avi_api - :param changed: boolean - :param req: ApiRequest to avi_api - :param existing_obj: object to be passed debug output - :param api_context: api login context - - helper function to return the right ansible based on the error code and - changed - Returns: specific ansible module exit function - """ - - if rsp is not None and rsp.status_code > 299: - return module.fail_json( - msg='Error %d Msg %s req: %s api_context:%s ' % ( - rsp.status_code, rsp.text, req, api_context)) - api_creds = AviCredentials() - api_creds.update_from_ansible_module(module) - key = '%s:%s:%s' % (api_creds.controller, api_creds.username, - api_creds.port) - disable_fact = module.params.get('avi_disable_session_cache_as_fact') - - fact_context = None - if not disable_fact: - fact_context = module.params.get('api_context', {}) - if fact_context: - fact_context.update({key: api_context}) - else: - fact_context = {key: api_context} - - obj_val = rsp.json() if rsp else existing_obj - - if (obj_val and module.params.get("obj_username", None) and - "username" in obj_val): - obj_val["obj_username"] = obj_val["username"] - if (obj_val and module.params.get("obj_password", None) and - "password" in obj_val): - obj_val["obj_password"] = obj_val["password"] - old_obj_val = existing_obj if changed and existing_obj else None - api_context_val = api_context if disable_fact else None - ansible_facts_val = dict( - avi_api_context=fact_context) if not disable_fact else {} - - return module.exit_json( - changed=changed, obj=obj_val, old_obj=old_obj_val, - ansible_facts=ansible_facts_val, api_context=api_context_val) - - -def purge_optional_fields(obj, module): - """ - It purges the optional arguments to be sent to the controller. - :param obj: dictionary of the ansible object passed as argument. - :param module: AnsibleModule - return modified obj - """ - purge_fields = [] - for param, spec in module.argument_spec.items(): - if not spec.get('required', False): - if param not in obj: - # these are ansible common items - continue - if obj[param] is None: - purge_fields.append(param) - log.debug('purging fields %s', purge_fields) - for param in purge_fields: - obj.pop(param, None) - return obj - - -def cleanup_absent_fields(obj): - """ - cleans up any field that is marked as state: absent. It needs to be removed - from the object if it is present. - :param obj: - :return: Purged object - """ - if type(obj) != dict: - return obj - cleanup_keys = [] - for k, v in obj.items(): - if type(v) == dict: - if (('state' in v and v['state'] == 'absent') or - (v == "{'state': 'absent'}")): - cleanup_keys.append(k) - else: - cleanup_absent_fields(v) - if not v: - cleanup_keys.append(k) - elif type(v) == list: - new_list = [] - for elem in v: - elem = cleanup_absent_fields(elem) - if elem: - # remove the item from list - new_list.append(elem) - if new_list: - obj[k] = new_list - else: - cleanup_keys.append(k) - elif isinstance(v, str) or isinstance(v, str): - if v == "{'state': 'absent'}": - cleanup_keys.append(k) - for k in cleanup_keys: - del obj[k] - return obj - - -RE_REF_MATCH = re.compile(r'^/api/[\w/]+\?name\=[\w]+[^#<>]*$') -# if HTTP ref match then strip out the #name -HTTP_REF_MATCH = re.compile(r'https://[\w.0-9:-]+/api/.+') -HTTP_REF_W_NAME_MATCH = re.compile(r'https://[\w.0-9:-]+/api/.*#.+') - - -def ref_n_str_cmp(x, y): - """ - compares two references - 1. check for exact reference - 2. check for obj_type/uuid - 3. check for name - - if x is ref=name then extract uuid and name from y and use it. - if x is http_ref then - strip x and y - compare them. - - if x and y are urls then match with split on # - if x is a RE_REF_MATCH then extract name - if y is a REF_MATCH then extract name - :param x: first string - :param y: second string from controller's object - - Returns - True if they are equivalent else False - """ - if type(y) in (int, float, bool, int, complex): - y = str(y) - x = str(x) - if not (_check_type_string(x) and _check_type_string(y)): - return False - y_uuid = y_name = str(y) - x = str(x) - if RE_REF_MATCH.match(x): - x = x.split('name=')[1] - elif HTTP_REF_MATCH.match(x): - x = x.rsplit('#', 1)[0] - y = y.rsplit('#', 1)[0] - elif RE_REF_MATCH.match(y): - y = y.split('name=')[1] - - if HTTP_REF_W_NAME_MATCH.match(y): - path = y.split('api/', 1)[1] - # Fetching name or uuid from path /xxxx_xx/xx/xx_x/uuid_or_name - uuid_or_name = path.split('/')[-1] - parts = uuid_or_name.rsplit('#', 1) - y_uuid = parts[0] - y_name = parts[1] if len(parts) > 1 else '' - # is just string but y is a url so match either uuid or name - result = (x in (y, y_name, y_uuid)) - if not result: - log.debug('x: %s y: %s y_name %s y_uuid %s', - x, y, y_name, y_uuid) - return result - - -def avi_obj_cmp(x, y, sensitive_fields=None): - """ - compares whether x is fully contained in y. The comparision is different - from a simple dictionary compare for following reasons - 1. Some fields could be references. The object in controller returns the - full URL for those references. However, the ansible script would have - it specified as /api/pool?name=blah. So, the reference fields need - to match uuid, relative reference based on name and actual reference. - - 2. Optional fields with defaults: In case there are optional fields with - defaults then controller automatically fills it up. This would - cause the comparison with Ansible object specification to always return - changed. - - 3. Optional fields without defaults: This is most tricky. The issue is - how to specify deletion of such objects from ansible script. If the - ansible playbook has object specified as Null then Avi controller will - reject for non Message(dict) type fields. In addition, to deal with the - defaults=null issue all the fields that are set with None are purged - out before comparing with Avi controller's version - - So, the solution is to pass state: absent if any optional field needs - to be deleted from the configuration. The script would return changed - =true if it finds a key in the controller version and it is marked with - state: absent in ansible playbook. Alternatively, it would return - false if key is not present in the controller object. Before, doing - put or post it would purge the fields that are marked state: absent. - - :param x: first string - :param y: second string from controller's object - :param sensitive_fields: sensitive fields to ignore for diff - - Returns: - True if x is subset of y else False - """ - if not sensitive_fields: - sensitive_fields = set() - if isinstance(x, str) or isinstance(x, str): - # Special handling for strings as they can be references. - return ref_n_str_cmp(x, y) - if type(x) not in [list, dict]: - # if it is not list or dict or string then simply compare the values - return x == y - if type(x) == list: - # should compare each item in the list and that should match - if len(x) != len(y): - log.debug('x has %d items y has %d', len(x), len(y)) - return False - for i in zip(x, y): - if not avi_obj_cmp(i[0], i[1], sensitive_fields=sensitive_fields): - # no need to continue - return False - - if type(x) == dict: - x.pop('_last_modified', None) - x.pop('tenant', None) - y.pop('_last_modified', None) - x.pop('api_version', None) - y.pop('api_verison', None) - d_xks = [k for k in x.keys() if k in sensitive_fields] - - if d_xks: - # if there is sensitive field then always return changed - return False - # pop the keys that are marked deleted but not present in y - # return false if item is marked absent and is present in y - d_x_absent_ks = [] - for k, v in x.items(): - if v is None: - d_x_absent_ks.append(k) - continue - if isinstance(v, dict): - if ('state' in v) and (v['state'] == 'absent'): - if type(y) == dict and k not in y: - d_x_absent_ks.append(k) - else: - return False - elif not v: - d_x_absent_ks.append(k) - elif isinstance(v, list) and not v: - d_x_absent_ks.append(k) - # Added condition to check key in dict. - elif isinstance(v, str) or (k in y and isinstance(y[k], str)): - # this is the case when ansible converts the dictionary into a - # string. - if v == "{'state': 'absent'}" and k not in y: - d_x_absent_ks.append(k) - elif not v and k not in y: - # this is the case when x has set the value that qualifies - # as not but y does not have that value - d_x_absent_ks.append(k) - for k in d_x_absent_ks: - x.pop(k) - x_keys = set(x.keys()) - y_keys = set(y.keys()) - if not x_keys.issubset(y_keys): - # log.debug('x has %s and y has %s keys', len(x_keys), len(y_keys)) - return False - for k, v in x.items(): - if k not in y: - # log.debug('k %s is not in y %s', k, y) - return False - if not avi_obj_cmp(v, y[k], sensitive_fields=sensitive_fields): - # log.debug('k %s v %s did not match in y %s', k, v, y[k]) - return False - return True - - -POP_FIELDS = ['state', 'controller', 'username', 'password', 'api_version', - 'avi_credentials', 'avi_api_update_method', 'avi_api_patch_op', - 'api_context', 'tenant', 'tenant_uuid', 'avi_disable_session_cache_as_fact'] - - -def get_api_context(module, api_creds): - api_context = module.params.get('api_context') - if api_context and module.params.get('avi_disable_session_cache_as_fact'): - return api_context - elif api_context and not module.params.get( - 'avi_disable_session_cache_as_fact'): - key = '%s:%s:%s' % (api_creds.controller, api_creds.username, - api_creds.port) - return api_context.get(key) - else: - return None - - -def avi_ansible_api(module, obj_type, sensitive_fields): - """ - This converts the Ansible module into AVI object and invokes APIs - :param module: Ansible module - :param obj_type: string representing Avi object type - :param sensitive_fields: sensitive fields to be excluded for comparison - purposes. - Returns: - success: module.exit_json with obj=avi object - faliure: module.fail_json - """ - - api_creds = AviCredentials() - api_creds.update_from_ansible_module(module) - api_context = get_api_context(module, api_creds) - if api_context: - api = ApiSession.get_session( - api_creds.controller, - api_creds.username, - password=api_creds.password, - timeout=api_creds.timeout, - tenant=api_creds.tenant, - tenant_uuid=api_creds.tenant_uuid, - token=api_context['csrftoken'], - port=api_creds.port, - session_id=api_context['session_id'], - csrftoken=api_context['csrftoken']) - else: - api = ApiSession.get_session( - api_creds.controller, - api_creds.username, - password=api_creds.password, - timeout=api_creds.timeout, - tenant=api_creds.tenant, - tenant_uuid=api_creds.tenant_uuid, - token=api_creds.token, - port=api_creds.port) - state = module.params['state'] - # Get the api version. - avi_update_method = module.params.get('avi_api_update_method', 'put') - avi_patch_op = module.params.get('avi_api_patch_op', 'add') - - api_version = api_creds.api_version - name = module.params.get('name', None) - # Added Support to get uuid - uuid = module.params.get('uuid', None) - check_mode = module.check_mode - if uuid and obj_type != 'cluster': - obj_path = '%s/%s' % (obj_type, uuid) - else: - obj_path = '%s/' % obj_type - obj = deepcopy(module.params) - tenant = obj.pop('tenant', '') - tenant_uuid = obj.pop('tenant_uuid', '') - # obj.pop('cloud_ref', None) - for k in POP_FIELDS: - obj.pop(k, None) - purge_optional_fields(obj, module) - - # Special code to handle situation where object has a field - # named username. This is used in case of api/user - # The following code copies the username and password - # from the obj_username and obj_password fields. - if 'obj_username' in obj: - obj['username'] = obj['obj_username'] - obj.pop('obj_username') - if 'obj_password' in obj: - obj['password'] = obj['obj_password'] - obj.pop('obj_password') - if 'full_name' not in obj and 'name' in obj and obj_type == "user": - obj['full_name'] = obj['name'] - # Special case as name represent full_name in user module - # As per API response, name is always same as username regardless of full_name - obj['name'] = obj['username'] - - log.info('passed object %s ', obj) - - if uuid: - # Get the object based on uuid. - try: - existing_obj = api.get( - obj_path, tenant=tenant, tenant_uuid=tenant_uuid, - params={'include_refs': '', 'include_name': ''}, - api_version=api_version) - existing_obj = existing_obj.json() - except ObjectNotFound: - existing_obj = None - elif name: - params = {'include_refs': '', 'include_name': ''} - if obj.get('cloud_ref', None): - # this is the case when gets have to be scoped with cloud - cloud = obj['cloud_ref'].split('name=')[1] - params['cloud_ref.name'] = cloud - existing_obj = api.get_object_by_name( - obj_type, name, tenant=tenant, tenant_uuid=tenant_uuid, - params=params, api_version=api_version) - - # Need to check if tenant_ref was provided and the object returned - # is actually in admin tenant. - if existing_obj and 'tenant_ref' in obj and 'tenant_ref' in existing_obj: - # https://10.10.25.42/api/tenant/admin#admin - existing_obj_tenant = existing_obj['tenant_ref'].split('#')[1] - obj_tenant = obj['tenant_ref'].split('name=')[1] - if obj_tenant != existing_obj_tenant: - existing_obj = None - else: - # added api version to avi api call. - existing_obj = api.get(obj_path, tenant=tenant, tenant_uuid=tenant_uuid, - params={'include_refs': '', 'include_name': ''}, - api_version=api_version).json() - - if state == 'absent': - rsp = None - changed = False - err = False - if not check_mode and existing_obj: - try: - if name is not None: - # added api version to avi api call. - rsp = api.delete_by_name( - obj_type, name, tenant=tenant, tenant_uuid=tenant_uuid, - api_version=api_version) - else: - # added api version to avi api call. - rsp = api.delete( - obj_path, tenant=tenant, tenant_uuid=tenant_uuid, - api_version=api_version) - except ObjectNotFound: - pass - if check_mode and existing_obj: - changed = True - - if rsp: - if rsp.status_code == 204: - changed = True - else: - err = True - if not err: - return ansible_return( - module, rsp, changed, existing_obj=existing_obj, - api_context=api.get_context()) - elif rsp: - return module.fail_json(msg=rsp.text) - - rsp = None - req = None - if existing_obj: - # this is case of modify as object exists. should find out - # if changed is true or not - if name is not None and obj_type != 'cluster': - obj_uuid = existing_obj['uuid'] - obj_path = '%s/%s' % (obj_type, obj_uuid) - if avi_update_method == 'put': - changed = not avi_obj_cmp(obj, existing_obj, sensitive_fields) - obj = cleanup_absent_fields(obj) - if changed: - req = obj - if check_mode: - # No need to process any further. - rsp = AviCheckModeResponse(obj=existing_obj) - else: - rsp = api.put( - obj_path, data=req, tenant=tenant, - tenant_uuid=tenant_uuid, api_version=api_version) - elif check_mode: - rsp = AviCheckModeResponse(obj=existing_obj) - else: - if check_mode: - # No need to process any further. - rsp = AviCheckModeResponse(obj=existing_obj) - changed = True - else: - obj.pop('name', None) - patch_data = {avi_patch_op: obj} - rsp = api.patch( - obj_path, data=patch_data, tenant=tenant, - tenant_uuid=tenant_uuid, api_version=api_version) - obj = rsp.json() - changed = not avi_obj_cmp(obj, existing_obj) - if changed: - log.debug('EXISTING OBJ %s', existing_obj) - log.debug('NEW OBJ %s', obj) - else: - changed = True - req = obj - if check_mode: - rsp = AviCheckModeResponse(obj=None) - else: - rsp = api.post(obj_type, data=obj, tenant=tenant, - tenant_uuid=tenant_uuid, api_version=api_version) - return ansible_return(module, rsp, changed, req, existing_obj=existing_obj, - api_context=api.get_context()) - - -def avi_common_argument_spec(): - """ - Returns common arguments for all Avi modules - :return: dict - """ - credentials_spec = dict( - controller=dict(fallback=(env_fallback, ['AVI_CONTROLLER'])), - username=dict(fallback=(env_fallback, ['AVI_USERNAME'])), - password=dict(fallback=(env_fallback, ['AVI_PASSWORD']), no_log=True), - api_version=dict(default='16.4.4', type='str'), - tenant=dict(default='admin'), - tenant_uuid=dict(default='', type='str'), - port=dict(type='int'), - timeout=dict(default=300, type='int'), - token=dict(default='', type='str', no_log=True), - session_id=dict(default='', type='str', no_log=True), - csrftoken=dict(default='', type='str', no_log=True) - ) - - return dict( - controller=dict(fallback=(env_fallback, ['AVI_CONTROLLER'])), - username=dict(fallback=(env_fallback, ['AVI_USERNAME'])), - password=dict(fallback=(env_fallback, ['AVI_PASSWORD']), no_log=True), - tenant=dict(default='admin'), - tenant_uuid=dict(default=''), - api_version=dict(default='16.4.4', type='str'), - avi_credentials=dict(default=None, type='dict', - options=credentials_spec), - api_context=dict(type='dict'), - avi_disable_session_cache_as_fact=dict(default=False, type='bool')) diff --git a/plugins/module_utils/network/avi/avi.py b/plugins/module_utils/network/avi/avi.py deleted file mode 100644 index 04cb4157f4..0000000000 --- a/plugins/module_utils/network/avi/avi.py +++ /dev/null @@ -1,38 +0,0 @@ -# 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. -# -# Copyright (c), Gaurav Rastogi , 2017 -# All rights reserved. -# -# 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. - -# This module initially matched the namespace of network module avi. However, -# that causes namespace import error when other modules from avi namespaces -# are imported. Added import of absolute_import to avoid import collisions for -# avi.sdk. - -from __future__ import absolute_import - -from ansible_collections.community.general.plugins.module_utils.network.avi.ansible_utils import ( - avi_ansible_api, avi_common_argument_spec, ansible_return, - avi_obj_cmp, cleanup_absent_fields, AviCheckModeResponse, HAS_AVI) diff --git a/plugins/module_utils/network/avi/avi_api.py b/plugins/module_utils/network/avi/avi_api.py deleted file mode 100644 index 817f909dd0..0000000000 --- a/plugins/module_utils/network/avi/avi_api.py +++ /dev/null @@ -1,972 +0,0 @@ -from __future__ import absolute_import -import os -import sys -import copy -import json -import logging -import time -from datetime import datetime, timedelta -from ssl import SSLError - - -class MockResponse(object): - def __init__(self, *args, **kwargs): - raise Exception("Requests library Response object not found. Using fake one.") - - -class MockRequestsConnectionError(Exception): - pass - - -class MockSession(object): - def __init__(self, *args, **kwargs): - raise Exception("Requests library Session object not found. Using fake one.") - - -HAS_AVI = True -try: - from requests import ConnectionError as RequestsConnectionError - from requests import Response - from requests.sessions import Session -except ImportError: - HAS_AVI = False - Response = MockResponse - RequestsConnectionError = MockRequestsConnectionError - Session = MockSession - - -logger = logging.getLogger(__name__) - -sessionDict = {} - - -def avi_timedelta(td): - ''' - This is a wrapper class to workaround python 2.6 builtin datetime.timedelta - does not have total_seconds method - :param timedelta object - ''' - if type(td) != timedelta: - raise TypeError() - if sys.version_info >= (2, 7): - ts = td.total_seconds() - else: - ts = td.seconds + (24 * 3600 * td.days) - return ts - - -def avi_sdk_syslog_logger(logger_name='avi.sdk'): - # The following sets up syslog module to log underlying avi SDK messages - # based on the environment variables: - # AVI_LOG_HANDLER: names the logging handler to use. Only syslog is - # supported. - # AVI_LOG_LEVEL: Logging level used for the avi SDK. Default is DEBUG - # AVI_SYSLOG_ADDRESS: Destination address for the syslog handler. - # Default is /dev/log - from logging.handlers import SysLogHandler - lf = '[%(asctime)s] %(levelname)s [%(module)s.%(funcName)s:%(lineno)d] %(message)s' - log = logging.getLogger(logger_name) - log_level = os.environ.get('AVI_LOG_LEVEL', 'DEBUG') - if log_level: - log.setLevel(getattr(logging, log_level)) - formatter = logging.Formatter(lf) - sh = SysLogHandler(address=os.environ.get('AVI_SYSLOG_ADDRESS', '/dev/log')) - sh.setFormatter(formatter) - log.addHandler(sh) - return log - - -class ObjectNotFound(Exception): - pass - - -class APIError(Exception): - def __init__(self, arg, rsp=None): - self.args = [arg, rsp] - self.rsp = rsp - - -class AviServerError(APIError): - def __init__(self, arg, rsp=None): - super(AviServerError, self).__init__(arg, rsp) - - -class APINotImplemented(Exception): - pass - - -class ApiResponse(Response): - """ - Returns copy of the requests.Response object provides additional helper - routines - 1. obj: returns dictionary of Avi Object - """ - def __init__(self, rsp): - super(ApiResponse, self).__init__() - for k, v in list(rsp.__dict__.items()): - setattr(self, k, v) - - def json(self): - """ - Extends the session default json interface to handle special errors - and raise Exceptions - returns the Avi object as a dictionary from rsp.text - """ - if self.status_code in (200, 201): - if not self.text: - # In cases like status_code == 201 the response text could be - # empty string. - return None - return super(ApiResponse, self).json() - elif self.status_code == 204: - # No response needed; e.g., delete operation - return None - elif self.status_code == 404: - raise ObjectNotFound('HTTP Error: %s Error Msg %s' % ( - self.status_code, self.text), self) - elif self.status_code >= 500: - raise AviServerError('HTTP Error: %s Error Msg %s' % ( - self.status_code, self.text), self) - else: - raise APIError('HTTP Error: %s Error Msg %s' % ( - self.status_code, self.text), self) - - def count(self): - """ - return the number of objects in the collection response. If it is not - a collection response then it would simply return 1. - """ - obj = self.json() - if 'count' in obj: - # this was a resposne to collection - return obj['count'] - return 1 - - @staticmethod - def to_avi_response(resp): - if type(resp) == Response: - return ApiResponse(resp) - return resp - - -class AviCredentials(object): - controller = '' - username = '' - password = '' - api_version = '16.4.4' - tenant = None - tenant_uuid = None - token = None - port = None - timeout = 300 - session_id = None - csrftoken = None - - def __init__(self, **kwargs): - for k, v in kwargs.items(): - setattr(self, k, v) - - def update_from_ansible_module(self, m): - """ - :param m: ansible module - :return: - """ - if m.params.get('avi_credentials'): - for k, v in m.params['avi_credentials'].items(): - if hasattr(self, k): - setattr(self, k, v) - if m.params['controller']: - self.controller = m.params['controller'] - if m.params['username']: - self.username = m.params['username'] - if m.params['password']: - self.password = m.params['password'] - if (m.params['api_version'] and - (m.params['api_version'] != '16.4.4')): - self.api_version = m.params['api_version'] - if m.params['tenant']: - self.tenant = m.params['tenant'] - if m.params['tenant_uuid']: - self.tenant_uuid = m.params['tenant_uuid'] - if m.params.get('session_id'): - self.session_id = m.params['session_id'] - if m.params.get('csrftoken'): - self.csrftoken = m.params['csrftoken'] - - def __str__(self): - return 'controller %s user %s api %s tenant %s' % ( - self.controller, self.username, self.api_version, self.tenant) - - -class ApiSession(Session): - """ - Extends the Request library's session object to provide helper - utilities to work with Avi Controller like authentication, api massaging - etc. - """ - - # This keeps track of the process which created the cache. - # At anytime the pid of the process changes then it would create - # a new cache for that process. - AVI_SLUG = 'Slug' - SESSION_CACHE_EXPIRY = 20 * 60 - SHARED_USER_HDRS = ['X-CSRFToken', 'Session-Id', 'Referer', 'Content-Type'] - MAX_API_RETRIES = 3 - - def __init__(self, controller_ip=None, username=None, password=None, - token=None, tenant=None, tenant_uuid=None, verify=False, - port=None, timeout=60, api_version=None, - retry_conxn_errors=True, data_log=False, - avi_credentials=None, session_id=None, csrftoken=None, - lazy_authentication=False, max_api_retries=None): - """ - ApiSession takes ownership of avi_credentials and may update the - information inside it. - - Initialize new session object with authenticated token from login api. - It also keeps a cache of user sessions that are cleaned up if inactive - for more than 20 mins. - - Notes: - 01. If mode is https and port is none or 443, we don't embed the - port in the prefix. The prefix would be 'https://ip'. If port - is a non-default value then we concatenate https://ip:port - in the prefix. - 02. If mode is http and the port is none or 80, we don't embed the - port in the prefix. The prefix would be 'http://ip'. If port is - a non-default value, then we concatenate http://ip:port in - the prefix. - """ - super(ApiSession, self).__init__() - if not avi_credentials: - tenant = tenant if tenant else "admin" - self.avi_credentials = AviCredentials( - controller=controller_ip, username=username, password=password, - api_version=api_version, tenant=tenant, tenant_uuid=tenant_uuid, - token=token, port=port, timeout=timeout, - session_id=session_id, csrftoken=csrftoken) - else: - self.avi_credentials = avi_credentials - self.headers = {} - self.verify = verify - self.retry_conxn_errors = retry_conxn_errors - self.remote_api_version = {} - self.session_cookie_name = '' - self.user_hdrs = {} - self.data_log = data_log - self.num_session_retries = 0 - self.retry_wait_time = 0 - self.max_session_retries = ( - self.MAX_API_RETRIES if max_api_retries is None - else int(max_api_retries)) - # Refer Notes 01 and 02 - k_port = port if port else 443 - if self.avi_credentials.controller.startswith('http'): - k_port = 80 if not self.avi_credentials.port else k_port - if self.avi_credentials.port is None or self.avi_credentials.port\ - == 80: - self.prefix = self.avi_credentials.controller - else: - self.prefix = '{x}:{y}'.format( - x=self.avi_credentials.controller, - y=self.avi_credentials.port) - else: - if port is None or port == 443: - self.prefix = 'https://{x}'.format( - x=self.avi_credentials.controller) - else: - self.prefix = 'https://{x}:{y}'.format( - x=self.avi_credentials.controller, - y=self.avi_credentials.port) - self.timeout = timeout - self.key = '%s:%s:%s' % (self.avi_credentials.controller, - self.avi_credentials.username, k_port) - # Added api token and session id to sessionDict for handle single - # session - if self.avi_credentials.csrftoken: - sessionDict[self.key] = { - 'api': self, - "csrftoken": self.avi_credentials.csrftoken, - "session_id": self.avi_credentials.session_id, - "last_used": datetime.utcnow() - } - elif lazy_authentication: - sessionDict.get(self.key, {}).update( - {'api': self, "last_used": datetime.utcnow()}) - else: - self.authenticate_session() - - self.num_session_retries = 0 - self.pid = os.getpid() - ApiSession._clean_inactive_sessions() - return - - @property - def controller_ip(self): - return self.avi_credentials.controller - - @controller_ip.setter - def controller_ip(self, controller_ip): - self.avi_credentials.controller = controller_ip - - @property - def username(self): - return self.avi_credentials.username - - @property - def connected(self): - return sessionDict.get(self.key, {}).get('connected', False) - - @username.setter - def username(self, username): - self.avi_credentials.username = username - - @property - def password(self): - return self.avi_credentials.password - - @password.setter - def password(self, password): - self.avi_credentials.password = password - - @property - def keystone_token(self): - return sessionDict.get(self.key, {}).get('csrftoken', None) - - @keystone_token.setter - def keystone_token(self, token): - sessionDict[self.key]['csrftoken'] = token - - @property - def tenant_uuid(self): - self.avi_credentials.tenant_uuid - - @tenant_uuid.setter - def tenant_uuid(self, tenant_uuid): - self.avi_credentials.tenant_uuid = tenant_uuid - - @property - def tenant(self): - return self.avi_credentials.tenant - - @tenant.setter - def tenant(self, tenant): - if tenant: - self.avi_credentials.tenant = tenant - else: - self.avi_credentials.tenant = 'admin' - - @property - def port(self): - self.avi_credentials.port - - @port.setter - def port(self, port): - self.avi_credentials.port = port - - @property - def api_version(self): - return self.avi_credentials.api_version - - @api_version.setter - def api_version(self, api_version): - self.avi_credentials.api_version = api_version - - @property - def session_id(self): - return sessionDict[self.key]['session_id'] - - def get_context(self): - return { - 'session_id': sessionDict[self.key]['session_id'], - 'csrftoken': sessionDict[self.key]['csrftoken'] - } - - @staticmethod - def clear_cached_sessions(): - global sessionDict - sessionDict = {} - - @staticmethod - def get_session( - controller_ip=None, username=None, password=None, token=None, tenant=None, - tenant_uuid=None, verify=False, port=None, timeout=60, - retry_conxn_errors=True, api_version=None, data_log=False, - avi_credentials=None, session_id=None, csrftoken=None, - lazy_authentication=False, max_api_retries=None): - """ - returns the session object for same user and tenant - calls init if session dose not exist and adds it to session cache - :param controller_ip: controller IP address - :param username: - :param password: - :param token: Token to use; example, a valid keystone token - :param tenant: Name of the tenant on Avi Controller - :param tenant_uuid: Don't specify tenant when using tenant_id - :param port: Rest-API may use a different port other than 443 - :param timeout: timeout for API calls; Default value is 60 seconds - :param retry_conxn_errors: retry on connection errors - :param api_version: Controller API version - """ - if not avi_credentials: - tenant = tenant if tenant else "admin" - avi_credentials = AviCredentials( - controller=controller_ip, username=username, password=password, - api_version=api_version, tenant=tenant, tenant_uuid=tenant_uuid, - token=token, port=port, timeout=timeout, - session_id=session_id, csrftoken=csrftoken) - - k_port = avi_credentials.port if avi_credentials.port else 443 - if avi_credentials.controller.startswith('http'): - k_port = 80 if not avi_credentials.port else k_port - key = '%s:%s:%s' % (avi_credentials.controller, - avi_credentials.username, k_port) - cached_session = sessionDict.get(key) - if cached_session: - user_session = cached_session['api'] - if not (user_session.avi_credentials.csrftoken or - lazy_authentication): - user_session.authenticate_session() - else: - user_session = ApiSession( - controller_ip, username, password, token=token, tenant=tenant, - tenant_uuid=tenant_uuid, verify=verify, port=port, - timeout=timeout, retry_conxn_errors=retry_conxn_errors, - api_version=api_version, data_log=data_log, - avi_credentials=avi_credentials, - lazy_authentication=lazy_authentication, - max_api_retries=max_api_retries) - ApiSession._clean_inactive_sessions() - return user_session - - def reset_session(self): - """ - resets and re-authenticates the current session. - """ - sessionDict[self.key]['connected'] = False - logger.info('resetting session for %s', self.key) - self.user_hdrs = {} - for k, v in self.headers.items(): - if k not in self.SHARED_USER_HDRS: - self.user_hdrs[k] = v - self.headers = {} - self.authenticate_session() - - def authenticate_session(self): - """ - Performs session authentication with Avi controller and stores - session cookies and sets header options like tenant. - """ - body = {"username": self.avi_credentials.username} - if self.avi_credentials.password: - body["password"] = self.avi_credentials.password - elif self.avi_credentials.token: - body["token"] = self.avi_credentials.token - else: - raise APIError("Neither user password or token provided") - logger.debug('authenticating user %s prefix %s', - self.avi_credentials.username, self.prefix) - self.cookies.clear() - err = None - try: - rsp = super(ApiSession, self).post( - self.prefix + "/login", body, timeout=self.timeout, verify=self.verify) - - if rsp.status_code == 200: - self.num_session_retries = 0 - self.remote_api_version = rsp.json().get('version', {}) - self.session_cookie_name = rsp.json().get('session_cookie_name', 'sessionid') - self.headers.update(self.user_hdrs) - if rsp.cookies and 'csrftoken' in rsp.cookies: - csrftoken = rsp.cookies['csrftoken'] - sessionDict[self.key] = { - 'csrftoken': csrftoken, - 'session_id': rsp.cookies[self.session_cookie_name], - 'last_used': datetime.utcnow(), - 'api': self, - 'connected': True - } - logger.debug("authentication success for user %s", - self.avi_credentials.username) - return - # Check for bad request and invalid credentials response code - elif rsp.status_code in [401, 403]: - logger.error('Status Code %s msg %s', rsp.status_code, rsp.text) - err = APIError('Status Code %s msg %s' % ( - rsp.status_code, rsp.text), rsp) - raise err - else: - logger.error("Error status code %s msg %s", rsp.status_code, - rsp.text) - err = APIError('Status Code %s msg %s' % ( - rsp.status_code, rsp.text), rsp) - except (RequestsConnectionError, SSLError) as e: - if not self.retry_conxn_errors: - raise - logger.warning('Connection error retrying %s', e) - err = e - # comes here only if there was either exception or login was not - # successful - if self.retry_wait_time: - time.sleep(self.retry_wait_time) - self.num_session_retries += 1 - if self.num_session_retries > self.max_session_retries: - self.num_session_retries = 0 - logger.error("giving up after %d retries connection failure %s", - self.max_session_retries, True) - ret_err = ( - err if err else APIError("giving up after %d retries connection failure %s" % - (self.max_session_retries, True))) - raise ret_err - self.authenticate_session() - return - - def _get_api_headers(self, tenant, tenant_uuid, timeout, headers, - api_version): - """ - returns the headers that are passed to the requests.Session api calls. - """ - api_hdrs = copy.deepcopy(self.headers) - api_hdrs.update({ - "Referer": self.prefix, - "Content-Type": "application/json" - }) - api_hdrs['timeout'] = str(timeout) - if self.key in sessionDict and 'csrftoken' in sessionDict.get(self.key): - api_hdrs['X-CSRFToken'] = sessionDict.get(self.key)['csrftoken'] - else: - self.authenticate_session() - api_hdrs['X-CSRFToken'] = sessionDict.get(self.key)['csrftoken'] - if api_version: - api_hdrs['X-Avi-Version'] = api_version - elif self.avi_credentials.api_version: - api_hdrs['X-Avi-Version'] = self.avi_credentials.api_version - if tenant: - tenant_uuid = None - elif tenant_uuid: - tenant = None - else: - tenant = self.avi_credentials.tenant - tenant_uuid = self.avi_credentials.tenant_uuid - if tenant_uuid: - api_hdrs.update({"X-Avi-Tenant-UUID": "%s" % tenant_uuid}) - api_hdrs.pop("X-Avi-Tenant", None) - elif tenant: - api_hdrs.update({"X-Avi-Tenant": "%s" % tenant}) - api_hdrs.pop("X-Avi-Tenant-UUID", None) - # Override any user headers that were passed by users. We don't know - # when the user had updated the user_hdrs - if self.user_hdrs: - api_hdrs.update(self.user_hdrs) - if headers: - # overwrite the headers passed via the API calls. - api_hdrs.update(headers) - return api_hdrs - - def _api(self, api_name, path, tenant, tenant_uuid, data=None, - headers=None, timeout=None, api_version=None, **kwargs): - """ - It calls the requests.Session APIs and handles session expiry - and other situations where session needs to be reset. - returns ApiResponse object - :param path: takes relative path to the AVI api. - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param headers: dictionary of headers that override the session - headers. - """ - if self.pid != os.getpid(): - logger.info('pid %d change detected new %d. Closing session', - self.pid, os.getpid()) - self.close() - self.pid = os.getpid() - if timeout is None: - timeout = self.timeout - fullpath = self._get_api_path(path) - fn = getattr(super(ApiSession, self), api_name) - api_hdrs = self._get_api_headers(tenant, tenant_uuid, timeout, headers, - api_version) - connection_error = False - err = None - cookies = { - 'csrftoken': api_hdrs['X-CSRFToken'], - } - try: - if self.session_cookie_name: - cookies[self.session_cookie_name] = sessionDict[self.key]['session_id'] - except KeyError: - pass - try: - if (data is not None) and (type(data) == dict): - resp = fn(fullpath, data=json.dumps(data), headers=api_hdrs, - timeout=timeout, cookies=cookies, **kwargs) - else: - resp = fn(fullpath, data=data, headers=api_hdrs, - timeout=timeout, cookies=cookies, **kwargs) - except (RequestsConnectionError, SSLError) as e: - logger.warning('Connection error retrying %s', e) - if not self.retry_conxn_errors: - raise - connection_error = True - err = e - except Exception as e: - logger.error('Error in Requests library %s', e) - raise - if not connection_error: - logger.debug('path: %s http_method: %s hdrs: %s params: ' - '%s data: %s rsp: %s', fullpath, api_name.upper(), - api_hdrs, kwargs, data, - (resp.text if self.data_log else 'None')) - if connection_error or resp.status_code in (401, 419): - if connection_error: - try: - self.close() - except Exception: - # ignoring exception in cleanup path - pass - logger.warning('Connection failed, retrying.') - # Adding sleep before retrying - if self.retry_wait_time: - time.sleep(self.retry_wait_time) - else: - logger.info('received error %d %s so resetting connection', - resp.status_code, resp.text) - ApiSession.reset_session(self) - self.num_session_retries += 1 - if self.num_session_retries > self.max_session_retries: - # Added this such that any code which re-tries can succeed - # eventually. - self.num_session_retries = 0 - if not connection_error: - err = APIError('Status Code %s msg %s' % ( - resp.status_code, resp.text), resp) - logger.error( - "giving up after %d retries conn failure %s err %s", - self.max_session_retries, connection_error, err) - ret_err = ( - err if err else APIError("giving up after %d retries connection failure %s" % - (self.max_session_retries, True))) - raise ret_err - # should restore the updated_hdrs to one passed down - resp = self._api(api_name, path, tenant, tenant_uuid, data, - headers=headers, api_version=api_version, - timeout=timeout, **kwargs) - self.num_session_retries = 0 - - if resp.cookies and 'csrftoken' in resp.cookies: - csrftoken = resp.cookies['csrftoken'] - self.headers.update({"X-CSRFToken": csrftoken}) - self._update_session_last_used() - return ApiResponse.to_avi_response(resp) - - def get_controller_details(self): - result = { - "controller_ip": self.controller_ip, - "controller_api_version": self.remote_api_version - } - return result - - def get(self, path, tenant='', tenant_uuid='', timeout=None, params=None, - api_version=None, **kwargs): - """ - It extends the Session Library interface to add AVI API prefixes, - handle session exceptions related to authentication and update - the global user session cache. - :param path: takes relative path to the AVI api. - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param params: dictionary of key value pairs to be sent as query - parameters - :param api_version: overrides x-avi-header in request header during - session creation - get method takes relative path to service and kwargs as per Session - class get method - returns session's response object - """ - return self._api('get', path, tenant, tenant_uuid, timeout=timeout, - params=params, api_version=api_version, **kwargs) - - def get_object_by_name(self, path, name, tenant='', tenant_uuid='', - timeout=None, params=None, api_version=None, - **kwargs): - """ - Helper function to access Avi REST Objects using object - type and name. It behaves like python dictionary interface where it - returns None when the object is not present in the AviController. - Internally, it transforms the request to api/path?name=... - :param path: relative path to service - :param name: name of the object - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param params: dictionary of key value pairs to be sent as query - parameters - :param api_version: overrides x-avi-header in request header during - session creation - returns dictionary object if successful else None - """ - obj = None - if not params: - params = {} - params['name'] = name - resp = self.get(path, tenant=tenant, tenant_uuid=tenant_uuid, - timeout=timeout, - params=params, api_version=api_version, **kwargs) - if resp.status_code in (401, 419): - ApiSession.reset_session(self) - resp = self.get_object_by_name( - path, name, tenant, tenant_uuid, timeout=timeout, - params=params, **kwargs) - if resp.status_code > 499 or 'Invalid version' in resp.text: - logger.error('Error in get object by name for %s named %s. ' - 'Error: %s', path, name, resp.text) - raise AviServerError(resp.text, rsp=resp) - elif resp.status_code > 299: - return obj - try: - if 'results' in resp.json(): - obj = resp.json()['results'][0] - else: - # For apis returning single object eg. api/cluster - obj = resp.json() - except IndexError: - logger.warning('Warning: Object Not found for %s named %s', - path, name) - obj = None - self._update_session_last_used() - return obj - - def post(self, path, data=None, tenant='', tenant_uuid='', timeout=None, - force_uuid=None, params=None, api_version=None, **kwargs): - """ - It extends the Session Library interface to add AVI API prefixes, - handle session exceptions related to authentication and update - the global user session cache. - :param path: takes relative path to the AVI api.It is modified by - the library to conform to AVI Controller's REST API interface - :param data: dictionary of the data. Support for json string - is deprecated - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param params: dictionary of key value pairs to be sent as query - parameters - :param api_version: overrides x-avi-header in request header during - session creation - returns session's response object - """ - if force_uuid is not None: - headers = kwargs.get('headers', {}) - headers[self.AVI_SLUG] = force_uuid - kwargs['headers'] = headers - return self._api('post', path, tenant, tenant_uuid, data=data, - timeout=timeout, params=params, - api_version=api_version, **kwargs) - - def put(self, path, data=None, tenant='', tenant_uuid='', - timeout=None, params=None, api_version=None, **kwargs): - """ - It extends the Session Library interface to add AVI API prefixes, - handle session exceptions related to authentication and update - the global user session cache. - :param path: takes relative path to the AVI api.It is modified by - the library to conform to AVI Controller's REST API interface - :param data: dictionary of the data. Support for json string - is deprecated - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param params: dictionary of key value pairs to be sent as query - parameters - :param api_version: overrides x-avi-header in request header during - session creation - returns session's response object - """ - return self._api('put', path, tenant, tenant_uuid, data=data, - timeout=timeout, params=params, - api_version=api_version, **kwargs) - - def patch(self, path, data=None, tenant='', tenant_uuid='', - timeout=None, params=None, api_version=None, **kwargs): - """ - It extends the Session Library interface to add AVI API prefixes, - handle session exceptions related to authentication and update - the global user session cache. - :param path: takes relative path to the AVI api.It is modified by - the library to conform to AVI Controller's REST API interface - :param data: dictionary of the data. Support for json string - is deprecated - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param params: dictionary of key value pairs to be sent as query - parameters - :param api_version: overrides x-avi-header in request header during - session creation - returns session's response object - """ - return self._api('patch', path, tenant, tenant_uuid, data=data, - timeout=timeout, params=params, - api_version=api_version, **kwargs) - - def put_by_name(self, path, name, data=None, tenant='', - tenant_uuid='', timeout=None, params=None, - api_version=None, **kwargs): - """ - Helper function to perform HTTP PUT on Avi REST Objects using object - type and name. - Internally, it transforms the request to api/path?name=... - :param path: relative path to service - :param name: name of the object - :param data: dictionary of the data. Support for json string - is deprecated - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param params: dictionary of key value pairs to be sent as query - parameters - :param api_version: overrides x-avi-header in request header during - session creation - returns session's response object - """ - uuid = self._get_uuid_by_name( - path, name, tenant, tenant_uuid, api_version=api_version) - path = '%s/%s' % (path, uuid) - return self.put(path, data, tenant, tenant_uuid, timeout=timeout, - params=params, api_version=api_version, **kwargs) - - def delete(self, path, tenant='', tenant_uuid='', timeout=None, params=None, - data=None, api_version=None, **kwargs): - """ - It extends the Session Library interface to add AVI API prefixes, - handle session exceptions related to authentication and update - the global user session cache. - :param path: takes relative path to the AVI api.It is modified by - the library to conform to AVI Controller's REST API interface - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param params: dictionary of key value pairs to be sent as query - parameters - :param data: dictionary of the data. Support for json string - is deprecated - :param api_version: overrides x-avi-header in request header during - session creation - returns session's response object - """ - return self._api('delete', path, tenant, tenant_uuid, data=data, - timeout=timeout, params=params, - api_version=api_version, **kwargs) - - def delete_by_name(self, path, name, tenant='', tenant_uuid='', - timeout=None, params=None, api_version=None, **kwargs): - """ - Helper function to perform HTTP DELETE on Avi REST Objects using object - type and name.Internally, it transforms the request to - api/path?name=... - :param path: relative path to service - :param name: name of the object - :param tenant: overrides the tenant used during session creation - :param tenant_uuid: overrides the tenant or tenant_uuid during session - creation - :param timeout: timeout for API calls; Default value is 60 seconds - :param params: dictionary of key value pairs to be sent as query - parameters - :param api_version: overrides x-avi-header in request header during - session creation - returns session's response object - """ - uuid = self._get_uuid_by_name(path, name, tenant, tenant_uuid, - api_version=api_version) - if not uuid: - raise ObjectNotFound("%s/?name=%s" % (path, name)) - path = '%s/%s' % (path, uuid) - return self.delete(path, tenant, tenant_uuid, timeout=timeout, - params=params, api_version=api_version, **kwargs) - - def get_obj_ref(self, obj): - """returns reference url from dict object""" - if not obj: - return None - if isinstance(obj, Response): - obj = json.loads(obj.text) - if obj.get(0, None): - return obj[0]['url'] - elif obj.get('url', None): - return obj['url'] - elif obj.get('results', None): - return obj['results'][0]['url'] - else: - return None - - def get_obj_uuid(self, obj): - """returns uuid from dict object""" - if not obj: - raise ObjectNotFound('Object %s Not found' % (obj)) - if isinstance(obj, Response): - obj = json.loads(obj.text) - if obj.get(0, None): - return obj[0]['uuid'] - elif obj.get('uuid', None): - return obj['uuid'] - elif obj.get('results', None): - return obj['results'][0]['uuid'] - else: - return None - - def _get_api_path(self, path, uuid=None): - """ - This function returns the full url from relative path and uuid. - """ - if path == 'logout': - return self.prefix + '/' + path - elif uuid: - return self.prefix + '/api/' + path + '/' + uuid - else: - return self.prefix + '/api/' + path - - def _get_uuid_by_name(self, path, name, tenant='admin', - tenant_uuid='', api_version=None): - """gets object by name and service path and returns uuid""" - resp = self.get_object_by_name( - path, name, tenant, tenant_uuid, api_version=api_version) - if not resp: - raise ObjectNotFound("%s/%s" % (path, name)) - return self.get_obj_uuid(resp) - - def _update_session_last_used(self): - if self.key in sessionDict: - sessionDict[self.key]["last_used"] = datetime.utcnow() - - @staticmethod - def _clean_inactive_sessions(): - """Removes sessions which are inactive more than 20 min""" - session_cache = sessionDict - logger.debug("cleaning inactive sessions in pid %d num elem %d", - os.getpid(), len(session_cache)) - keys_to_delete = [] - for key, session in list(session_cache.items()): - tdiff = avi_timedelta(datetime.utcnow() - session["last_used"]) - if tdiff < ApiSession.SESSION_CACHE_EXPIRY: - continue - keys_to_delete.append(key) - for key in keys_to_delete: - del session_cache[key] - logger.debug("Removed session for : %s", key) - - def delete_session(self): - """ Removes the session for cleanup""" - logger.debug("Removed session for : %s", self.key) - sessionDict.pop(self.key, None) - return -# End of file diff --git a/plugins/module_utils/network/bigswitch/__init__.py b/plugins/module_utils/network/bigswitch/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/bigswitch/bigswitch.py b/plugins/module_utils/network/bigswitch/bigswitch.py deleted file mode 100644 index 299fcd3310..0000000000 --- a/plugins/module_utils/network/bigswitch/bigswitch.py +++ /dev/null @@ -1,91 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2016, Ted Elhourani -# -# -# 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. - -import json - -from ansible.module_utils.urls import fetch_url - - -class Response(object): - - def __init__(self, resp, info): - self.body = None - if resp: - self.body = resp.read() - self.info = info - - @property - def json(self): - if not self.body: - if "body" in self.info: - return json.loads(self.info["body"]) - return None - try: - return json.loads(self.body) - except ValueError: - return None - - @property - def status_code(self): - return self.info["status"] - - -class Rest(object): - - def __init__(self, module, headers, baseurl): - self.module = module - self.headers = headers - self.baseurl = baseurl - - def _url_builder(self, path): - if path[0] == '/': - path = path[1:] - return '%s/%s' % (self.baseurl, path) - - def send(self, method, path, data=None, headers=None): - url = self._url_builder(path) - data = self.module.jsonify(data) - - resp, info = fetch_url(self.module, url, data=data, headers=self.headers, method=method) - - return Response(resp, info) - - def get(self, path, data=None, headers=None): - return self.send('GET', path, data, headers) - - def put(self, path, data=None, headers=None): - return self.send('PUT', path, data, headers) - - def post(self, path, data=None, headers=None): - return self.send('POST', path, data, headers) - - def patch(self, path, data=None, headers=None): - return self.send('PATCH', path, data, headers) - - def delete(self, path, data=None, headers=None): - return self.send('DELETE', path, data, headers) diff --git a/plugins/module_utils/network/checkpoint/__init__.py b/plugins/module_utils/network/checkpoint/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/cloudengine/__init__.py b/plugins/module_utils/network/cloudengine/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/cloudengine/ce.py b/plugins/module_utils/network/cloudengine/ce.py deleted file mode 100644 index b9fe91ff3c..0000000000 --- a/plugins/module_utils/network/cloudengine/ce.py +++ /dev/null @@ -1,421 +0,0 @@ -# -# This code is part of Ansible, but is an independent component. -# -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2017 Red Hat, Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -import re -import socket -import sys -import traceback - -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import exec_command, ConnectionError -from ansible.module_utils.six import iteritems -from ansible.module_utils._text import to_native -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.netconf import NetconfConnection - - -try: - from ncclient.xml_ import to_xml, new_ele_ns - HAS_NCCLIENT = True -except ImportError: - HAS_NCCLIENT = False - - -try: - from lxml import etree -except ImportError: - from xml.etree import ElementTree as etree - -_DEVICE_CLI_CONNECTION = None -_DEVICE_NC_CONNECTION = None - -ce_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'), - 'use_ssl': dict(type='bool'), - 'validate_certs': dict(type='bool'), - 'timeout': dict(type='int'), - 'transport': dict(default='cli', choices=['cli', 'netconf']), -} -ce_argument_spec = { - 'provider': dict(type='dict', options=ce_provider_spec), -} -ce_top_spec = { - 'host': dict(removed_in_version=2.9), - 'port': dict(removed_in_version=2.9, type='int'), - 'username': dict(removed_in_version=2.9), - 'password': dict(removed_in_version=2.9, no_log=True), - 'ssh_keyfile': dict(removed_in_version=2.9, type='path'), - 'use_ssl': dict(removed_in_version=2.9, type='bool'), - 'validate_certs': dict(removed_in_version=2.9, type='bool'), - 'timeout': dict(removed_in_version=2.9, type='int'), - 'transport': dict(removed_in_version=2.9, choices=['cli', 'netconf']), -} -ce_argument_spec.update(ce_top_spec) - - -def to_string(data): - return re.sub(r'|>)', r' 2 and err[0] in ["<", "["] and err[-1] in [">", "]"]: - continue - err.strip('.,\r\n\t ') - if err: - msg.append(err) - - if cmd: - msg.insert(0, "Command: %s" % cmd) - - return ", ".join(msg).capitalize() + "." - - -def to_command(module, commands): - default_output = 'text' - transform = ComplexList(dict( - command=dict(key=True), - output=dict(default=default_output), - prompt=dict(), - answer=dict() - ), module) - - commands = transform(to_list(commands)) - - return commands - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - conn = get_connection(module) - return conn.get_config(flags) - - -def run_commands(module, commands, check_rc=True): - conn = get_connection(module) - return conn.run_commands(to_command(module, commands), check_rc) - - -def load_config(module, config): - """load_config""" - conn = get_connection(module) - return conn.load_config(config) - - -def ce_unknown_host_cb(host, fingerprint): - """ ce_unknown_host_cb """ - - return True - - -def get_nc_set_id(xml_str): - """get netconf set-id value""" - - result = re.findall(r'= 0 and index >= len(xml_list): - return None - if index < 0 and abs(index) > len(xml_list): - return None - - ele = xml_list[index] - if not ele.replace(" ", ""): - xml_list.pop(index) - ele = None - return ele - - -def merge_nc_xml(xml1, xml2): - """merge xml1 and xml2""" - - xml1_list = xml1.split("")[0].split("\n") - xml2_list = xml2.split("")[1].split("\n") - - while True: - xml1_ele1 = get_xml_line(xml1_list, -1) - xml1_ele2 = get_xml_line(xml1_list, -2) - xml2_ele1 = get_xml_line(xml2_list, 0) - xml2_ele2 = get_xml_line(xml2_list, 1) - if not xml1_ele1 or not xml1_ele2 or not xml2_ele1 or not xml2_ele2: - return xml1 - - if "xmlns" in xml2_ele1: - xml2_ele1 = xml2_ele1.lstrip().split(" ")[0] + ">" - if "xmlns" in xml2_ele2: - xml2_ele2 = xml2_ele2.lstrip().split(" ")[0] + ">" - if xml1_ele1.replace(" ", "").replace("/", "") == xml2_ele1.replace(" ", "").replace("/", ""): - if xml1_ele2.replace(" ", "").replace("/", "") == xml2_ele2.replace(" ", "").replace("/", ""): - xml1_list.pop() - xml2_list.pop(0) - else: - break - else: - break - - return "\n".join(xml1_list + xml2_list) - - -def get_nc_connection(module): - global _DEVICE_NC_CONNECTION - if not _DEVICE_NC_CONNECTION: - load_params(module) - conn = NetconfConnection(module._socket_path) - _DEVICE_NC_CONNECTION = conn - return _DEVICE_NC_CONNECTION - - -def set_nc_config(module, xml_str): - """ set_config """ - - conn = get_nc_connection(module) - try: - out = conn.edit_config(target='running', config=xml_str, default_operation='merge', - error_option='rollback-on-error') - finally: - # conn.unlock(target = 'candidate') - pass - return to_string(to_xml(out)) - - -def get_nc_next(module, xml_str): - """ get_nc_next for exchange capability """ - - conn = get_nc_connection(module) - result = None - if xml_str is not None: - response = conn.get(xml_str, if_rpc_reply=True) - result = response.find('./*') - set_id = response.get('set-id') - while True and set_id is not None: - try: - fetch_node = new_ele_ns('get-next', 'http://www.huawei.com/netconf/capability/base/1.0', {'set-id': set_id}) - next_xml = conn.dispatch_rpc(etree.tostring(fetch_node)) - if next_xml is not None: - result.extend(next_xml.find('./*')) - set_id = next_xml.get('set-id') - except ConnectionError: - break - if result is not None: - return etree.tostring(result) - return result - - -def get_nc_config(module, xml_str): - """ get_config """ - - conn = get_nc_connection(module) - if xml_str is not None: - response = conn.get(xml_str) - else: - return None - - return to_string(to_xml(response)) - - -def execute_nc_action(module, xml_str): - """ huawei execute-action """ - - conn = get_nc_connection(module) - response = conn.execute_action(xml_str) - return to_string(to_xml(response)) - - -def execute_nc_cli(module, xml_str): - """ huawei execute-cli """ - - if xml_str is not None: - try: - conn = get_nc_connection(module) - out = conn.execute_nc_cli(command=xml_str) - return to_string(to_xml(out)) - except Exception as exc: - raise Exception(exc) - - -def check_ip_addr(ipaddr): - """ check ip address, Supports IPv4 and IPv6 """ - - if not ipaddr or '\x00' in ipaddr: - return False - - try: - res = socket.getaddrinfo(ipaddr, 0, socket.AF_UNSPEC, - socket.SOCK_STREAM, - 0, socket.AI_NUMERICHOST) - return bool(res) - except socket.gaierror: - err = sys.exc_info()[1] - if err.args[0] == socket.EAI_NONAME: - return False - raise diff --git a/plugins/module_utils/network/cnos/__init__.py b/plugins/module_utils/network/cnos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/cnos/cnos.py b/plugins/module_utils/network/cnos/cnos.py deleted file mode 100644 index ae12a9a22d..0000000000 --- a/plugins/module_utils/network/cnos/cnos.py +++ /dev/null @@ -1,660 +0,0 @@ -# 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. -# -# Copyright (C) 2017 Lenovo, Inc. -# All rights reserved. -# -# 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. -# -# Contains utility methods -# Lenovo Networking - -import time -import socket -import re -import json -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos_errorcodes - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos_devicerules - HAS_LIB = True -except Exception: - HAS_LIB = False -from distutils.cmd import Command -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, EntityCollection -from ansible.module_utils.connection import Connection, exec_command -from ansible.module_utils.connection import ConnectionError - -_DEVICE_CONFIGS = {} -_CONNECTION = None -_VALID_USER_ROLES = ['network-admin', 'network-operator'] - -cnos_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'), - 'context': dict(), - 'passwords': dict() -} - -cnos_argument_spec = { - 'provider': dict(type='dict', options=cnos_provider_spec), -} - -command_spec = { - 'command': dict(key=True), - 'prompt': dict(), - 'answer': dict(), - 'check_all': dict() -} - - -def get_provider_argspec(): - return cnos_provider_spec - - -def check_args(module, warnings): - pass - - -def get_user_roles(): - return _VALID_USER_ROLES - - -def get_connection(module): - global _CONNECTION - if _CONNECTION: - return _CONNECTION - _CONNECTION = Connection(module._socket_path) - - context = None - try: - context = module.params['context'] - except KeyError: - context = None - - if context: - if context == 'system': - command = 'changeto system' - else: - command = 'changeto context %s' % context - _CONNECTION.get(command) - - return _CONNECTION - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - passwords = None - try: - passwords = module.params['passwords'] - except KeyError: - passwords = None - if passwords: - cmd = 'more system:running-config' - else: - cmd = 'display running-config ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - try: - return _DEVICE_CONFIGS[cmd] - except KeyError: - conn = get_connection(module) - out = conn.get(cmd) - cfg = to_text(out, errors='surrogate_then_replace').strip() - _DEVICE_CONFIGS[cmd] = cfg - return cfg - - -def to_commands(module, commands): - if not isinstance(commands, list): - raise AssertionError('argument must be of type ') - - transform = EntityCollection(module, command_spec) - commands = transform(commands) - - for index, item in enumerate(commands): - if module.check_mode and not item['command'].startswith('show'): - module.warn('only show commands are supported when using check ' - 'mode, not executing `%s`' % item['command']) - - return commands - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - connection.get('enable') - commands = to_commands(module, to_list(commands)) - - responses = list() - - for cmd in commands: - out = connection.get(**cmd) - responses.append(to_text(out, errors='surrogate_then_replace')) - - return responses - - -def run_cnos_commands(module, commands, check_rc=True): - retVal = '' - enter_config = {'command': 'configure terminal', 'prompt': None, - 'answer': None} - exit_config = {'command': 'end', 'prompt': None, 'answer': None} - commands.insert(0, enter_config) - commands.append(exit_config) - for cmd in commands: - retVal = retVal + '>> ' + cmd['command'] + '\n' - try: - responses = run_commands(module, commands, check_rc) - for response in responses: - retVal = retVal + '<< ' + response + '\n' - except Exception as e: - errMsg = '' - if hasattr(e, 'message'): - errMsg = e.message - else: - errMsg = str(e) - # Exception in Exceptions - if 'VLAN_ACCESS_MAP' in errMsg: - return retVal + '<<' + errMsg + '\n' - if 'confederation identifier' in errMsg: - return retVal + '<<' + errMsg + '\n' - # Add more here if required - retVal = retVal + '<< ' + 'Error-101 ' + errMsg + '\n' - return str(retVal) - - -def get_capabilities(module): - if hasattr(module, '_cnos_capabilities'): - return module._cnos_capabilities - try: - capabilities = Connection(module._socket_path).get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module._cnos_capabilities = json.loads(capabilities) - return module._cnos_capabilities - - -def load_config(module, config): - try: - conn = get_connection(module) - conn.get('enable') - resp = conn.edit_config(config) - return resp.get('response') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def get_defaults_flag(module): - rc, out, err = exec_command(module, 'display running-config ?') - out = to_text(out, errors='surrogate_then_replace') - - commands = set() - for line in out.splitlines(): - if line: - commands.add(line.strip().split()[0]) - - if 'all' in commands: - return 'all' - else: - return 'full' - - -def enterEnableModeForDevice(enablePassword, timeout, obj): - command = "enable\n" - pwdPrompt = "password:" - # debugOutput(enablePassword) - # debugOutput('\n') - obj.settimeout(int(timeout)) - # Executing enable - obj.send(command) - flag = False - retVal = "" - count = 5 - while not flag: - # If wait time is execeeded. - if(count == 0): - flag = True - else: - count = count - 1 - # A delay of one second - time.sleep(1) - try: - buffByte = obj.recv(9999) - buff = buffByte.decode() - retVal = retVal + buff - # debugOutput(buff) - gotit = buff.find(pwdPrompt) - if(gotit != -1): - time.sleep(1) - if(enablePassword is None or enablePassword == ""): - return "\n Error-106" - obj.send(enablePassword) - obj.send("\r") - obj.send("\n") - time.sleep(1) - innerBuffByte = obj.recv(9999) - innerBuff = innerBuffByte.decode() - retVal = retVal + innerBuff - # debugOutput(innerBuff) - innerGotit = innerBuff.find("#") - if(innerGotit != -1): - return retVal - else: - gotit = buff.find("#") - if(gotit != -1): - return retVal - except Exception: - retVal = retVal + "\n Error-101" - flag = True - if(retVal == ""): - retVal = "\n Error-101" - return retVal -# EOM - - -def waitForDeviceResponse(command, prompt, timeout, obj): - obj.settimeout(int(timeout)) - obj.send(command) - flag = False - retVal = "" - while not flag: - time.sleep(1) - try: - buffByte = obj.recv(9999) - buff = buffByte.decode() - retVal = retVal + buff - # debugOutput(retVal) - gotit = buff.find(prompt) - if(gotit != -1): - flag = True - except Exception: - # debugOutput(prompt) - if prompt == "(yes/no)?": - pass - elif prompt == "Password:": - pass - else: - retVal = retVal + "\n Error-101" - flag = True - return retVal -# EOM - - -def checkOutputForError(output): - retVal = "" - index = output.lower().find('error') - startIndex = index + 6 - if(index == -1): - index = output.lower().find('invalid') - startIndex = index + 8 - if(index == -1): - index = output.lower().find('cannot be enabled in l2 interface') - startIndex = index + 34 - if(index == -1): - index = output.lower().find('incorrect') - startIndex = index + 10 - if(index == -1): - index = output.lower().find('failure') - startIndex = index + 8 - if(index == -1): - return None - - endIndex = startIndex + 3 - errorCode = output[startIndex:endIndex] - result = errorCode.isdigit() - if(result is not True): - return "Device returned an Error. Please check Results for more \ - information" - - errorFile = "dictionary/ErrorCodes.lvo" - try: - # with open(errorFile, 'r') as f: - f = open(errorFile, 'r') - for line in f: - if('=' in line): - data = line.split('=') - if(data[0].strip() == errorCode): - errorString = data[1].strip() - return errorString - except Exception: - errorString = cnos_errorcodes.getErrorString(errorCode) - errorString = errorString.strip() - return errorString - return "Error Code Not Found" -# EOM - - -def checkSanityofVariable(deviceType, variableId, variableValue): - retVal = "" - ruleFile = "dictionary/" + deviceType + "_rules.lvo" - ruleString = getRuleStringForVariable(deviceType, ruleFile, variableId) - retVal = validateValueAgainstRule(ruleString, variableValue) - return retVal -# EOM - - -def getRuleStringForVariable(deviceType, ruleFile, variableId): - retVal = "" - try: - # with open(ruleFile, 'r') as f: - f = open(ruleFile, 'r') - for line in f: - # debugOutput(line) - if(':' in line): - data = line.split(':') - # debugOutput(data[0]) - if(data[0].strip() == variableId): - retVal = line - except Exception: - ruleString = cnos_devicerules.getRuleString(deviceType, variableId) - retVal = ruleString.strip() - return retVal -# EOM - - -def validateValueAgainstRule(ruleString, variableValue): - - retVal = "" - if(ruleString == ""): - return 1 - rules = ruleString.split(':') - variableType = rules[1].strip() - varRange = rules[2].strip() - if(variableType == "INTEGER"): - result = checkInteger(variableValue) - if(result is True): - return "ok" - else: - return "Error-111" - elif(variableType == "FLOAT"): - result = checkFloat(variableValue) - if(result is True): - return "ok" - else: - return "Error-112" - - elif(variableType == "INTEGER_VALUE"): - int_range = varRange.split('-') - r = range(int(int_range[0].strip()), int(int_range[1].strip())) - if(checkInteger(variableValue) is not True): - return "Error-111" - result = int(variableValue) in r - if(result is True): - return "ok" - else: - return "Error-113" - - elif(variableType == "INTEGER_VALUE_RANGE"): - int_range = varRange.split('-') - varLower = int_range[0].strip() - varHigher = int_range[1].strip() - r = range(int(varLower), int(varHigher)) - val_range = variableValue.split('-') - try: - valLower = val_range[0].strip() - valHigher = val_range[1].strip() - except Exception: - return "Error-113" - if((checkInteger(valLower) is not True) or - (checkInteger(valHigher) is not True)): - # debugOutput("Error-114") - return "Error-114" - result = (int(valLower) in r) and (int(valHigher)in r) \ - and (int(valLower) < int(valHigher)) - if(result is True): - return "ok" - else: - # debugOutput("Error-113") - return "Error-113" - - elif(variableType == "INTEGER_OPTIONS"): - int_options = varRange.split(',') - if(checkInteger(variableValue) is not True): - return "Error-111" - for opt in int_options: - if(opt.strip() is variableValue): - result = True - break - if(result is True): - return "ok" - else: - return "Error-115" - - elif(variableType == "LONG"): - result = checkLong(variableValue) - if(result is True): - return "ok" - else: - return "Error-116" - - elif(variableType == "LONG_VALUE"): - long_range = varRange.split('-') - r = range(int(long_range[0].strip()), int(long_range[1].strip())) - if(checkLong(variableValue) is not True): - # debugOutput(variableValue) - return "Error-116" - result = int(variableValue) in r - if(result is True): - return "ok" - else: - return "Error-113" - - elif(variableType == "LONG_VALUE_RANGE"): - long_range = varRange.split('-') - r = range(int(long_range[0].strip()), int(long_range[1].strip())) - val_range = variableValue.split('-') - if((checkLong(val_range[0]) is not True) or - (checkLong(val_range[1]) is not True)): - return "Error-117" - result = (val_range[0] in r) and ( - val_range[1] in r) and (val_range[0] < val_range[1]) - if(result is True): - return "ok" - else: - return "Error-113" - elif(variableType == "LONG_OPTIONS"): - long_options = varRange.split(',') - if(checkLong(variableValue) is not True): - return "Error-116" - for opt in long_options: - if(opt.strip() == variableValue): - result = True - break - if(result is True): - return "ok" - else: - return "Error-115" - - elif(variableType == "TEXT"): - if(variableValue == ""): - return "Error-118" - if(True is isinstance(variableValue, str)): - return "ok" - else: - return "Error-119" - - elif(variableType == "NO_VALIDATION"): - if(variableValue == ""): - return "Error-118" - else: - return "ok" - - elif(variableType == "TEXT_OR_EMPTY"): - if(variableValue is None or variableValue == ""): - return "ok" - if(result == isinstance(variableValue, str)): - return "ok" - else: - return "Error-119" - - elif(variableType == "MATCH_TEXT"): - if(variableValue == ""): - return "Error-118" - if(isinstance(variableValue, str)): - if(varRange == variableValue): - return "ok" - else: - return "Error-120" - else: - return "Error-119" - - elif(variableType == "MATCH_TEXT_OR_EMPTY"): - if(variableValue is None or variableValue == ""): - return "ok" - if(isinstance(variableValue, str)): - if(varRange == variableValue): - return "ok" - else: - return "Error-120" - else: - return "Error-119" - - elif(variableType == "TEXT_OPTIONS"): - str_options = varRange.split(',') - if(isinstance(variableValue, str) is not True): - return "Error-119" - result = False - for opt in str_options: - if(opt.strip() == variableValue): - result = True - break - if(result is True): - return "ok" - else: - return "Error-115" - - elif(variableType == "TEXT_OPTIONS_OR_EMPTY"): - if(variableValue is None or variableValue == ""): - return "ok" - str_options = varRange.split(',') - if(isinstance(variableValue, str) is not True): - return "Error-119" - for opt in str_options: - if(opt.strip() == variableValue): - result = True - break - if(result is True): - return "ok" - else: - return "Error-115" - - elif(variableType == "IPV4Address"): - try: - socket.inet_pton(socket.AF_INET, variableValue) - result = True - except socket.error: - result = False - if(result is True): - return "ok" - else: - return "Error-121" - elif(variableType == "IPV4AddressWithMask"): - if(variableValue is None or variableValue == ""): - return "Error-119" - str_options = variableValue.split('/') - ipaddr = str_options[0] - mask = str_options[1] - try: - socket.inet_pton(socket.AF_INET, ipaddr) - if(checkInteger(mask) is True): - result = True - else: - result = False - except socket.error: - result = False - if(result is True): - return "ok" - else: - return "Error-121" - - elif(variableType == "IPV6Address"): - try: - socket.inet_pton(socket.AF_INET6, variableValue) - result = True - except socket.error: - result = False - if(result is True): - return "ok" - else: - return "Error-122" - - return retVal -# EOM - - -def disablePaging(remote_conn): - remote_conn.send("terminal length 0\n") - time.sleep(1) - # Clear the buffer on the screen - outputByte = remote_conn.recv(1000) - output = outputByte.decode() - return output -# EOM - - -def checkInteger(s): - try: - int(s) - return True - except ValueError: - return False -# EOM - - -def checkFloat(s): - try: - float(s) - return True - except ValueError: - return False -# EOM - - -def checkLong(s): - try: - int(s) - return True - except ValueError: - return False - - -def debugOutput(command): - f = open('debugOutput.txt', 'a') - f.write(str(command)) # python will convert \n to os.linesep - f.close() # you can omit in most cases as the destructor will call it -# EOM diff --git a/plugins/module_utils/network/cnos/cnos_devicerules.py b/plugins/module_utils/network/cnos/cnos_devicerules.py deleted file mode 100644 index f6c8f24ea7..0000000000 --- a/plugins/module_utils/network/cnos/cnos_devicerules.py +++ /dev/null @@ -1,1921 +0,0 @@ -# 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. -# -# Copyright (C) 2017 Lenovo, Inc. -# All rights reserved. -# -# 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. -# -# Contains device rule and methods -# Lenovo Networking - - -def getRuleString(deviceType, variableId): - retVal = variableId + ":" - if(deviceType == 'g8272_cnos'): - if variableId in g8272_cnos: - retVal = retVal + g8272_cnos[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - elif(deviceType == 'g8296_cnos'): - if variableId in g8296_cnos: - retVal = retVal + g8296_cnos[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - elif(deviceType == 'g8332_cnos'): - if variableId in g8332_cnos: - retVal = retVal + g8332_cnos[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - elif(deviceType == 'NE1072T'): - if variableId in NE1072T: - retVal = retVal + NE1072T[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - elif(deviceType == 'NE1032'): - if variableId in NE1032: - retVal = retVal + NE1032[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - elif(deviceType == 'NE1032T'): - if variableId in NE1032T: - retVal = retVal + NE1032T[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - elif(deviceType == 'NE10032'): - if variableId in NE10032: - retVal = retVal + NE10032[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - elif(deviceType == 'NE2572'): - if variableId in NE2572: - retVal = retVal + NE2572[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - elif(deviceType == 'NE0152T'): - if variableId in NE0152T: - retVal = retVal + NE0152T[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - else: - if variableId in default_cnos: - retVal = retVal + default_cnos[variableId] - else: - retVal = "The variable " + variableId + " is not supported" - return retVal -# EOM - - -default_cnos = { - 'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-64', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-32', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-32', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,\ - interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,\ - trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,\ - input,output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,\ - vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:1000,10000,100000,25000,40000,50000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', -} -NE0152T = { - 'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-64', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-52', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-52', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,\ - interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,\ - trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,\ - input,output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,\ - vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:10,100,1000,10000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', -} -NE2572 = { - 'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-64', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-54', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-54', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,input,\ - output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:10000,100000,25000,40000,50000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', -} -NE1032T = { - 'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-64', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-32', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-32', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,input,\ - output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:1000,10000,100000,25000,40000,50000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', -} -NE1032 = { - 'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-64', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-32', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-32', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,input,\ - output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:1000,10000,100000,25000,40000,50000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', -} -NE1072T = { - 'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-64', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-54', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-54', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,input,\ - output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:1000,10000,100000,25000,40000,50000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', -} -NE10032 = { - 'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-64', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-32', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-32', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,input,\ - output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:10000,100000,25000,40000,50000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', -} -g8272_cnos = {'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-64', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-54', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-54', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,input,\ - output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:1000,10000,40000', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', - } -g8296_cnos = {'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-128', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-96', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-96', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,\ - arp,dhcp,ospf,port,port-unreachable,redirects,router,\ - unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,\ - input,output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:1000,10000,40000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', - } -g8332_cnos = {'vlan_id': 'INTEGER_VALUE:1-3999', - 'vlan_id_range': 'INTEGER_VALUE_RANGE:1-3999', - 'vlan_name': 'TEXT:', - 'vlan_flood': 'TEXT_OPTIONS:ipv4,ipv6', - 'vlan_state': 'TEXT_OPTIONS:active,suspend', - 'vlan_last_member_query_interval': 'INTEGER_VALUE:1-25', - 'vlan_querier': 'IPV4Address:', - 'vlan_querier_timeout': 'INTEGER_VALUE:1-65535', - 'vlan_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_query_max_response_time': 'INTEGER_VALUE:1-25', - 'vlan_report_suppression': 'INTEGER_VALUE:1-25', - 'vlan_robustness_variable': 'INTEGER_VALUE:1-7', - 'vlan_startup_query_count': 'INTEGER_VALUE:1-10', - 'vlan_startup_query_interval': 'INTEGER_VALUE:1-18000', - 'vlan_snooping_version': 'INTEGER_VALUE:2-3', - 'vlan_access_map_name': 'TEXT: ', - 'vlan_ethernet_interface': 'TEXT:', - 'vlan_portagg_number': 'INTEGER_VALUE:1-4096', - 'vlan_accessmap_action': 'TEXT_OPTIONS:drop,forward,redirect', - 'vlan_dot1q_tag': 'MATCH_TEXT_OR_EMPTY:egress-only', - 'vlan_filter_name': 'TEXT:', - 'vlag_auto_recovery': 'INTEGER_VALUE:240-3600', - 'vlag_config_consistency': 'TEXT_OPTIONS:disable,strict', - 'vlag_instance': 'INTEGER_VALUE:1-128', - 'vlag_port_aggregation': 'INTEGER_VALUE:1-4096', - 'vlag_priority': 'INTEGER_VALUE:0-65535', - 'vlag_startup_delay': 'INTEGER_VALUE:0-3600', - 'vlag_tier_id': 'INTEGER_VALUE:1-512', - 'vlag_hlthchk_options': 'TEXT_OPTIONS:keepalive-attempts,\ - keepalive-interval,peer-ip,retry-interval', - 'vlag_keepalive_attempts': 'INTEGER_VALUE:1-24', - 'vlag_keepalive_interval': 'INTEGER_VALUE:2-300', - 'vlag_retry_interval': 'INTEGER_VALUE:1-300', - 'vlag_peerip': 'IPV4Address:', - 'vlag_peerip_vrf': 'TEXT_OPTIONS:default,management', - 'bgp_as_number': 'NO_VALIDATION:1-4294967295', - 'bgp_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_bgp_local_count': 'INTEGER_VALUE:2-64', - 'cluster_id_as_ip': 'IPV4Address:', - 'cluster_id_as_number': 'NO_VALIDATION:1-4294967295', - 'confederation_identifier': 'INTEGER_VALUE:1-65535', - 'condeferation_peers_as': 'INTEGER_VALUE:1-65535', - 'stalepath_delay_value': 'INTEGER_VALUE:1-3600', - 'maxas_limit_as': 'INTEGER_VALUE:1-2000', - 'neighbor_ipaddress': 'IPV4Address:', - 'neighbor_as': 'NO_VALIDATION:1-4294967295', - 'router_id': 'IPV4Address:', - 'bgp_keepalive_interval': 'INTEGER_VALUE:0-3600', - 'bgp_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_aggregate_prefix': 'IPV4AddressWithMask:', - 'addrfamily_routemap_name': 'TEXT:', - 'reachability_half_life': 'INTEGER_VALUE:1-45', - 'start_reuse_route_value': 'INTEGER_VALUE:1-20000', - 'start_suppress_route_value': 'INTEGER_VALUE:1-20000', - 'max_duration_to_suppress_route': 'INTEGER_VALUE:1-255', - 'unreachability_halftime_for_penalty': 'INTEGER_VALUE:1-45', - 'distance_external_AS': 'INTEGER_VALUE:1-255', - 'distance_internal_AS': 'INTEGER_VALUE:1-255', - 'distance_local_routes': 'INTEGER_VALUE:1-255', - 'maxpath_option': 'TEXT_OPTIONS:ebgp,ibgp', - 'maxpath_numbers': 'INTEGER_VALUE:2-32', - 'network_ip_prefix_with_mask': 'IPV4AddressWithMask:', - 'network_ip_prefix_value': 'IPV4Address:', - 'network_ip_prefix_mask': 'IPV4Address:', - 'nexthop_crtitical_delay': 'NO_VALIDATION:1-4294967295', - 'nexthop_noncrtitical_delay': 'NO_VALIDATION:1-4294967295', - 'addrfamily_redistribute_option': 'TEXT_OPTIONS:direct,ospf,\ - static', - 'bgp_neighbor_af_occurances': 'INTEGER_VALUE:1-10', - 'bgp_neighbor_af_filtername': 'TEXT:', - 'bgp_neighbor_af_maxprefix': 'INTEGER_VALUE:1-15870', - 'bgp_neighbor_af_prefixname': 'TEXT:', - 'bgp_neighbor_af_routemap': 'TEXT:', - 'bgp_neighbor_address_family': 'TEXT_OPTIONS:ipv4,ipv6', - 'bgp_neighbor_connection_retrytime': 'INTEGER_VALUE:1-65535', - 'bgp_neighbor_description': 'TEXT:', - 'bgp_neighbor_maxhopcount': 'INTEGER_VALUE:1-255', - 'bgp_neighbor_local_as': 'NO_VALIDATION:1-4294967295', - 'bgp_neighbor_maxpeers': 'INTEGER_VALUE:1-96', - 'bgp_neighbor_password': 'TEXT:', - 'bgp_neighbor_timers_Keepalive': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_timers_holdtime': 'INTEGER_VALUE:0-3600', - 'bgp_neighbor_ttl_hops': 'INTEGER_VALUE:1-254', - 'bgp_neighbor_update_options': 'TEXT_OPTIONS:ethernet,loopback,\ - vlan', - 'bgp_neighbor_update_ethernet': 'TEXT:', - 'bgp_neighbor_update_loopback': 'INTEGER_VALUE:0-7', - 'bgp_neighbor_update_vlan': 'INTEGER_VALUE:1-4094', - 'bgp_neighbor_weight': 'INTEGER_VALUE:0-65535', - 'ethernet_interface_value': 'INTEGER_VALUE:1-32', - 'ethernet_interface_range': 'INTEGER_VALUE_RANGE:1-32', - 'ethernet_interface_string': 'TEXT:', - 'loopback_interface_value': 'INTEGER_VALUE:0-7', - 'mgmt_interface_value': 'INTEGER_VALUE:0-0', - 'vlan_interface_value': 'INTEGER_VALUE:1-4094', - 'portchannel_interface_value': 'INTEGER_VALUE:1-4096', - 'portchannel_interface_range': 'INTEGER_VALUE_RANGE:1-4096', - 'portchannel_interface_string': 'TEXT:', - 'aggregation_group_no': 'INTEGER_VALUE:1-4096', - 'aggregation_group_mode': 'TEXT_OPTIONS:active,on,passive', - 'bfd_options': 'TEXT_OPTIONS:authentication,echo,interval,ipv4,\ - ipv6,neighbor', - 'bfd_interval': 'INTEGER_VALUE:50-999', - 'bfd_minrx': 'INTEGER_VALUE:50-999', - 'bfd_ multiplier': 'INTEGER_VALUE:3-50', - 'bfd_ipv4_options': 'TEXT_OPTIONS:authentication,echo,interval', - 'bfd_auth_options': 'TEXT_OPTIONS:keyed-md5,keyed-sha1,\ - meticulous-keyed-md5,meticulous-keyed-sha1,simple', - 'bfd_key_options': 'TEXT_OPTIONS:key-chain,key-id', - 'bfd_key_chain': 'TEXT:', - 'bfd_key_id': 'INTEGER_VALUE:0-255', - 'bfd_key_name': 'TEXT:', - 'bfd_neighbor_ip': 'TEXT:', - 'bfd_neighbor_options': 'TEXT_OPTIONS:admin-down,multihop,\ - non-persistent', - 'bfd_access_vlan': 'INTEGER_VALUE:1-3999', - 'bfd_bridgeport_mode': 'TEXT_OPTIONS:access,dot1q-tunnel,trunk', - 'trunk_options': 'TEXT_OPTIONS:allowed,native', - 'trunk_vlanid': 'INTEGER_VALUE:1-3999', - 'portCh_description': 'TEXT:', - 'duplex_option': 'TEXT_OPTIONS:auto,full,half', - 'flowcontrol_options': 'TEXT_OPTIONS:receive,send', - 'portchannel_ip_options': 'TEXT_OPTIONS:access-group,address,arp,\ - dhcp,ospf,port,port-unreachable,redirects,router,unreachables', - 'accessgroup_name': 'TEXT:', - 'portchannel_ipv4': 'IPV4Address:', - 'portchannel_ipv4_mask': 'TEXT:', - 'arp_ipaddress': 'IPV4Address:', - 'arp_macaddress': 'TEXT:', - 'arp_timeout_value': 'INTEGER_VALUE:60-28800', - 'relay_ipaddress': 'IPV4Address:', - 'ip_ospf_options': 'TEXT_OPTIONS:authentication,\ - authentication-key,bfd,cost,database-filter,dead-interval,\ - hello-interval,message-digest-key,mtu,mtu-ignore,network,\ - passive-interface,priority,retransmit-interval,shutdown,\ - transmit-delay', - 'ospf_id_decimal_value': 'NO_VALIDATION:1-4294967295', - 'ospf_id_ipaddres_value': 'IPV4Address:', - 'lacp_options': 'TEXT_OPTIONS:port-priority,suspend-individual,\ - timeout', - 'port_priority': 'INTEGER_VALUE:1-65535', - 'lldp_options': 'TEXT_OPTIONS:receive,tlv-select,transmit,\ - trap-notification', - 'lldp_tlv_options': 'TEXT_OPTIONS:link-aggregation,\ - mac-phy-status,management-address,max-frame-size,\ - port-description,port-protocol-vlan,port-vlan,power-mdi,\ - protocol-identity,system-capabilities,system-description,\ - system-name,vid-management,vlan-name', - 'load_interval_delay': 'INTEGER_VALUE:30-300', - 'load_interval_counter': 'INTEGER_VALUE:1-3', - 'mac_accessgroup_name': 'TEXT:', - 'mac_address': 'TEXT:', - 'microburst_threshold': 'NO_VALIDATION:1-4294967295', - 'mtu_value': 'INTEGER_VALUE:64-9216', - 'service_instance': 'NO_VALIDATION:1-4294967295', - 'service_policy_options': 'TEXT_OPTIONS:copp-system-policy,\ - input,output,type', - 'service_policy_name': 'TEXT:', - 'spanning_tree_options': 'TEXT_OPTIONS:bpdufilter,bpduguard,\ - cost,disable,enable,guard,link-type,mst,port,port-priority,vlan', - 'spanning_tree_cost': 'NO_VALIDATION:1-200000000', - 'spanning_tree_interfacerange': 'INTEGER_VALUE_RANGE:1-3999', - 'spanning_tree_portpriority': 'TEXT_OPTIONS:0,32,64,96,128,160,\ - 192,224', - 'portchannel_ipv6_neighbor_mac': 'TEXT:', - 'portchannel_ipv6_neighbor_address': 'IPV6Address:', - 'portchannel_ipv6_linklocal': 'IPV6Address:', - 'portchannel_ipv6_dhcp_vlan': 'INTEGER_VALUE:1-4094', - 'portchannel_ipv6_dhcp_ethernet': 'TEXT:', - 'portchannel_ipv6_dhcp': 'IPV6Address:', - 'portchannel_ipv6_address': 'IPV6Address:', - 'portchannel_ipv6_options': 'TEXT_OPTIONS:address,dhcp,\ - link-local,nd,neighbor', - 'interface_speed': 'TEXT_OPTIONS:1000,10000,40000,50000,auto', - 'stormcontrol_options': 'TEXT_OPTIONS:broadcast,multicast,\ - unicast', - 'stormcontrol_level': 'FLOAT:', - 'portchannel_dot1q_tag': 'TEXT_OPTIONS:disable,enable,\ - egress-only', - 'vrrp_id': 'INTEGER_VALUE:1-255', - } diff --git a/plugins/module_utils/network/cnos/cnos_errorcodes.py b/plugins/module_utils/network/cnos/cnos_errorcodes.py deleted file mode 100644 index 3a83af00fc..0000000000 --- a/plugins/module_utils/network/cnos/cnos_errorcodes.py +++ /dev/null @@ -1,256 +0,0 @@ -# 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. -# -# Copyright (C) 2017 Lenovo, Inc. -# All rights reserved. -# -# 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. -# -# Contains error codes and methods -# Lenovo Networking - -errorDict = {0: 'Success', - 1: 'NOK', - 101: 'Device Response Timed out', - 102: 'Command Not supported - Use CLI command', - 103: 'Invalid Context', - 104: 'Command Value Not Supported as of Now. Use vlan Id only', - 105: 'Invalid interface Range', - 106: 'Please provide Enable Password.', - 108: '', - 109: '', - 110: 'Invalid protocol option', - 111: 'The Value is not Integer', - 112: 'The Value is not Float', - 113: 'Value is not in Range', - 114: 'Range value is not Integer', - 115: 'Value is not in Options', - 116: 'The Value is not Long', - 117: 'Range value is not Long', - 118: 'The Value cannot be empty', - 119: 'The Value is not String', - 120: 'The Value is not Matching', - 121: 'The Value is not IPV4 Address', - 122: 'The Value is not IPV6 Address', - 123: '', - 124: '', - 125: '', - 126: '', - 127: '', - 128: '', - 129: '', - 130: 'Invalid Access Map Name', - 131: 'Invalid Vlan Dot1q Tag', - 132: 'Invalid Vlan filter value', - 133: 'Invalid Vlan Range Value', - 134: 'Invalid Vlan Id', - 135: 'Invalid Vlan Access Map Action', - 136: 'Invalid Vlan Access Map Name', - 137: 'Invalid Access List', - 138: 'Invalid Vlan Access Map parameter', - 139: 'Invalid Vlan Name', - 140: 'Invalid Vlan Flood value,', - 141: 'Invalid Vlan State Value', - 142: 'Invalid Vlan Last Member query Interval', - 143: 'Invalid Querier IP address', - 144: 'Invalid Querier Time out', - 145: 'Invalid Query Interval', - 146: 'Invalid Vlan query max response time', - 147: 'Invalid vlan robustness variable', - 148: 'Invalid Vlan Startup Query count', - 149: 'Invalid vlan Startup Query Interval', - 150: 'Invalid Vlan snooping version', - 151: 'Invalid Vlan Ethernet Interface', - 152: 'Invalid Vlan Port Tag Number', - 153: 'Invalid mrouter option', - 154: 'Invalid Vlan Option', - 155: '', - 156: '', - 157: '', - 158: '', - 159: '', - 160: 'Invalid Vlag Auto Recovery Value', - 161: 'Invalid Vlag Config Consistency Value', - 162: 'Invalid Vlag Port Aggregation Number', - 163: 'Invalid Vlag Priority Value', - 164: 'Invalid Vlag Startup delay value', - 165: 'Invalid Vlag Trie Id', - 166: 'Invalid Vlag Instance Option', - 167: 'Invalid Vlag Keep Alive Attempts', - 168: 'Invalid Vlag Keep Alive Interval', - 169: 'Invalid Vlag Retry Interval', - 170: 'Invalid Vlag Peer Ip VRF Value', - 171: 'Invalid Vlag Health Check Options', - 172: 'Invalid Vlag Option', - 173: '', - 174: '', - 175: '', - 176: 'Invalid BGP As Number', - 177: 'Invalid Routing protocol option', - 178: 'Invalid BGP Address Family', - 179: 'Invalid AS Path options', - 180: 'Invalid BGP med options', - 181: 'Invalid Best Path option', - 182: 'Invalid BGP Local count number', - 183: 'Cluster Id has to either IP or AS Number', - 184: 'Invalid confederation identifier', - 185: 'Invalid Confederation Peer AS Value', - 186: 'Invalid Confederation Option', - 187: 'Invalid state path relay value', - 188: 'Invalid Maxas Limit AS Value', - 189: 'Invalid Neighbor IP Address or Neighbor AS Number', - 190: 'Invalid Router Id', - 191: 'Invalid BGP Keep Alive Interval', - 192: 'Invalid BGP Hold time', - 193: 'Invalid BGP Option', - 194: 'Invalid BGP Address Family option', - 195: 'Invalid BGP Address Family Redistribution option. ', - 196: 'Invalid BGP Address Family Route Map Name', - 197: 'Invalid Next Hop Critical Delay', - 198: 'Invalid Next Hop Non Critical Delay', - 199: 'Invalid Multipath Number Value', - 200: 'Invalid Aggegation Group Mode', - 201: 'Invalid Aggregation Group No', - 202: 'Invalid BFD Access Vlan', - 203: 'Invalid CFD Bridgeport Mode', - 204: 'Invalid Trunk Option', - 205: 'Invalid BFD Option', - 206: 'Invalid Portchannel description', - 207: 'Invalid Portchannel duplex option', - 208: 'Invalid Flow control option state', - 209: 'Invalid Flow control option', - 210: 'Invalid LACP Port priority', - 211: 'Invalid LACP Time out options', - 212: 'Invalid LACP Command options', - 213: 'Invalid LLDP TLV Option', - 214: 'Invalid LLDP Option', - 215: 'Invalid Load interval delay', - 216: 'Invalid Load interval Counter Number', - 217: 'Invalid Load Interval option', - 218: 'Invalid Mac Access Group Name', - 219: 'Invalid Mac Address', - 220: 'Invalid Microburst threshold value', - 221: 'Invalid MTU Value', - 222: 'Invalid Service instance value', - 223: 'Invalid service policy name', - 224: 'Invalid service policy options', - 225: 'Invalid Interface speed value', - 226: 'Invalid Storm control level value', - 227: 'Invalid Storm control option', - 228: 'Invalid Portchannel dot1q tag', - 229: 'Invalid VRRP Id Value', - 230: 'Invalid VRRP Options', - 231: 'Invalid portchannel source interface option', - 232: 'Invalid portchannel load balance options', - 233: 'Invalid Portchannel configuration attribute', - 234: 'Invalid BFD Interval Value', - 235: 'Invalid BFD minrx Value', - 236: 'Invalid BFD multiplier Value', - 237: 'Invalid Key Chain Value', - 238: 'Invalid key name option', - 239: 'Invalid key id value', - 240: 'Invalid Key Option', - 241: 'Invalid authentication option', - 242: 'Invalid destination Ip', - 243: 'Invalid source Ip', - 244: 'Invalid IP Option', - 245: 'Invalid Access group option', - 246: 'Invalid Access group name', - 247: 'Invalid ARP MacAddress Value', - 248: 'Invalid ARP timeout value', - 249: 'Invalid ARP Option', - 250: 'Invalid dhcp request option', - 251: 'Invalid dhcp Client option', - 252: 'Invalid relay Ip Address', - 253: 'Invalid dhcp Option', - 254: 'Invalid OSPF Option', - 255: 'Invalid OSPF Id IP Address Value', - 256: 'Invalid Ip Router Option', - 257: 'Invalid Spanning tree bpdufilter Options', - 258: 'Invalid Spanning tree bpduguard Options', - 259: 'Invalid Spanning tree cost Options', - 260: 'Invalid Spanning tree guard Options', - 261: 'Invalid Spanning tree link-type Options', - 262: 'Invalid Spanning tree link-type Options', - 263: 'Invalid Spanning tree options', - 264: 'Port-priority in increments of 32 is required', - 265: 'Invalid Spanning tree vlan options', - 266: 'Invalid IPv6 option', - 267: 'Invalid IPV6 neighbor IP Address', - 268: 'Invalid IPV6 neighbor mac address', - 269: 'Invalid IPV6 dhcp option', - 270: 'Invalid IPV6 relay address option', - 271: 'Invalid IPV6 Ethernet option', - 272: 'Invalid IPV6 Vlan option', - 273: 'Invalid IPV6 Link Local option', - 274: 'Invalid IPV6 dhcp option', - 275: 'Invalid IPV6 Address', - 276: 'Invalid IPV6 Address option', - 277: 'Invalid BFD neighbor options', - 278: 'Invalid Secondary option', - 289: 'Invalid PortChannel IPV4 address', - 290: 'Invalid Max Path Options', - 291: 'Invalid Distance Local Route value', - 292: 'Invalid Distance Internal AS value', - 293: 'Invalid Distance External AS value', - 294: 'Invalid BGP Reachability Half Life', - 295: 'Invalid BGP Dampening parameter', - 296: 'Invalid BGP Aggregate Prefix value', - 297: 'Invalid BGP Aggregate Prefix Option', - 298: 'Invalid BGP Address Family Route Map Name', - 299: 'Invalid BGP Net IP Mask Value', - 300: 'Invalid BGP Net IP Prefix Value', - 301: 'Invalid BGP Neighbor configuration option', - 302: 'Invalid BGP Neighbor Weight Value', - 303: 'Invalid Neigbor update source option', - 304: 'Invalid Ethernet slot/chassis number', - 305: 'Invalid Loopback Interface number', - 306: 'Invalid vlan id', - 307: 'Invalid Number of hops', - 308: 'Invalid Neighbor Keepalive interval', - 309: 'Invalid Neighbor timer hold time', - 310: 'Invalid neighbor password ', - 311: 'Invalid Max peer limit', - 312: 'Invalid Local AS Number', - 313: 'Invalid maximum hop count', - 314: 'Invalid neighbor description', - 315: 'Invalid Neighbor connect timer value', - 316: 'Invalid Neighbor address family option', - 317: 'Invalid neighbor address family option', - 318: 'Invalid route-map name', - 319: 'Invalid route-map', - 320: 'Invalid Name of a prefix list', - 321: 'Invalid Filter incoming option', - 322: 'Invalid AS path access-list name', - 323: 'Invalid Filter route option', - 324: 'Invalid route-map name', - 325: 'Invalid Number of occurrences of AS number', - 326: 'Invalid Prefix Limit'} - - -def getErrorString(errorCode): - retVal = errorDict[int(errorCode)] - return retVal -# EOM diff --git a/plugins/module_utils/network/edgeos/__init__.py b/plugins/module_utils/network/edgeos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/edgeos/edgeos.py b/plugins/module_utils/network/edgeos/edgeos.py deleted file mode 100644 index c7eef62179..0000000000 --- a/plugins/module_utils/network/edgeos/edgeos.py +++ /dev/null @@ -1,132 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2018 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -import json -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.module_utils.connection import Connection, ConnectionError - -_DEVICE_CONFIGS = None - - -def get_connection(module): - if hasattr(module, '_edgeos_connection'): - return module._edgeos_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module._edgeos_connection = Connection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type %s' % network_api) - - return module._edgeos_connection - - -def get_capabilities(module): - if hasattr(module, '_edgeos_capabilities'): - return module._edgeos_capabilities - - capabilities = Connection(module._socket_path).get_capabilities() - module._edgeos_capabilities = json.loads(capabilities) - return module._edgeos_capabilities - - -def get_config(module): - global _DEVICE_CONFIGS - - if _DEVICE_CONFIGS is not None: - return _DEVICE_CONFIGS - else: - connection = get_connection(module) - out = connection.get_config() - cfg = to_text(out, errors='surrogate_then_replace').strip() - _DEVICE_CONFIGS = cfg - return cfg - - -def run_commands(module, commands, check_rc=True): - responses = list() - connection = get_connection(module) - - for cmd in to_list(commands): - if isinstance(cmd, dict): - command = cmd['command'] - prompt = cmd['prompt'] - answer = cmd['answer'] - else: - command = cmd - prompt = None - answer = None - - try: - out = connection.get(command, prompt, answer) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - try: - out = to_text(out, errors='surrogate_or_strict') - except UnicodeError: - module.fail_json(msg=u'Failed to decode output from %s: %s' % - (cmd, to_text(out))) - - responses.append(out) - - return responses - - -def load_config(module, commands, commit=False, comment=None): - connection = get_connection(module) - - try: - out = connection.edit_config(commands) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - diff = None - if module._diff: - out = connection.get('compare') - out = to_text(out, errors='surrogate_or_strict') - - if not out.startswith('No changes'): - out = connection.get('show') - diff = to_text(out, errors='surrogate_or_strict').strip() - - if commit: - try: - out = connection.commit(comment) - except ConnectionError: - connection.discard_changes() - module.fail_json(msg='commit failed: %s' % out) - - if not commit: - connection.discard_changes() - else: - connection.get('exit') - - if diff: - return diff diff --git a/plugins/module_utils/network/edgeswitch/__init__.py b/plugins/module_utils/network/edgeswitch/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/edgeswitch/edgeswitch.py b/plugins/module_utils/network/edgeswitch/edgeswitch.py deleted file mode 100644 index 78073c2158..0000000000 --- a/plugins/module_utils/network/edgeswitch/edgeswitch.py +++ /dev/null @@ -1,168 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2018 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -import json -import re - -from copy import deepcopy - -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import Connection, ConnectionError -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - -_DEVICE_CONFIGS = {} - - -def build_aggregate_spec(element_spec, required, *extra_spec): - aggregate_spec = deepcopy(element_spec) - for elt in required: - aggregate_spec[elt] = dict(required=True) - remove_default_spec(aggregate_spec) - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec) - ) - argument_spec.update(element_spec) - for elt in extra_spec: - argument_spec.update(elt) - return argument_spec - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - d = item.copy() - obj.append(d) - else: - obj.append(module.params) - - return obj - - -def get_connection(module): - if hasattr(module, '_edgeswitch_connection'): - return module._edgeswitch_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module._edgeswitch_connection = Connection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type %s' % network_api) - - return module._edgeswitch_connection - - -def get_capabilities(module): - if hasattr(module, '_edgeswitch_capabilities'): - return module._edgeswitch_capabilities - try: - capabilities = Connection(module._socket_path).get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module._edgeswitch_capabilities = json.loads(capabilities) - return module._edgeswitch_capabilities - - -def get_defaults_flag(module): - connection = get_connection(module) - try: - out = connection.get_defaults_flag() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return to_text(out, errors='surrogate_then_replace').strip() - - -def get_config(module, flags=None): - flag_str = ' '.join(to_list(flags)) - - try: - return _DEVICE_CONFIGS[flag_str] - except KeyError: - connection = get_connection(module) - try: - out = connection.get_config(flags=flags) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - cfg = to_text(out, errors='surrogate_then_replace').strip() - _DEVICE_CONFIGS[flag_str] = cfg - return cfg - - -def get_interfaces_config(module): - config = get_config(module) - lines = config.split('\n') - interfaces = {} - interface = None - for line in lines: - if line == 'exit': - if interface: - interfaces[interface[0]] = interface - interface = None - elif interface: - interface.append(line) - else: - match = re.match(r'^interface (.*)$', line) - if match: - interface = list() - interface.append(line) - - return interfaces - - -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): - connection = get_connection(module) - try: - return connection.run_commands(commands=commands, check_rc=check_rc) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def load_config(module, commands): - connection = get_connection(module) - - try: - resp = connection.edit_config(commands) - return resp.get('response') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) diff --git a/plugins/module_utils/network/edgeswitch/edgeswitch_interface.py b/plugins/module_utils/network/edgeswitch/edgeswitch_interface.py deleted file mode 100644 index 793d0e0831..0000000000 --- a/plugins/module_utils/network/edgeswitch/edgeswitch_interface.py +++ /dev/null @@ -1,91 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2018 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -import re - - -class InterfaceConfiguration: - def __init__(self): - self.commands = [] - self.merged = False - - def has_same_commands(self, interface): - len1 = len(self.commands) - len2 = len(interface.commands) - return len1 == len2 and len1 == len(frozenset(self.commands).intersection(interface.commands)) - - -def merge_interfaces(interfaces): - """ to reduce commands generated by an edgeswitch module - we take interfaces one by one and we try to merge them with neighbors if everyone has same commands to run - """ - merged = {} - - for i, interface in interfaces.items(): - if interface.merged: - continue - interface.merged = True - - match = re.match(r'(\d+)\/(\d+)', i) - group = int(match.group(1)) - start = int(match.group(2)) - end = start - - while True: - try: - start = start - 1 - key = '{0}/{1}'.format(group, start) - neighbor = interfaces[key] - if not neighbor.merged and interface.has_same_commands(neighbor): - neighbor.merged = True - else: - break - except KeyError: - break - start = start + 1 - - while True: - try: - end = end + 1 - key = '{0}/{1}'.format(group, end) - neighbor = interfaces[key] - if not neighbor.merged and interface.has_same_commands(neighbor): - neighbor.merged = True - else: - break - except KeyError: - break - end = end - 1 - - if end == start: - key = '{0}/{1}'.format(group, start) - else: - key = '{0}/{1}-{2}/{3}'.format(group, start, group, end) - - merged[key] = interface - return merged diff --git a/plugins/module_utils/network/enos/__init__.py b/plugins/module_utils/network/enos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/enos/enos.py b/plugins/module_utils/network/enos/enos.py deleted file mode 100644 index 9cb4ba0081..0000000000 --- a/plugins/module_utils/network/enos/enos.py +++ /dev/null @@ -1,172 +0,0 @@ -# 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. -# -# Copyright (C) 2017 Lenovo. -# All rights reserved. -# -# 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. -# -# Contains utility methods -# Lenovo Networking - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, EntityCollection -from ansible.module_utils.connection import Connection, exec_command -from ansible.module_utils.connection import ConnectionError - -_DEVICE_CONFIGS = {} -_CONNECTION = None - -enos_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'), - 'context': dict(), - 'passwords': dict() -} - -enos_argument_spec = { - 'provider': dict(type='dict', options=enos_provider_spec), -} - -command_spec = { - 'command': dict(key=True), - 'prompt': dict(), - 'answer': dict() -} - - -def get_provider_argspec(): - return enos_provider_spec - - -def check_args(module, warnings): - pass - - -def get_connection(module): - global _CONNECTION - if _CONNECTION: - return _CONNECTION - _CONNECTION = Connection(module._socket_path) - - context = None - try: - context = module.params['context'] - except KeyError: - context = None - - if context: - if context == 'system': - command = 'changeto system' - else: - command = 'changeto context %s' % context - _CONNECTION.get(command) - - return _CONNECTION - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - passwords = None - try: - passwords = module.params['passwords'] - except KeyError: - passwords = None - if passwords: - cmd = 'more system:running-config' - else: - cmd = 'show running-config ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - try: - return _DEVICE_CONFIGS[cmd] - except KeyError: - conn = get_connection(module) - out = conn.get(cmd) - cfg = to_text(out, errors='surrogate_then_replace').strip() - _DEVICE_CONFIGS[cmd] = cfg - return cfg - - -def to_commands(module, commands): - if not isinstance(commands, list): - raise AssertionError('argument must be of type ') - - transform = EntityCollection(module, command_spec) - commands = transform(commands) - - for index, item in enumerate(commands): - if module.check_mode and not item['command'].startswith('show'): - module.warn('only show commands are supported when using check ' - 'mode, not executing `%s`' % item['command']) - - return commands - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - - commands = to_commands(module, to_list(commands)) - - responses = list() - - for cmd in commands: - out = connection.get(**cmd) - responses.append(to_text(out, errors='surrogate_then_replace')) - - return responses - - -def load_config(module, config): - try: - conn = get_connection(module) - conn.get('enable') - conn.edit_config(config) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def get_defaults_flag(module): - rc, out, err = exec_command(module, 'show running-config ?') - out = to_text(out, errors='surrogate_then_replace') - - commands = set() - for line in out.splitlines(): - if line: - commands.add(line.strip().split()[0]) - - if 'all' in commands: - return 'all' - else: - return 'full' diff --git a/plugins/module_utils/network/eric_eccli/__init__.py b/plugins/module_utils/network/eric_eccli/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/eric_eccli/eric_eccli.py b/plugins/module_utils/network/eric_eccli/eric_eccli.py deleted file mode 100644 index 19a526ec28..0000000000 --- a/plugins/module_utils/network/eric_eccli/eric_eccli.py +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright (c) 2019 Ericsson AB. -# 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 - -import json - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import Connection, ConnectionError - -_DEVICE_CONFIGS = {} - - -def get_connection(module): - if hasattr(module, '_eric_eccli_connection'): - return module._eric_eccli_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module._eric_eccli_connection = Connection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type %s' % network_api) - - return module._eric_eccli_connection - - -def get_capabilities(module): - if hasattr(module, '_eric_eccli_capabilities'): - return module._eric_eccli_capabilities - try: - capabilities = Connection(module._socket_path).get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module._eric_eccli_capabilities = json.loads(capabilities) - return module._eric_eccli_capabilities - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - try: - return connection.run_commands(commands=commands, check_rc=check_rc) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) diff --git a/plugins/module_utils/network/exos/__init__.py b/plugins/module_utils/network/exos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/argspec/__init__.py b/plugins/module_utils/network/exos/argspec/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/argspec/facts/__init__.py b/plugins/module_utils/network/exos/argspec/facts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/argspec/facts/facts.py b/plugins/module_utils/network/exos/argspec/facts/facts.py deleted file mode 100644 index 4ab2e934ea..0000000000 --- a/plugins/module_utils/network/exos/argspec/facts/facts.py +++ /dev/null @@ -1,23 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The arg spec for the exos facts module. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class FactsArgs(object): # pylint: disable=R0903 - """ The arg spec for the exos facts module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'gather_subset': dict(default=['!config'], type='list'), - 'gather_network_resources': dict(type='list'), - } diff --git a/plugins/module_utils/network/exos/argspec/l2_interfaces/__init__.py b/plugins/module_utils/network/exos/argspec/l2_interfaces/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/argspec/l2_interfaces/l2_interfaces.py b/plugins/module_utils/network/exos/argspec/l2_interfaces/l2_interfaces.py deleted file mode 100644 index 3c6f250811..0000000000 --- a/plugins/module_utils/network/exos/argspec/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the exos_l2_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class L2_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the exos_l2_interfaces module - """ - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'access': {'options': {'vlan': {'type': 'int'}}, - 'type': 'dict'}, - 'name': {'required': True, 'type': 'str'}, - 'trunk': {'options': {'native_vlan': {'type': 'int'}, 'trunk_allowed_vlans': {'type': 'list'}}, - 'type': 'dict'}}, - 'type': 'list'}, - 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], 'default': 'merged', 'type': 'str'} - } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/exos/argspec/lldp_global/__init__.py b/plugins/module_utils/network/exos/argspec/lldp_global/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/argspec/lldp_global/lldp_global.py b/plugins/module_utils/network/exos/argspec/lldp_global/lldp_global.py deleted file mode 100644 index 4106c53428..0000000000 --- a/plugins/module_utils/network/exos/argspec/lldp_global/lldp_global.py +++ /dev/null @@ -1,57 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The arg spec for the exos_lldp_global module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Lldp_globalArgs(object): # pylint: disable=R0903 - """The arg spec for the exos_lldp_global module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'options': { - 'interval': {'default': 30, 'type': 'int'}, - 'tlv_select': { - 'options': { - 'management_address': {'type': 'bool'}, - 'port_description': {'type': 'bool'}, - 'system_capabilities': {'type': 'bool'}, - 'system_description': { - 'default': True, - 'type': 'bool'}, - 'system_name': {'default': True, 'type': 'bool'}}, - 'type': 'dict'}}, - 'type': 'dict'}, - 'state': { - 'choices': ['merged', 'replaced', 'deleted'], - 'default': 'merged', - 'type': 'str'}} # pylint: disable=C0301 diff --git a/plugins/module_utils/network/exos/argspec/lldp_interfaces/__init__.py b/plugins/module_utils/network/exos/argspec/lldp_interfaces/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/argspec/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/exos/argspec/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index c2a981f919..0000000000 --- a/plugins/module_utils/network/exos/argspec/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,49 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The arg spec for the exos_lldp_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Lldp_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the exos_lldp_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'enabled': {'type': 'bool'}, - 'name': {'required': True, 'type': 'str'}}, - 'type': 'list'}, - 'state': { - 'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str'}} # pylint: disable=C0301 diff --git a/plugins/module_utils/network/exos/argspec/vlans/__init__.py b/plugins/module_utils/network/exos/argspec/vlans/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/argspec/vlans/vlans.py b/plugins/module_utils/network/exos/argspec/vlans/vlans.py deleted file mode 100644 index 538a155a7d..0000000000 --- a/plugins/module_utils/network/exos/argspec/vlans/vlans.py +++ /dev/null @@ -1,53 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The arg spec for the exos_vlans module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class VlansArgs(object): # pylint: disable=R0903 - """The arg spec for the exos_vlans module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'name': {'type': 'str'}, - 'state': { - 'choices': ['active', 'suspend'], - 'default': 'active', - 'type': 'str'}, - 'vlan_id': {'required': True, 'type': 'int'}}, - 'type': 'list'}, - 'state': { - 'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str'}} # pylint: disable=C0301 diff --git a/plugins/module_utils/network/exos/config/__init__.py b/plugins/module_utils/network/exos/config/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/config/l2_interfaces/__init__.py b/plugins/module_utils/network/exos/config/l2_interfaces/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/config/l2_interfaces/l2_interfaces.py b/plugins/module_utils/network/exos/config/l2_interfaces/l2_interfaces.py deleted file mode 100644 index 51f8951db2..0000000000 --- a/plugins/module_utils/network/exos/config/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,294 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The exos_l2_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import json -from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ConfigBase -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, dict_diff -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.facts import Facts -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import send_requests - - -class L2_interfaces(ConfigBase): - """ - The exos_l2_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'l2_interfaces', - ] - - L2_INTERFACE_NATIVE = { - "data": { - "openconfig-vlan:config": { - "interface-mode": "TRUNK", - "native-vlan": None, - "trunk-vlans": [] - } - }, - "method": "PATCH", - "path": None - } - - L2_INTERFACE_TRUNK = { - "data": { - "openconfig-vlan:config": { - "interface-mode": "TRUNK", - "trunk-vlans": [] - } - }, - "method": "PATCH", - "path": None - } - - L2_INTERFACE_ACCESS = { - "data": { - "openconfig-vlan:config": { - "interface-mode": "ACCESS", - "access-vlan": None - } - }, - "method": "PATCH", - "path": None - } - - L2_PATH = "/rest/restconf/data/openconfig-interfaces:interfaces/interface=" - - def __init__(self, module): - super(L2_interfaces, self).__init__(module) - - def get_l2_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources) - l2_interfaces_facts = facts['ansible_network_resources'].get( - 'l2_interfaces') - if not l2_interfaces_facts: - return [] - return l2_interfaces_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - requests = list() - - existing_l2_interfaces_facts = self.get_l2_interfaces_facts() - requests.extend(self.set_config(existing_l2_interfaces_facts)) - if requests: - if not self._module.check_mode: - send_requests(self._module, requests=requests) - result['changed'] = True - result['requests'] = requests - - changed_l2_interfaces_facts = self.get_l2_interfaces_facts() - - result['before'] = existing_l2_interfaces_facts - if result['changed']: - result['after'] = changed_l2_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_l2_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - want = self._module.params['config'] - have = existing_l2_interfaces_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state == 'overridden': - requests = self._state_overridden(want, have) - elif state == 'deleted': - requests = self._state_deleted(want, have) - elif state == 'merged': - requests = self._state_merged(want, have) - elif state == 'replaced': - requests = self._state_replaced(want, have) - return requests - - def _state_replaced(self, want, have): - """ The request generator when state is replaced - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - requests = [] - for w in want: - for h in have: - if w["name"] == h["name"]: - if dict_diff(w, h): - l2_request = self._update_patch_request(w, h) - l2_request["data"] = json.dumps(l2_request["data"]) - requests.append(l2_request) - break - - return requests - - def _state_overridden(self, want, have): - """ The request generator when state is overridden - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - requests = [] - have_copy = [] - for w in want: - for h in have: - if w["name"] == h["name"]: - if dict_diff(w, h): - l2_request = self._update_patch_request(w, h) - l2_request["data"] = json.dumps(l2_request["data"]) - requests.append(l2_request) - have_copy.append(h) - break - - for h in have: - if h not in have_copy: - l2_delete = self._update_delete_request(h) - if l2_delete["path"]: - l2_delete["data"] = json.dumps(l2_delete["data"]) - requests.append(l2_delete) - - return requests - - def _state_merged(self, want, have): - """ The request generator when state is merged - - :rtype: A list - :returns: the requests necessary to merge the provided into - the current configuration - """ - requests = [] - for w in want: - for h in have: - if w["name"] == h["name"]: - if dict_diff(h, w): - l2_request = self._update_patch_request(w, h) - l2_request["data"] = json.dumps(l2_request["data"]) - requests.append(l2_request) - break - - return requests - - def _state_deleted(self, want, have): - """ The request generator when state is deleted - - :rtype: A list - :returns: the requests necessary to remove the current configuration - of the provided objects - """ - requests = [] - if want: - for w in want: - for h in have: - if w["name"] == h["name"]: - l2_delete = self._update_delete_request(h) - if l2_delete["path"]: - l2_delete["data"] = json.dumps(l2_delete["data"]) - requests.append(l2_delete) - break - - else: - for h in have: - l2_delete = self._update_delete_request(h) - if l2_delete["path"]: - l2_delete["data"] = json.dumps(l2_delete["data"]) - requests.append(l2_delete) - - return requests - - def _update_patch_request(self, want, have): - - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, ['vlans', ]) - vlans_facts = facts['ansible_network_resources'].get('vlans') - - vlan_id = [] - - for vlan in vlans_facts: - vlan_id.append(vlan['vlan_id']) - - if want.get("access"): - if want["access"]["vlan"] in vlan_id: - l2_request = deepcopy(self.L2_INTERFACE_ACCESS) - l2_request["data"]["openconfig-vlan:config"]["access-vlan"] = want["access"]["vlan"] - l2_request["path"] = self.L2_PATH + str(want["name"]) + "/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" - else: - self._module.fail_json(msg="VLAN %s does not exist" % (want["access"]["vlan"])) - - elif want.get("trunk"): - if want["trunk"]["native_vlan"]: - if want["trunk"]["native_vlan"] in vlan_id: - l2_request = deepcopy(self.L2_INTERFACE_NATIVE) - l2_request["data"]["openconfig-vlan:config"]["native-vlan"] = want["trunk"]["native_vlan"] - l2_request["path"] = self.L2_PATH + str(want["name"]) + "/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" - for vlan in want["trunk"]["trunk_allowed_vlans"]: - if int(vlan) in vlan_id: - l2_request["data"]["openconfig-vlan:config"]["trunk-vlans"].append(int(vlan)) - else: - self._module.fail_json(msg="VLAN %s does not exist" % (vlan)) - else: - self._module.fail_json(msg="VLAN %s does not exist" % (want["trunk"]["native_vlan"])) - else: - l2_request = deepcopy(self.L2_INTERFACE_TRUNK) - l2_request["path"] = self.L2_PATH + str(want["name"]) + "/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" - for vlan in want["trunk"]["trunk_allowed_vlans"]: - if int(vlan) in vlan_id: - l2_request["data"]["openconfig-vlan:config"]["trunk-vlans"].append(int(vlan)) - else: - self._module.fail_json(msg="VLAN %s does not exist" % (vlan)) - return l2_request - - def _update_delete_request(self, have): - - l2_request = deepcopy(self.L2_INTERFACE_ACCESS) - - if have["access"] and have["access"]["vlan"] != 1 or have["trunk"] or not have["access"]: - l2_request["data"]["openconfig-vlan:config"]["access-vlan"] = 1 - l2_request["path"] = self.L2_PATH + str(have["name"]) + "/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" - - return l2_request diff --git a/plugins/module_utils/network/exos/config/lldp_global/__init__.py b/plugins/module_utils/network/exos/config/lldp_global/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/config/lldp_global/lldp_global.py b/plugins/module_utils/network/exos/config/lldp_global/lldp_global.py deleted file mode 100644 index 0bac6bf505..0000000000 --- a/plugins/module_utils/network/exos/config/lldp_global/lldp_global.py +++ /dev/null @@ -1,199 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The exos_lldp_global class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ConfigBase -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.facts import Facts -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import send_requests - -import json -from copy import deepcopy - - -class Lldp_global(ConfigBase): - """ - The exos_lldp_global class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lldp_global', - ] - - LLDP_DEFAULT_INTERVAL = 30 - LLDP_DEFAULT_TLV = { - 'system_name': True, - 'system_description': True, - 'system_capabilities': False, - 'port_description': False, - 'management_address': False - } - LLDP_REQUEST = { - "data": {"openconfig-lldp:config": {}}, - "method": "PUT", - "path": "/rest/restconf/data/openconfig-lldp:lldp/config" - } - - def __init__(self, module): - super(Lldp_global, self).__init__(module) - - def get_lldp_global_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources) - lldp_global_facts = facts['ansible_network_resources'].get('lldp_global') - if not lldp_global_facts: - return {} - return lldp_global_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - requests = list() - - existing_lldp_global_facts = self.get_lldp_global_facts() - requests.extend(self.set_config(existing_lldp_global_facts)) - if requests: - if not self._module.check_mode: - send_requests(self._module, requests) - result['changed'] = True - result['requests'] = requests - - changed_lldp_global_facts = self.get_lldp_global_facts() - - result['before'] = existing_lldp_global_facts - if result['changed']: - result['after'] = changed_lldp_global_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_lldp_global_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - want = self._module.params['config'] - have = existing_lldp_global_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - - if state == 'deleted': - requests = self._state_deleted(want, have) - elif state == 'merged': - requests = self._state_merged(want, have) - elif state == 'replaced': - requests = self._state_replaced(want, have) - - return requests - - def _state_replaced(self, want, have): - """ The request generator when state is replaced - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - requests = [] - requests.extend(self._state_deleted(want, have)) - requests.extend(self._state_merged(want, have)) - return requests - - def _state_merged(self, want, have): - """ The request generator when state is merged - - :rtype: A list - :returns: the requests necessary to merge the provided into - the current configuration - """ - requests = [] - - request = deepcopy(self.LLDP_REQUEST) - self._update_lldp_config_body_if_diff(want, have, request) - - if len(request["data"]["openconfig-lldp:config"]): - request["data"] = json.dumps(request["data"]) - requests.append(request) - - return requests - - def _state_deleted(self, want, have): - """ The request generator when state is deleted - - :rtype: A list - :returns: the requests necessary to remove the current configuration - of the provided objects - """ - requests = [] - - request = deepcopy(self.LLDP_REQUEST) - if want: - self._update_lldp_config_body_if_diff(want, have, request) - else: - if self.LLDP_DEFAULT_INTERVAL != have['interval']: - request["data"]["openconfig-lldp:config"].update( - {"hello-timer": self.LLDP_DEFAULT_INTERVAL}) - - if have['tlv_select'] != self.LLDP_DEFAULT_TLV: - request["data"]["openconfig-lldp:config"].update( - {"suppress-tlv-advertisement": [key.upper() for key, value in self.LLDP_DEFAULT_TLV.items() if not value]}) - request["data"]["openconfig-lldp:config"]["suppress-tlv-advertisement"].sort() - if len(request["data"]["openconfig-lldp:config"]): - request["data"] = json.dumps(request["data"]) - requests.append(request) - - return requests - - def _update_lldp_config_body_if_diff(self, want, have, request): - if want.get('interval'): - if want['interval'] != have['interval']: - request["data"]["openconfig-lldp:config"].update( - {"hello-timer": want['interval']}) - if want.get('tlv_select'): - # Create list of TLVs to be suppressed which aren't already - want_suppress = [key.upper() for key, value in want["tlv_select"].items() if have["tlv_select"][key] != value and value is False] - if want_suppress: - # Add previously suppressed TLVs to the list as we are doing a PUT op - want_suppress.extend([key.upper() for key, value in have["tlv_select"].items() if value is False]) - request["data"]["openconfig-lldp:config"].update( - {"suppress-tlv-advertisement": want_suppress}) - request["data"]["openconfig-lldp:config"]["suppress-tlv-advertisement"].sort() diff --git a/plugins/module_utils/network/exos/config/lldp_interfaces/__init__.py b/plugins/module_utils/network/exos/config/lldp_interfaces/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/config/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/exos/config/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index 6b81806b6a..0000000000 --- a/plugins/module_utils/network/exos/config/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,243 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The exos_lldp_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import json -from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ConfigBase -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, dict_diff -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.facts import Facts -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import send_requests - - -class Lldp_interfaces(ConfigBase): - """ - The exos_lldp_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lldp_interfaces', - ] - - LLDP_INTERFACE = { - "data": { - "openconfig-lldp:config": { - "name": None, - "enabled": True - } - }, - "method": "PATCH", - "path": None - } - - LLDP_PATH = "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=" - - def __init__(self, module): - super(Lldp_interfaces, self).__init__(module) - - def get_lldp_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources) - lldp_interfaces_facts = facts['ansible_network_resources'].get( - 'lldp_interfaces') - if not lldp_interfaces_facts: - return [] - return lldp_interfaces_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - requests = list() - - existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() - requests.extend(self.set_config(existing_lldp_interfaces_facts)) - if requests: - if not self._module.check_mode: - send_requests(self._module, requests=requests) - result['changed'] = True - result['requests'] = requests - - changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() - - result['before'] = existing_lldp_interfaces_facts - if result['changed']: - result['after'] = changed_lldp_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_lldp_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - want = self._module.params['config'] - have = existing_lldp_interfaces_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state == 'overridden': - requests = self._state_overridden(want, have) - elif state == 'deleted': - requests = self._state_deleted(want, have) - elif state == 'merged': - requests = self._state_merged(want, have) - elif state == 'replaced': - requests = self._state_replaced(want, have) - return requests - - def _state_replaced(self, want, have): - """ The request generator when state is replaced - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - requests = [] - - for w in want: - for h in have: - if w['name'] == h['name']: - lldp_request = self._update_patch_request(w, h) - if lldp_request["path"]: - lldp_request["data"] = json.dumps(lldp_request["data"]) - requests.append(lldp_request) - - return requests - - def _state_overridden(self, want, have): - """ The request generator when state is overridden - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - requests = [] - have_copy = [] - for w in want: - for h in have: - if w['name'] == h['name']: - lldp_request = self._update_patch_request(w, h) - if lldp_request["path"]: - lldp_request["data"] = json.dumps(lldp_request["data"]) - requests.append(lldp_request) - have_copy.append(h) - - for h in have: - if h not in have_copy: - if not h['enabled']: - lldp_delete = self._update_delete_request(h) - if lldp_delete["path"]: - lldp_delete["data"] = json.dumps(lldp_delete["data"]) - requests.append(lldp_delete) - - return requests - - def _state_merged(self, want, have): - """ The request generator when state is merged - - :rtype: A list - :returns: the requests necessary to merge the provided into - the current configuration - """ - requests = [] - for w in want: - for h in have: - if w['name'] == h['name']: - lldp_request = self._update_patch_request(w, h) - if lldp_request["path"]: - lldp_request["data"] = json.dumps(lldp_request["data"]) - requests.append(lldp_request) - - return requests - - def _state_deleted(self, want, have): - """ The request generator when state is deleted - - :rtype: A list - :returns: the requests necessary to remove the current configuration - of the provided objects - """ - requests = [] - if want: - for w in want: - for h in have: - if w['name'] == h['name']: - if not h['enabled']: - lldp_delete = self._update_delete_request(h) - if lldp_delete["path"]: - lldp_delete["data"] = json.dumps( - lldp_delete["data"]) - requests.append(lldp_delete) - else: - for h in have: - if not h['enabled']: - lldp_delete = self._update_delete_request(h) - if lldp_delete["path"]: - lldp_delete["data"] = json.dumps(lldp_delete["data"]) - requests.append(lldp_delete) - - return requests - - def _update_patch_request(self, want, have): - - lldp_request = deepcopy(self.LLDP_INTERFACE) - - if have['enabled'] != want['enabled']: - lldp_request["data"]["openconfig-lldp:config"]["name"] = want[ - 'name'] - lldp_request["data"]["openconfig-lldp:config"]["enabled"] = want[ - 'enabled'] - lldp_request["path"] = self.LLDP_PATH + str( - want['name']) + "/config" - - return lldp_request - - def _update_delete_request(self, have): - - lldp_delete = deepcopy(self.LLDP_INTERFACE) - - lldp_delete["data"]["openconfig-lldp:config"]["name"] = have['name'] - lldp_delete["data"]["openconfig-lldp:config"]["enabled"] = True - lldp_delete["path"] = self.LLDP_PATH + str(have['name']) + "/config" - - return lldp_delete diff --git a/plugins/module_utils/network/exos/config/vlans/__init__.py b/plugins/module_utils/network/exos/config/vlans/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/config/vlans/vlans.py b/plugins/module_utils/network/exos/config/vlans/vlans.py deleted file mode 100644 index bd4c102025..0000000000 --- a/plugins/module_utils/network/exos/config/vlans/vlans.py +++ /dev/null @@ -1,277 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The exos_vlans class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import json -from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ConfigBase -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, dict_diff -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.facts import Facts -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import send_requests -from ansible_collections.community.general.plugins.module_utils.network.exos.utils.utils import search_obj_in_list - - -class Vlans(ConfigBase): - """ - The exos_vlans class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'vlans', - ] - - VLAN_POST = { - "data": {"openconfig-vlan:vlans": []}, - "method": "POST", - "path": "/rest/restconf/data/openconfig-vlan:vlans/" - } - - VLAN_PATCH = { - "data": {"openconfig-vlan:vlans": {"vlan": []}}, - "method": "PATCH", - "path": "/rest/restconf/data/openconfig-vlan:vlans/" - } - - VLAN_DELETE = { - "method": "DELETE", - "path": None - } - - DEL_PATH = "/rest/restconf/data/openconfig-vlan:vlans/vlan=" - - REQUEST_BODY = { - "config": {"name": None, "status": "ACTIVE", "tpid": "oc-vlan-types:TPID_0x8100", "vlan-id": None} - } - - def __init__(self, module): - super(Vlans, self).__init__(module) - - def get_vlans_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources) - vlans_facts = facts['ansible_network_resources'].get('vlans') - if not vlans_facts: - return [] - return vlans_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - requests = list() - - existing_vlans_facts = self.get_vlans_facts() - requests.extend(self.set_config(existing_vlans_facts)) - if requests: - if not self._module.check_mode: - send_requests(self._module, requests=requests) - result['changed'] = True - result['requests'] = requests - - changed_vlans_facts = self.get_vlans_facts() - - result['before'] = existing_vlans_facts - if result['changed']: - result['after'] = changed_vlans_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_vlans_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - want = self._module.params['config'] - have = existing_vlans_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state == 'overridden': - requests = self._state_overridden(want, have) - elif state == 'deleted': - requests = self._state_deleted(want, have) - elif state == 'merged': - requests = self._state_merged(want, have) - elif state == 'replaced': - requests = self._state_replaced(want, have) - return requests - - def _state_replaced(self, want, have): - """ The request generator when state is replaced - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - requests = [] - request_patch = deepcopy(self.VLAN_PATCH) - - for w in want: - if w.get('vlan_id'): - h = search_obj_in_list(w['vlan_id'], have, 'vlan_id') - if h: - if dict_diff(w, h): - request_body = self._update_patch_request(w) - request_patch["data"]["openconfig-vlan:vlans"]["vlan"].append(request_body) - else: - request_post = self._update_post_request(w) - requests.append(request_post) - - if len(request_patch["data"]["openconfig-vlan:vlans"]["vlan"]): - request_patch["data"] = json.dumps(request_patch["data"]) - requests.append(request_patch) - - return requests - - def _state_overridden(self, want, have): - """ The request generator when state is overridden - - :rtype: A list - :returns: the requests necessary to migrate the current configuration - to the desired configuration - """ - requests = [] - request_patch = deepcopy(self.VLAN_PATCH) - - have_copy = [] - for w in want: - if w.get('vlan_id'): - h = search_obj_in_list(w['vlan_id'], have, 'vlan_id') - if h: - if dict_diff(w, h): - request_body = self._update_patch_request(w) - request_patch["data"]["openconfig-vlan:vlans"]["vlan"].append(request_body) - have_copy.append(h) - else: - request_post = self._update_post_request(w) - requests.append(request_post) - - for h in have: - if h not in have_copy and h['vlan_id'] != 1: - request_delete = self._update_delete_request(h) - requests.append(request_delete) - - if len(request_patch["data"]["openconfig-vlan:vlans"]["vlan"]): - request_patch["data"] = json.dumps(request_patch["data"]) - requests.append(request_patch) - - return requests - - def _state_merged(self, want, have): - """ The requests generator when state is merged - - :rtype: A list - :returns: the requests necessary to merge the provided into - the current configuration - """ - requests = [] - - request_patch = deepcopy(self.VLAN_PATCH) - - for w in want: - if w.get('vlan_id'): - h = search_obj_in_list(w['vlan_id'], have, 'vlan_id') - if h: - if dict_diff(w, h): - request_body = self._update_patch_request(w) - request_patch["data"]["openconfig-vlan:vlans"]["vlan"].append(request_body) - else: - request_post = self._update_post_request(w) - requests.append(request_post) - - if len(request_patch["data"]["openconfig-vlan:vlans"]["vlan"]): - request_patch["data"] = json.dumps(request_patch["data"]) - requests.append(request_patch) - return requests - - def _state_deleted(self, want, have): - """ The requests generator when state is deleted - - :rtype: A list - :returns: the requests necessary to remove the current configuration - of the provided objects - """ - requests = [] - - if want: - for w in want: - if w.get('vlan_id'): - h = search_obj_in_list(w['vlan_id'], have, 'vlan_id') - if h: - request_delete = self._update_delete_request(h) - requests.append(request_delete) - - else: - if not have: - return requests - for h in have: - if h['vlan_id'] == 1: - continue - else: - request_delete = self._update_delete_request(h) - requests.append(request_delete) - - return requests - - def _update_vlan_config_body(self, want, request): - request["config"]["name"] = want["name"] - request["config"]["status"] = "SUSPENDED" if want["state"] == "suspend" else want["state"].upper() - request["config"]["vlan-id"] = want["vlan_id"] - return request - - def _update_patch_request(self, want): - request_body = deepcopy(self.REQUEST_BODY) - request_body = self._update_vlan_config_body(want, request_body) - return request_body - - def _update_post_request(self, want): - request_post = deepcopy(self.VLAN_POST) - request_body = deepcopy(self.REQUEST_BODY) - request_body = self._update_vlan_config_body(want, request_body) - request_post["data"]["openconfig-vlan:vlans"].append(request_body) - request_post["data"] = json.dumps(request_post["data"]) - return request_post - - def _update_delete_request(self, have): - request_delete = deepcopy(self.VLAN_DELETE) - request_delete["path"] = self.DEL_PATH + str(have['vlan_id']) - return request_delete diff --git a/plugins/module_utils/network/exos/exos.py b/plugins/module_utils/network/exos/exos.py deleted file mode 100644 index f7f70ae3e3..0000000000 --- a/plugins/module_utils/network/exos/exos.py +++ /dev/null @@ -1,219 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2016 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -import json -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.common._collections_compat import Mapping -from ansible.module_utils.connection import Connection, ConnectionError - -_DEVICE_CONNECTION = None - - -class Cli: - def __init__(self, module): - self._module = module - self._device_configs = {} - self._connection = None - - def get_capabilities(self): - """Returns platform info of the remove device - """ - connection = self._get_connection() - return json.loads(connection.get_capabilities()) - - def _get_connection(self): - if not self._connection: - self._connection = Connection(self._module._socket_path) - return self._connection - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - if self._device_configs == {}: - connection = self._get_connection() - try: - out = connection.get_config(flags=flags) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - self._device_configs = to_text(out, errors='surrogate_then_replace').strip() - return self._device_configs - - def run_commands(self, commands, check_rc=True): - """Runs list of commands on remote device and returns results - """ - connection = self._get_connection() - try: - response = connection.run_commands(commands=commands, check_rc=check_rc) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return response - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - conn = self._get_connection() - try: - diff = conn.get_diff(candidate=candidate, running=running, diff_match=diff_match, - diff_ignore_lines=diff_ignore_lines, path=path, diff_replace=diff_replace) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return diff - - -class HttpApi: - def __init__(self, module): - self._module = module - self._device_configs = {} - self._connection_obj = None - - def get_capabilities(self): - """Returns platform info of the remove device - """ - try: - capabilities = self._connection.get_capabilities() - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - return json.loads(capabilities) - - @property - def _connection(self): - if not self._connection_obj: - self._connection_obj = Connection(self._module._socket_path) - return self._connection_obj - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - if self._device_configs == {}: - try: - out = self._connection.get_config(flags=flags) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - self._device_configs = to_text(out, errors='surrogate_then_replace').strip() - return self._device_configs - - def run_commands(self, commands, check_rc=True): - """Runs list of commands on remote device and returns results - """ - try: - response = self._connection.run_commands(commands=commands, check_rc=check_rc) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return response - - def send_requests(self, requests): - """Send a list of http requests to remote device and return results - """ - if requests is None: - raise ValueError("'requests' value is required") - - responses = list() - for req in to_list(requests): - try: - response = self._connection.send_request(**req) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - responses.append(response) - return responses - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - try: - diff = self._connection.get_diff(candidate=candidate, running=running, diff_match=diff_match, - diff_ignore_lines=diff_ignore_lines, path=path, diff_replace=diff_replace) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return diff - - -def get_capabilities(module): - conn = get_connection(module) - return conn.get_capabilities() - - -def get_connection(module): - global _DEVICE_CONNECTION - if not _DEVICE_CONNECTION: - connection_proxy = Connection(module._socket_path) - cap = json.loads(connection_proxy.get_capabilities()) - if cap['network_api'] == 'cliconf': - conn = Cli(module) - elif cap['network_api'] == 'exosapi': - conn = HttpApi(module) - else: - module.fail_json(msg='Invalid connection type %s' % cap['network_api']) - _DEVICE_CONNECTION = conn - return _DEVICE_CONNECTION - - -def get_config(module, flags=None): - flags = None if flags is None else flags - conn = get_connection(module) - return conn.get_config(flags) - - -def load_config(module, commands): - conn = get_connection(module) - return conn.run_commands(to_command(module, commands)) - - -def run_commands(module, commands, check_rc=True): - conn = get_connection(module) - return conn.run_commands(to_command(module, commands), check_rc=check_rc) - - -def to_command(module, commands): - transform = ComplexList(dict( - command=dict(key=True), - output=dict(default='text'), - prompt=dict(type='list'), - answer=dict(type='list'), - sendonly=dict(type='bool', default=False), - check_all=dict(type='bool', default=False), - ), module) - return transform(to_list(commands)) - - -def send_requests(module, requests): - conn = get_connection(module) - return conn.send_requests(to_request(module, requests)) - - -def to_request(module, requests): - transform = ComplexList(dict( - path=dict(key=True), - method=dict(), - data=dict(type='dict'), - ), module) - return transform(to_list(requests)) - - -def get_diff(module, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - conn = get_connection(module) - return conn.get_diff(candidate=candidate, running=running, diff_match=diff_match, diff_ignore_lines=diff_ignore_lines, path=path, diff_replace=diff_replace) diff --git a/plugins/module_utils/network/exos/facts/__init__.py b/plugins/module_utils/network/exos/facts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/facts/facts.py b/plugins/module_utils/network/exos/facts/facts.py deleted file mode 100644 index b9b058304f..0000000000 --- a/plugins/module_utils/network/exos/facts/facts.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The facts class for exos -this file validates each subset of facts and selectively -calls the appropriate facts gathering function -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.facts.facts import FactsArgs -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import FactsBase -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.lldp_global.lldp_global import Lldp_globalFacts -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.vlans.vlans import VlansFacts -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.legacy.base import Default, Hardware, Interfaces, Config -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.l2_interfaces.l2_interfaces import L2_interfacesFacts - -FACT_LEGACY_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config) - -FACT_RESOURCE_SUBSETS = dict( - lldp_global=Lldp_globalFacts, - vlans=VlansFacts, - lldp_interfaces=Lldp_interfacesFacts, - l2_interfaces=L2_interfacesFacts, -) - - -class Facts(FactsBase): - """ The fact class for exos - """ - - VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys()) - VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys()) - - def __init__(self, module): - super(Facts, self).__init__(module) - - def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None): - """ Collect the facts for exos - - :param legacy_facts_type: List of legacy facts types - :param resource_facts_type: List of resource fact types - :param data: previously collected conf - :rtype: dict - :return: the facts gathered - """ - if self.VALID_RESOURCE_SUBSETS: - self.get_network_resources_facts(FACT_RESOURCE_SUBSETS, resource_facts_type, data) - - if self.VALID_LEGACY_GATHER_SUBSETS: - self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type) - - return self.ansible_facts, self._warnings diff --git a/plugins/module_utils/network/exos/facts/l2_interfaces/__init__.py b/plugins/module_utils/network/exos/facts/l2_interfaces/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/facts/l2_interfaces/l2_interfaces.py b/plugins/module_utils/network/exos/facts/l2_interfaces/l2_interfaces.py deleted file mode 100644 index bbe9ab4402..0000000000 --- a/plugins/module_utils/network/exos/facts/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,92 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The exos l2_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.l2_interfaces.l2_interfaces import L2_interfacesArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import send_requests - - -class L2_interfacesFacts(object): - """ The exos l2_interfaces fact class - """ - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = L2_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for l2_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - - if not data: - request = [{ - "path": "/rest/restconf/data/openconfig-interfaces:interfaces", - "method": "GET" - }] - data = send_requests(self._module, requests=request) - - objs = [] - if data: - for d in data[0]["openconfig-interfaces:interfaces"]["interface"]: - obj = self.render_config(self.generated_spec, d) - if obj: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('l2_interfaces', None) - facts = {} - if objs: - params = utils.validate_config(self.argument_spec, {'config': objs}) - facts['l2_interfaces'] = params['config'] - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - if conf["config"]["type"] == "ethernetCsmacd": - conf_dict = conf["openconfig-if-ethernet:ethernet"]["openconfig-vlan:switched-vlan"]["config"] - config["name"] = conf["name"] - if conf_dict["interface-mode"] == "ACCESS": - config["access"]["vlan"] = conf_dict.get("access-vlan") - else: - if 'native-vlan' in conf_dict: - config["trunk"]["native_vlan"] = conf_dict.get("native-vlan") - config["trunk"]["trunk_allowed_vlans"] = conf_dict.get("trunk-vlans") - return utils.remove_empties(config) diff --git a/plugins/module_utils/network/exos/facts/legacy/__init__.py b/plugins/module_utils/network/exos/facts/legacy/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/facts/legacy/base.py b/plugins/module_utils/network/exos/facts/legacy/base.py deleted file mode 100644 index c913350973..0000000000 --- a/plugins/module_utils/network/exos/facts/legacy/base.py +++ /dev/null @@ -1,263 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -""" -The exos legacy fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -import re -import json - -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.warnings = list() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, self.COMMANDS) - - def run(self, cmd): - return run_commands(self.module, cmd) - - -class Default(FactsBase): - - COMMANDS = [ - 'show version', - 'show switch' - ] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - - data = self.responses[1] - if data: - self.facts['model'] = self.parse_model(data) - self.facts['hostname'] = self.parse_hostname(data) - - def parse_version(self, data): - match = re.search(r'Image\s+: ExtremeXOS version (\S+)', data) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'System Type:\s+(.*$)', data, re.M) - if match: - return match.group(1) - - def parse_hostname(self, data): - match = re.search(r'SysName:\s+(\S+)', data, re.M) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'Switch\s+: \S+ (\S+)', data, re.M) - if match: - return match.group(1) - # For stack, return serial number of the first switch in the stack. - match = re.search(r'Slot-\d+\s+: \S+ (\S+)', data, re.M) - if match: - return match.group(1) - # Handle unique formatting for VM - match = re.search(r'Switch\s+: PN:\S+\s+SN:(\S+)', data, re.M) - if match: - return match.group(1) - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show memory' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - if data: - self.facts['memtotal_mb'] = int(round(int(self.parse_memtotal(data)) / 1024, 0)) - self.facts['memfree_mb'] = int(round(int(self.parse_memfree(data)) / 1024, 0)) - - def parse_memtotal(self, data): - match = re.search(r' Total DRAM \(KB\): (\d+)', data, re.M) - if match: - return match.group(1) - # Handle unique formatting for VM - match = re.search(r' Total \s+\(KB\): (\d+)', data, re.M) - if match: - return match.group(1) - - def parse_memfree(self, data): - match = re.search(r' Free\s+\(KB\): (\d+)', data, re.M) - if match: - return match.group(1) - - -class Config(FactsBase): - - COMMANDS = ['show configuration detail'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'show switch', - {'command': 'show port config', 'output': 'json'}, - {'command': 'show port description', 'output': 'json'}, - {'command': 'show vlan detail', 'output': 'json'}, - {'command': 'show lldp neighbors', 'output': 'json'} - ] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - data = self.responses[0] - if data: - sysmac = self.parse_sysmac(data) - - data = self.responses[1] - if data: - self.facts['interfaces'] = self.populate_interfaces(data, sysmac) - - data = self.responses[2] - if data: - self.populate_interface_descriptions(data) - - data = self.responses[3] - if data: - self.populate_vlan_interfaces(data, sysmac) - - data = self.responses[4] - if data: - self.facts['neighbors'] = self.parse_neighbors(data) - - def parse_sysmac(self, data): - match = re.search(r'System MAC:\s+(\S+)', data, re.M) - if match: - return match.group(1) - - def populate_interfaces(self, interfaces, sysmac): - facts = dict() - for elem in interfaces: - intf = dict() - - if 'show_ports_config' not in elem: - continue - - key = str(elem['show_ports_config']['port']) - - if elem['show_ports_config']['linkState'] == 2: - # Link state is "not present", don't include - continue - - intf['type'] = 'Ethernet' - intf['macaddress'] = sysmac - intf['bandwidth_configured'] = str(elem['show_ports_config']['speedCfg']) - intf['bandwidth'] = str(elem['show_ports_config']['speedActual']) - intf['duplex_configured'] = elem['show_ports_config']['duplexCfg'] - intf['duplex'] = elem['show_ports_config']['duplexActual'] - if elem['show_ports_config']['linkState'] == 1: - intf['lineprotocol'] = 'up' - else: - intf['lineprotocol'] = 'down' - if elem['show_ports_config']['portState'] == 1: - intf['operstatus'] = 'up' - else: - intf['operstatus'] = 'admin down' - - facts[key] = intf - return facts - - def populate_interface_descriptions(self, data): - for elem in data: - if 'show_ports_description' not in elem: - continue - key = str(elem['show_ports_description']['port']) - - if 'descriptionString' in elem['show_ports_description']: - desc = elem['show_ports_description']['descriptionString'] - self.facts['interfaces'][key]['description'] = desc - - def populate_vlan_interfaces(self, data, sysmac): - for elem in data: - if 'vlanProc' in elem: - key = elem['vlanProc']['name1'] - if key not in self.facts['interfaces']: - intf = dict() - intf['type'] = 'VLAN' - intf['macaddress'] = sysmac - self.facts['interfaces'][key] = intf - - if elem['vlanProc']['ipAddress'] != '0.0.0.0': - self.facts['interfaces'][key]['ipv4'] = list() - addr = elem['vlanProc']['ipAddress'] - subnet = elem['vlanProc']['maskForDisplay'] - ipv4 = dict(address=addr, subnet=subnet) - self.add_ip_address(addr, 'ipv4') - self.facts['interfaces'][key]['ipv4'].append(ipv4) - - if 'rtifIpv6Address' in elem: - key = elem['rtifIpv6Address']['rtifName'] - if key not in self.facts['interfaces']: - intf = dict() - intf['type'] = 'VLAN' - intf['macaddress'] = sysmac - self.facts['interfaces'][key] = intf - self.facts['interfaces'][key]['ipv6'] = list() - addr, subnet = elem['rtifIpv6Address']['ipv6_address_mask'].split('/') - ipv6 = dict(address=addr, subnet=subnet) - self.add_ip_address(addr, 'ipv6') - self.facts['interfaces'][key]['ipv6'].append(ipv6) - - def add_ip_address(self, address, family): - if family == 'ipv4': - if address not in self.facts['all_ipv4_addresses']: - self.facts['all_ipv4_addresses'].append(address) - else: - if address not in self.facts['all_ipv6_addresses']: - self.facts['all_ipv6_addresses'].append(address) - - def parse_neighbors(self, data): - facts = dict() - for elem in data: - if 'lldpPortNbrInfoShort' not in elem: - continue - intf = str(elem['lldpPortNbrInfoShort']['port']) - if intf not in facts: - facts[intf] = list() - fact = dict() - fact['host'] = elem['lldpPortNbrInfoShort']['nbrSysName'] - fact['port'] = str(elem['lldpPortNbrInfoShort']['nbrPortID']) - facts[intf].append(fact) - return facts diff --git a/plugins/module_utils/network/exos/facts/lldp_global/__init__.py b/plugins/module_utils/network/exos/facts/lldp_global/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/facts/lldp_global/lldp_global.py b/plugins/module_utils/network/exos/facts/lldp_global/lldp_global.py deleted file mode 100644 index f01893da81..0000000000 --- a/plugins/module_utils/network/exos/facts/lldp_global/lldp_global.py +++ /dev/null @@ -1,97 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The exos lldp_global fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.lldp_global.lldp_global \ - import Lldp_globalArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import send_requests - - -class Lldp_globalFacts(object): - """ The exos lldp_global fact class - """ - - TLV_SELECT_OPTIONS = [ - "SYSTEM_NAME", - "SYSTEM_DESCRIPTION", - "SYSTEM_CAPABILITIES", - "MANAGEMENT_ADDRESS", - "PORT_DESCRIPTION"] - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Lldp_globalArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lldp_global - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - request = { - "path": "/rest/restconf/data/openconfig-lldp:lldp/config/", - "method": "GET", - } - data = send_requests(self._module, request) - - obj = {} - if data: - lldp_obj = self.render_config(self.generated_spec, data[0]) - if lldp_obj: - obj = lldp_obj - - ansible_facts['ansible_network_resources'].pop('lldp_global', None) - facts = {} - - params = utils.validate_config(self.argument_spec, {'config': obj}) - facts['lldp_global'] = params['config'] - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - config['interval'] = conf["openconfig-lldp:config"]["hello-timer"] - - for item in self.TLV_SELECT_OPTIONS: - config["tlv_select"][item.lower()] = ( - False if (item in conf["openconfig-lldp:config"]["suppress-tlv-advertisement"]) - else True) - - return utils.remove_empties(config) diff --git a/plugins/module_utils/network/exos/facts/lldp_interfaces/__init__.py b/plugins/module_utils/network/exos/facts/lldp_interfaces/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/facts/lldp_interfaces/lldp_interfaces.py b/plugins/module_utils/network/exos/facts/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index 444dee443e..0000000000 --- a/plugins/module_utils/network/exos/facts/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The exos lldp_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.lldp_interfaces.lldp_interfaces import Lldp_interfacesArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import send_requests - - -class Lldp_interfacesFacts(object): - """ The exos lldp_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Lldp_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lldp_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - - if not data: - request = [{ - "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4", - "method": "GET" - }] - data = send_requests(self._module, requests=request) - - objs = [] - if data: - for d in data[0]["openconfig-lldp:interfaces"]["interface"]: - obj = self.render_config(self.generated_spec, d["config"]) - if obj: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('lldp_interfaces', None) - facts = {} - if objs: - params = utils.validate_config(self.argument_spec, {'config': objs}) - facts['lldp_interfaces'] = params['config'] - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - - config["name"] = conf["name"] - config["enabled"] = bool(conf["enabled"]) - - return utils.remove_empties(config) diff --git a/plugins/module_utils/network/exos/facts/vlans/__init__.py b/plugins/module_utils/network/exos/facts/vlans/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/facts/vlans/vlans.py b/plugins/module_utils/network/exos/facts/vlans/vlans.py deleted file mode 100644 index 4ba7284747..0000000000 --- a/plugins/module_utils/network/exos/facts/vlans/vlans.py +++ /dev/null @@ -1,89 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The exos vlans fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.vlans.vlans import VlansArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import send_requests - - -class VlansFacts(object): - """ The exos vlans fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = VlansArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for vlans - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - - if not data: - request = [{ - "path": "/rest/restconf/data/openconfig-vlan:vlans?depth=5", - "method": "GET" - }] - data = send_requests(self._module, requests=request) - - objs = [] - if data: - for d in data[0]["openconfig-vlan:vlans"]["vlan"]: - obj = self.render_config(self.generated_spec, d["config"]) - if obj: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('vlans', None) - facts = {} - if objs: - params = utils.validate_config(self.argument_spec, {'config': objs}) - facts['vlans'] = params['config'] - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - - config["name"] = conf["name"] - config["state"] = "suspend" if conf["status"] == "SUSPENDED" else conf["status"].lower() - config["vlan_id"] = conf["vlan-id"] - - return utils.remove_empties(config) diff --git a/plugins/module_utils/network/exos/utils/__init__.py b/plugins/module_utils/network/exos/utils/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/exos/utils/utils.py b/plugins/module_utils/network/exos/utils/utils.py deleted file mode 100644 index d40f81714c..0000000000 --- a/plugins/module_utils/network/exos/utils/utils.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -def search_obj_in_list(item, lst, key): - for o in lst: - if o[key] == item: - return o - return None diff --git a/plugins/module_utils/network/f5/__init__.py b/plugins/module_utils/network/f5/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/f5/iworkflow.py b/plugins/module_utils/network/f5/iworkflow.py deleted file mode 100644 index 92496a4f63..0000000000 --- a/plugins/module_utils/network/f5/iworkflow.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2017 F5 Networks 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 - - -import time - -try: - from f5.iworkflow import ManagementRoot - from icontrol.exceptions import iControlUnexpectedHTTPError - HAS_F5SDK = True -except ImportError: - HAS_F5SDK = False - -try: - from library.module_utils.network.f5.common import F5BaseClient - from library.module_utils.network.f5.common import F5ModuleError -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5BaseClient - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - - -class F5Client(F5BaseClient): - @property - def api(self): - exc = None - if self._client: - return self._client - for x in range(0, 3): - try: - server = self.params['provider']['server'] or self.params['server'] - user = self.params['provider']['user'] or self.params['user'] - password = self.params['provider']['password'] or self.params['password'] - server_port = self.params['provider']['server_port'] or self.params['server_port'] or 443 - validate_certs = self.params['provider']['validate_certs'] or self.params['validate_certs'] - - result = ManagementRoot( - server, - user, - password, - port=server_port, - verify=validate_certs, - token='local' - ) - self._client = result - return self._client - except Exception as ex: - exc = ex - time.sleep(3) - error = 'Unable to connect to {0} on port {1}.'.format(self.params['server'], self.params['server_port']) - if exc is not None: - error += ' The reported error was "{0}".'.format(str(exc)) - raise F5ModuleError(error) diff --git a/plugins/module_utils/network/f5/legacy.py b/plugins/module_utils/network/f5/legacy.py deleted file mode 100644 index bb2189c2bb..0000000000 --- a/plugins/module_utils/network/f5/legacy.py +++ /dev/null @@ -1,121 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2017 F5 Networks 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 - -try: - import bigsuds - bigsuds_found = True -except ImportError: - bigsuds_found = False - - -from ansible.module_utils.basic import env_fallback - - -def f5_argument_spec(): - return dict( - server=dict( - type='str', - required=True, - fallback=(env_fallback, ['F5_SERVER']) - ), - user=dict( - type='str', - required=True, - fallback=(env_fallback, ['F5_USER']) - ), - password=dict( - type='str', - aliases=['pass', 'pwd'], - required=True, - no_log=True, - fallback=(env_fallback, ['F5_PASSWORD']) - ), - validate_certs=dict( - default='yes', - type='bool', - fallback=(env_fallback, ['F5_VALIDATE_CERTS']) - ), - server_port=dict( - type='int', - default=443, - fallback=(env_fallback, ['F5_SERVER_PORT']) - ), - state=dict( - type='str', - default='present', - choices=['present', 'absent'] - ), - partition=dict( - type='str', - default='Common', - fallback=(env_fallback, ['F5_PARTITION']) - ) - ) - - -def f5_parse_arguments(module): - if not bigsuds_found: - module.fail_json(msg="the python bigsuds module is required") - - if module.params['validate_certs']: - import ssl - if not hasattr(ssl, 'SSLContext'): - module.fail_json( - msg="bigsuds does not support verifying certificates with python < 2.7.9." - "Either update python or set validate_certs=False on the task'") - - return ( - module.params['server'], - module.params['user'], - module.params['password'], - module.params['state'], - module.params['partition'], - module.params['validate_certs'], - module.params['server_port'] - ) - - -def bigip_api(bigip, user, password, validate_certs, port=443): - try: - if bigsuds.__version__ >= '1.0.4': - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password, verify=validate_certs, port=port) - elif bigsuds.__version__ == '1.0.3': - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password, verify=validate_certs) - else: - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password) - except TypeError: - # bigsuds < 1.0.3, no verify param - if validate_certs: - # Note: verified we have SSLContext when we parsed params - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password) - else: - import ssl - if hasattr(ssl, 'SSLContext'): - # Really, you should never do this. It disables certificate - # verification *globally*. But since older bigip libraries - # don't give us a way to toggle verification we need to - # disable it at the global level. - # From https://www.python.org/dev/peps/pep-0476/#id29 - ssl._create_default_https_context = ssl._create_unverified_context - api = bigsuds.BIGIP(hostname=bigip, username=user, password=password) - - return api - - -# Fully Qualified name (with the partition) -def fq_name(partition, name): - if name is not None and not name.startswith('/'): - return '/%s/%s' % (partition, name) - return name - - -# Fully Qualified name (with partition) for a list -def fq_list_names(partition, list_names): - if list_names is None: - return None - return map(lambda x: fq_name(partition, x), list_names) diff --git a/plugins/module_utils/network/f5/urls.py b/plugins/module_utils/network/f5/urls.py deleted file mode 100644 index ed6dc3b5aa..0000000000 --- a/plugins/module_utils/network/f5/urls.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2017, F5 Networks 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 - - -import re - -try: - from library.module_utils.network.f5.common import F5ModuleError -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - -_CLEAN_HEADER_REGEX_BYTE = re.compile(b'^\\S[^\\r\\n]*$|^$') -_CLEAN_HEADER_REGEX_STR = re.compile(r'^\S[^\r\n]*$|^$') - - -def check_header_validity(header): - """Verifies that header value is a string which doesn't contain - leading whitespace or return characters. - - NOTE: This is a slightly modified version of the original function - taken from the requests library: - http://docs.python-requests.org/en/master/_modules/requests/utils/ - - :param header: string containing ':'. - """ - try: - name, value = header.split(':') - except ValueError: - raise F5ModuleError('Invalid header format: {0}'.format(header)) - if name == '': - raise F5ModuleError('Invalid header format: {0}'.format(header)) - - if isinstance(value, bytes): - pat = _CLEAN_HEADER_REGEX_BYTE - else: - pat = _CLEAN_HEADER_REGEX_STR - try: - if not pat.match(value): - raise F5ModuleError("Invalid return character or leading space in header: %s" % name) - except TypeError: - raise F5ModuleError("Value for header {%s: %s} must be of type str or " - "bytes, not %s" % (name, value, type(value))) - - -def build_service_uri(base_uri, partition, name): - """Build the proper uri for a service resource. - This follows the scheme: - /~~<.app>~ - :param base_uri: str -- base uri of the REST endpoint - :param partition: str -- partition for the service - :param name: str -- name of the service - :returns: str -- uri to access the service - """ - name = name.replace('/', '~') - return '%s~%s~%s.app~%s' % (base_uri, partition, name, name) - - -def parseStats(entry): - if 'description' in entry: - return entry['description'] - elif 'value' in entry: - return entry['value'] - elif 'entries' in entry or 'nestedStats' in entry and 'entries' in entry['nestedStats']: - if 'entries' in entry: - entries = entry['entries'] - else: - entries = entry['nestedStats']['entries'] - result = None - - for name in entries: - entry = entries[name] - if 'https://localhost' in name: - name = name.split('/') - name = name[-1] - if result and isinstance(result, list): - result.append(parseStats(entry)) - elif result and isinstance(result, dict): - result[name] = parseStats(entry) - else: - try: - int(name) - result = list() - result.append(parseStats(entry)) - except ValueError: - result = dict() - result[name] = parseStats(entry) - else: - if '.' in name: - names = name.split('.') - key = names[0] - value = names[1] - if result is None: - # result can be None if this branch is reached first - # - # For example, the mgmt/tm/net/trunk/NAME/stats API - # returns counters.bitsIn before anything else. - result = dict() - result[key] = dict() - elif key not in result: - result[key] = dict() - elif result[key] is None: - result[key] = dict() - result[key][value] = parseStats(entry) - else: - if result and isinstance(result, list): - result.append(parseStats(entry)) - elif result and isinstance(result, dict): - result[name] = parseStats(entry) - else: - try: - int(name) - result = list() - result.append(parseStats(entry)) - except ValueError: - result = dict() - result[name] = parseStats(entry) - return result diff --git a/plugins/module_utils/network/fortianalyzer/__init__.py b/plugins/module_utils/network/fortianalyzer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/fortianalyzer/common.py b/plugins/module_utils/network/fortianalyzer/common.py deleted file mode 100644 index 546f71aa12..0000000000 --- a/plugins/module_utils/network/fortianalyzer/common.py +++ /dev/null @@ -1,292 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2017 Fortinet, Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -# BEGIN STATIC DATA AND MESSAGES -class FAZMethods: - GET = "get" - SET = "set" - EXEC = "exec" - EXECUTE = "exec" - UPDATE = "update" - ADD = "add" - DELETE = "delete" - REPLACE = "replace" - CLONE = "clone" - MOVE = "move" - - -BASE_HEADERS = { - 'Content-Type': 'application/json', - 'Accept': 'application/json' -} - - -# FAZ RETURN CODES -FAZ_RC = { - "faz_return_codes": { - 0: { - "msg": "OK", - "changed": True, - "stop_on_success": True - }, - -100000: { - "msg": "Module returned without actually running anything. " - "Check parameters, and please contact the authors if needed.", - "failed": True - }, - -2: { - "msg": "Object already exists.", - "skipped": True, - "changed": False, - "good_codes": [0, -2] - }, - -6: { - "msg": "Invalid Url. Sometimes this can happen because the path is mapped to a hostname or object that" - " doesn't exist. Double check your input object parameters." - }, - -3: { - "msg": "Object doesn't exist.", - "skipped": True, - "changed": False, - "good_codes": [0, -3] - }, - -10131: { - "msg": "Object dependency failed. Do all named objects in parameters exist?", - "changed": False, - "skipped": True - }, - -9998: { - "msg": "Duplicate object. Try using mode='set', if using add. STOPPING. Use 'ignore_errors=yes' in playbook" - "to override and mark successful.", - }, - -20042: { - "msg": "Device Unreachable.", - "skipped": True - }, - -10033: { - "msg": "Duplicate object. Try using mode='set', if using add.", - "changed": False, - "skipped": True - }, - -10000: { - "msg": "Duplicate object. Try using mode='set', if using add.", - "changed": False, - "skipped": True - }, - -20010: { - "msg": "Device already added to FortiAnalyzer. Serial number already in use.", - "good_codes": [0, -20010], - "changed": False, - "stop_on_failure": False - }, - -20002: { - "msg": "Invalid Argument -- Does this Device exist on FortiAnalyzer?", - "changed": False, - "skipped": True, - } - } -} - -DEFAULT_RESULT_OBJ = (-100000, {"msg": "Nothing Happened. Check that handle_response is being called!"}) -FAIL_SOCKET_MSG = {"msg": "Socket Path Empty! The persistent connection manager is messed up. " - "Try again in a few moments."} - - -# BEGIN ERROR EXCEPTIONS -class FAZBaseException(Exception): - """Wrapper to catch the unexpected""" - - def __init__(self, msg=None, *args, **kwargs): - if msg is None: - msg = "An exception occurred within the fortianalyzer.py httpapi connection plugin." - super(FAZBaseException, self).__init__(msg, *args) - -# END ERROR CLASSES - - -# BEGIN CLASSES -class FAZCommon(object): - - @staticmethod - def format_request(method, url, *args, **kwargs): - """ - Formats the payload from the module, into a payload the API handler can use. - - :param url: Connection URL to access - :type url: string - :param method: The preferred API Request method (GET, ADD, POST, etc....) - :type method: basestring - :param kwargs: The payload dictionary from the module to be converted. - - :return: Properly formatted dictionary payload for API Request via Connection Plugin. - :rtype: dict - """ - - params = [{"url": url}] - if args: - for arg in args: - params[0].update(arg) - if kwargs: - keylist = list(kwargs) - for k in keylist: - kwargs[k.replace("__", "-")] = kwargs.pop(k) - if method == "get" or method == "clone": - params[0].update(kwargs) - else: - if kwargs.get("data", False): - params[0]["data"] = kwargs["data"] - else: - params[0]["data"] = kwargs - return params - - @staticmethod - def split_comma_strings_into_lists(obj): - """ - Splits a CSV String into a list. Also takes a dictionary, and converts any CSV strings in any key, to a list. - - :param obj: object in CSV format to be parsed. - :type obj: str or dict - - :return: A list containing the CSV items. - :rtype: list - """ - return_obj = () - if isinstance(obj, dict): - if len(obj) > 0: - for k, v in obj.items(): - if isinstance(v, str): - new_list = list() - if "," in v: - new_items = v.split(",") - for item in new_items: - new_list.append(item.strip()) - obj[k] = new_list - return_obj = obj - elif isinstance(obj, str): - return_obj = obj.replace(" ", "").split(",") - - return return_obj - - @staticmethod - def cidr_to_netmask(cidr): - """ - Converts a CIDR Network string to full blown IP/Subnet format in decimal format. - Decided not use IP Address module to keep includes to a minimum. - - :param cidr: String object in CIDR format to be processed - :type cidr: str - - :return: A string object that looks like this "x.x.x.x/y.y.y.y" - :rtype: str - """ - if isinstance(cidr, str): - cidr = int(cidr) - mask = (0xffffffff >> (32 - cidr)) << (32 - cidr) - return (str((0xff000000 & mask) >> 24) + '.' - + str((0x00ff0000 & mask) >> 16) + '.' - + str((0x0000ff00 & mask) >> 8) + '.' - + str((0x000000ff & mask))) - - @staticmethod - def paramgram_child_list_override(list_overrides, paramgram, module): - """ - If a list of items was provided to a "parent" paramgram attribute, the paramgram needs to be rewritten. - The child keys of the desired attribute need to be deleted, and then that "parent" keys' contents is replaced - With the list of items that was provided. - - :param list_overrides: Contains the response from the FortiAnalyzer. - :type list_overrides: list - :param paramgram: Contains the paramgram passed to the modules' local modify function. - :type paramgram: dict - :param module: Contains the Ansible Module Object being used by the module. - :type module: classObject - - :return: A new "paramgram" refactored to allow for multiple entries being added. - :rtype: dict - """ - if len(list_overrides) > 0: - for list_variable in list_overrides: - try: - list_variable = list_variable.replace("-", "_") - override_data = module.params[list_variable] - if override_data: - del paramgram[list_variable] - paramgram[list_variable] = override_data - except BaseException as e: - raise FAZBaseException("Error occurred merging custom lists for the paramgram parent: " + str(e)) - return paramgram - - @staticmethod - def syslog(module, msg): - try: - module.log(msg=msg) - except BaseException: - pass - - -# RECURSIVE FUNCTIONS START -def prepare_dict(obj): - """ - Removes any keys from a dictionary that are only specific to our use in the module. FortiAnalyzer will reject - requests with these empty/None keys in it. - - :param obj: Dictionary object to be processed. - :type obj: dict - - :return: Processed dictionary. - :rtype: dict - """ - - list_of_elems = ["mode", "adom", "host", "username", "password"] - - if isinstance(obj, dict): - obj = dict((key, prepare_dict(value)) for (key, value) in obj.items() if key not in list_of_elems) - return obj - - -def scrub_dict(obj): - """ - Removes any keys from a dictionary that are EMPTY -- this includes parent keys. FortiAnalyzer doesn't - like empty keys in dictionaries - - :param obj: Dictionary object to be processed. - :type obj: dict - - :return: Processed dictionary. - :rtype: dict - """ - - if isinstance(obj, dict): - return dict((k, scrub_dict(v)) for k, v in obj.items() if v and scrub_dict(v)) - else: - return obj diff --git a/plugins/module_utils/network/fortianalyzer/fortianalyzer.py b/plugins/module_utils/network/fortianalyzer/fortianalyzer.py deleted file mode 100644 index 94d83cd7ef..0000000000 --- a/plugins/module_utils/network/fortianalyzer/fortianalyzer.py +++ /dev/null @@ -1,477 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2017 Fortinet, Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZ_RC -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZBaseException -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZCommon -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import scrub_dict -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZMethods - - -# ACTIVE BUG WITH OUR DEBUG IMPORT CALL - BECAUSE IT'S UNDER MODULE_UTILITIES -# WHEN module_common.recursive_finder() runs under the module loader, it looks for this namespace debug import -# and because it's not there, it always fails, regardless of it being under a try/catch here. -# we're going to move it to a different namespace. -# # check for debug lib -# try: -# from ansible.module_utils.network.fortianalyzer.fortianalyzer_debug import debug_dump -# HAS_FAZ_DEBUG = True -# except: -# HAS_FAZ_DEBUG = False - - -# BEGIN HANDLER CLASSES -class FortiAnalyzerHandler(object): - def __init__(self, conn, module): - self._conn = conn - self._module = module - self._tools = FAZCommon - self._uses_workspace = None - self._uses_adoms = None - self._locked_adom_list = list() - self._lock_info = None - - self.workspace_check() - if self._uses_workspace: - self.get_lock_info(adom=self._module.paramgram["adom"]) - - def process_request(self, url, datagram, method): - """ - Formats and Runs the API Request via Connection Plugin. Streamlined for use from Modules. - - :param url: Connection URL to access - :type url: string - :param datagram: The prepared payload for the API Request in dictionary format - :type datagram: dict - :param method: The preferred API Request method (GET, ADD, POST, etc....) - :type method: basestring - - :return: Dictionary containing results of the API Request via Connection Plugin. - :rtype: dict - """ - try: - adom = self._module.paramgram["adom"] - if self.uses_workspace and adom not in self._locked_adom_list and method != FAZMethods.GET: - self.lock_adom(adom=adom) - except BaseException as err: - raise FAZBaseException(err) - - data = self._tools.format_request(method, url, **datagram) - response = self._conn.send_request(method, data) - - try: - adom = self._module.paramgram["adom"] - if self.uses_workspace and adom in self._locked_adom_list \ - and response[0] == 0 and method != FAZMethods.GET: - self.commit_changes(adom=adom) - except BaseException as err: - raise FAZBaseException(err) - - # if HAS_FAZ_DEBUG: - # try: - # debug_dump(response, datagram, self._module.paramgram, url, method) - # except BaseException: - # pass - - return response - - def workspace_check(self): - """ - Checks FortiAnalyzer for the use of Workspace mode. - """ - url = "/cli/global/system/global" - data = {"fields": ["workspace-mode", "adom-status"]} - resp_obj = self.process_request(url, data, FAZMethods.GET) - try: - if resp_obj[1]["workspace-mode"] in ["workflow", "normal"]: - self.uses_workspace = True - elif resp_obj[1]["workspace-mode"] == "disabled": - self.uses_workspace = False - except KeyError: - self.uses_workspace = False - except BaseException as err: - raise FAZBaseException(msg="Couldn't determine workspace-mode in the plugin. Error: " + str(err)) - try: - if resp_obj[1]["adom-status"] in [1, "enable"]: - self.uses_adoms = True - else: - self.uses_adoms = False - except KeyError: - self.uses_adoms = False - except BaseException as err: - raise FAZBaseException(msg="Couldn't determine adom-status in the plugin. Error: " + str(err)) - - def run_unlock(self): - """ - Checks for ADOM status, if locked, it will unlock - """ - for adom_locked in self._locked_adom_list: - self.unlock_adom(adom_locked) - - def lock_adom(self, adom=None): - """ - Locks an ADOM for changes - """ - if not adom or adom == "root": - url = "/dvmdb/adom/root/workspace/lock" - else: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/lock/" - else: - url = "/dvmdb/adom/{adom}/workspace/lock/".format(adom=adom) - datagram = {} - data = self._tools.format_request(FAZMethods.EXEC, url, **datagram) - resp_obj = self._conn.send_request(FAZMethods.EXEC, data) - code = resp_obj[0] - if code == 0 and resp_obj[1]["status"]["message"].lower() == "ok": - self.add_adom_to_lock_list(adom) - else: - lockinfo = self.get_lock_info(adom=adom) - self._module.fail_json(msg=("An error occurred trying to lock the adom. Error: " - + str(resp_obj) + ", LOCK INFO: " + str(lockinfo))) - return resp_obj - - def unlock_adom(self, adom=None): - """ - Unlocks an ADOM after changes - """ - if not adom or adom == "root": - url = "/dvmdb/adom/root/workspace/unlock" - else: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/unlock/" - else: - url = "/dvmdb/adom/{adom}/workspace/unlock/".format(adom=adom) - datagram = {} - data = self._tools.format_request(FAZMethods.EXEC, url, **datagram) - resp_obj = self._conn.send_request(FAZMethods.EXEC, data) - code = resp_obj[0] - if code == 0 and resp_obj[1]["status"]["message"].lower() == "ok": - self.remove_adom_from_lock_list(adom) - else: - self._module.fail_json(msg=("An error occurred trying to unlock the adom. Error: " + str(resp_obj))) - return resp_obj - - def get_lock_info(self, adom=None): - """ - Gets ADOM lock info so it can be displayed with the error messages. Or if determined to be locked by ansible - for some reason, then unlock it. - """ - if not adom or adom == "root": - url = "/dvmdb/adom/root/workspace/lockinfo" - else: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/lockinfo/" - else: - url = "/dvmdb/adom/{adom}/workspace/lockinfo/".format(adom=adom) - datagram = {} - data = self._tools.format_request(FAZMethods.GET, url, **datagram) - resp_obj = self._conn.send_request(FAZMethods.GET, data) - code = resp_obj[0] - if code != 0: - self._module.fail_json(msg=("An error occurred trying to get the ADOM Lock Info. Error: " + str(resp_obj))) - elif code == 0: - self._lock_info = resp_obj[1] - return resp_obj - - def commit_changes(self, adom=None, aux=False): - """ - Commits changes to an ADOM - """ - if not adom or adom == "root": - url = "/dvmdb/adom/root/workspace/commit" - else: - if aux: - url = "/pm/config/adom/{adom}/workspace/commit".format(adom=adom) - else: - if adom.lower() == "global": - url = "/dvmdb/global/workspace/commit/" - else: - url = "/dvmdb/adom/{adom}/workspace/commit".format(adom=adom) - datagram = {} - data = self._tools.format_request(FAZMethods.EXEC, url, **datagram) - resp_obj = self._conn.send_request(FAZMethods.EXEC, data) - code = resp_obj[0] - if code != 0: - self._module.fail_json(msg=("An error occurred trying to commit changes to the adom. Error: " - + str(resp_obj))) - - def govern_response(self, module, results, msg=None, good_codes=None, - stop_on_fail=None, stop_on_success=None, skipped=None, - changed=None, unreachable=None, failed=None, success=None, changed_if_success=None, - ansible_facts=None): - """ - This function will attempt to apply default values to canned responses from FortiAnalyzer we know of. - This saves time, and turns the response in the module into a "one-liner", while still giving us... - the flexibility to directly use return_response in modules if we have too. This function saves repeated code. - - :param module: The Ansible Module CLASS object, used to run fail/exit json - :type module: object - :param msg: An overridable custom message from the module that called this. - :type msg: string - :param results: A dictionary object containing an API call results - :type results: dict - :param good_codes: A list of exit codes considered successful from FortiAnalyzer - :type good_codes: list - :param stop_on_fail: If true, stops playbook run when return code is NOT IN good codes (default: true) - :type stop_on_fail: boolean - :param stop_on_success: If true, stops playbook run when return code is IN good codes (default: false) - :type stop_on_success: boolean - :param changed: If True, tells Ansible that object was changed (default: false) - :type skipped: boolean - :param skipped: If True, tells Ansible that object was skipped (default: false) - :type skipped: boolean - :param unreachable: If True, tells Ansible that object was unreachable (default: false) - :type unreachable: boolean - :param failed: If True, tells Ansible that execution was a failure. Overrides good_codes. (default: false) - :type unreachable: boolean - :param success: If True, tells Ansible that execution was a success. Overrides good_codes. (default: false) - :type unreachable: boolean - :param changed_if_success: If True, defaults to changed if successful if you specify or not" - :type changed_if_success: boolean - :param ansible_facts: A prepared dictionary of ansible facts from the execution. - :type ansible_facts: dict - """ - if module is None and results is None: - raise FAZBaseException("govern_response() was called without a module and/or results tuple! Fix!") - # Get the Return code from results - try: - rc = results[0] - except BaseException: - raise FAZBaseException("govern_response() was called without the return code at results[0]") - - # init a few items - rc_data = None - - # Get the default values for the said return code. - try: - rc_codes = FAZ_RC.get('faz_return_codes') - rc_data = rc_codes.get(rc) - except BaseException: - pass - - if not rc_data: - rc_data = {} - # ONLY add to overrides if not none -- This is very important that the keys aren't added at this stage - # if they are empty. And there aren't that many, so let's just do a few if then statements. - if good_codes is not None: - rc_data["good_codes"] = good_codes - if stop_on_fail is not None: - rc_data["stop_on_fail"] = stop_on_fail - if stop_on_success is not None: - rc_data["stop_on_success"] = stop_on_success - if skipped is not None: - rc_data["skipped"] = skipped - if changed is not None: - rc_data["changed"] = changed - if unreachable is not None: - rc_data["unreachable"] = unreachable - if failed is not None: - rc_data["failed"] = failed - if success is not None: - rc_data["success"] = success - if changed_if_success is not None: - rc_data["changed_if_success"] = changed_if_success - if results is not None: - rc_data["results"] = results - if msg is not None: - rc_data["msg"] = msg - if ansible_facts is None: - rc_data["ansible_facts"] = {} - else: - rc_data["ansible_facts"] = ansible_facts - - return self.return_response(module=module, - results=results, - msg=rc_data.get("msg", "NULL"), - good_codes=rc_data.get("good_codes", (0,)), - stop_on_fail=rc_data.get("stop_on_fail", True), - stop_on_success=rc_data.get("stop_on_success", False), - skipped=rc_data.get("skipped", False), - changed=rc_data.get("changed", False), - changed_if_success=rc_data.get("changed_if_success", False), - unreachable=rc_data.get("unreachable", False), - failed=rc_data.get("failed", False), - success=rc_data.get("success", False), - ansible_facts=rc_data.get("ansible_facts", dict())) - - def return_response(self, module, results, msg="NULL", good_codes=(0,), - stop_on_fail=True, stop_on_success=False, skipped=False, - changed=False, unreachable=False, failed=False, success=False, changed_if_success=True, - ansible_facts=()): - """ - This function controls the logout and error reporting after an method or function runs. The exit_json for - ansible comes from logic within this function. If this function returns just the msg, it means to continue - execution on the playbook. It is called from the ansible module, or from the self.govern_response function. - - :param module: The Ansible Module CLASS object, used to run fail/exit json - :type module: object - :param msg: An overridable custom message from the module that called this. - :type msg: string - :param results: A dictionary object containing an API call results - :type results: dict - :param good_codes: A list of exit codes considered successful from FortiAnalyzer - :type good_codes: list - :param stop_on_fail: If true, stops playbook run when return code is NOT IN good codes (default: true) - :type stop_on_fail: boolean - :param stop_on_success: If true, stops playbook run when return code is IN good codes (default: false) - :type stop_on_success: boolean - :param changed: If True, tells Ansible that object was changed (default: false) - :type skipped: boolean - :param skipped: If True, tells Ansible that object was skipped (default: false) - :type skipped: boolean - :param unreachable: If True, tells Ansible that object was unreachable (default: false) - :type unreachable: boolean - :param failed: If True, tells Ansible that execution was a failure. Overrides good_codes. (default: false) - :type unreachable: boolean - :param success: If True, tells Ansible that execution was a success. Overrides good_codes. (default: false) - :type unreachable: boolean - :param changed_if_success: If True, defaults to changed if successful if you specify or not" - :type changed_if_success: boolean - :param ansible_facts: A prepared dictionary of ansible facts from the execution. - :type ansible_facts: dict - - :return: A string object that contains an error message - :rtype: str - """ - - # VALIDATION ERROR - if (len(results) == 0) or (failed and success) or (changed and unreachable): - module.exit_json(msg="Handle_response was called with no results, or conflicting failed/success or " - "changed/unreachable parameters. Fix the exit code on module. " - "Generic Failure", failed=True) - - # IDENTIFY SUCCESS/FAIL IF NOT DEFINED - if not failed and not success: - if len(results) > 0: - if results[0] not in good_codes: - failed = True - elif results[0] in good_codes: - success = True - - if len(results) > 0: - # IF NO MESSAGE WAS SUPPLIED, GET IT FROM THE RESULTS, IF THAT DOESN'T WORK, THEN WRITE AN ERROR MESSAGE - if msg == "NULL": - try: - msg = results[1]['status']['message'] - except BaseException: - msg = "No status message returned at results[1][status][message], " \ - "and none supplied to msg parameter for handle_response." - - if failed: - # BECAUSE SKIPPED/FAILED WILL OFTEN OCCUR ON CODES THAT DON'T GET INCLUDED, THEY ARE CONSIDERED FAILURES - # HOWEVER, THEY ARE MUTUALLY EXCLUSIVE, SO IF IT IS MARKED SKIPPED OR UNREACHABLE BY THE MODULE LOGIC - # THEN REMOVE THE FAILED FLAG SO IT DOESN'T OVERRIDE THE DESIRED STATUS OF SKIPPED OR UNREACHABLE. - if failed and skipped: - failed = False - if failed and unreachable: - failed = False - if stop_on_fail: - if self._uses_workspace: - try: - self.run_unlock() - except BaseException as err: - raise FAZBaseException(msg=("Couldn't unlock ADOM! Error: " + str(err))) - module.exit_json(msg=msg, failed=failed, changed=changed, unreachable=unreachable, skipped=skipped, - results=results[1], ansible_facts=ansible_facts, rc=results[0], - invocation={"module_args": ansible_facts["ansible_params"]}) - elif success: - if changed_if_success: - changed = True - success = False - if stop_on_success: - if self._uses_workspace: - try: - self.run_unlock() - except BaseException as err: - raise FAZBaseException(msg=("Couldn't unlock ADOM! Error: " + str(err))) - module.exit_json(msg=msg, success=success, changed=changed, unreachable=unreachable, - skipped=skipped, results=results[1], ansible_facts=ansible_facts, rc=results[0], - invocation={"module_args": ansible_facts["ansible_params"]}) - return msg - - @staticmethod - def construct_ansible_facts(response, ansible_params, paramgram, *args, **kwargs): - """ - Constructs a dictionary to return to ansible facts, containing various information about the execution. - - :param response: Contains the response from the FortiAnalyzer. - :type response: dict - :param ansible_params: Contains the parameters Ansible was called with. - :type ansible_params: dict - :param paramgram: Contains the paramgram passed to the modules' local modify function. - :type paramgram: dict - :param args: Free-form arguments that could be added. - :param kwargs: Free-form keyword arguments that could be added. - - :return: A dictionary containing lots of information to append to Ansible Facts. - :rtype: dict - """ - - facts = { - "response": response, - "ansible_params": scrub_dict(ansible_params), - "paramgram": scrub_dict(paramgram), - } - - if args: - facts["custom_args"] = args - if kwargs: - facts.update(kwargs) - - return facts - - @property - def uses_workspace(self): - return self._uses_workspace - - @uses_workspace.setter - def uses_workspace(self, val): - self._uses_workspace = val - - @property - def uses_adoms(self): - return self._uses_adoms - - @uses_adoms.setter - def uses_adoms(self, val): - self._uses_adoms = val - - def add_adom_to_lock_list(self, adom): - if adom not in self._locked_adom_list: - self._locked_adom_list.append(adom) - - def remove_adom_from_lock_list(self, adom): - if adom in self._locked_adom_list: - self._locked_adom_list.remove(adom) diff --git a/plugins/module_utils/network/ftd/__init__.py b/plugins/module_utils/network/ftd/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/ftd/common.py b/plugins/module_utils/network/ftd/common.py deleted file mode 100644 index de3f459d5b..0000000000 --- a/plugins/module_utils/network/ftd/common.py +++ /dev/null @@ -1,238 +0,0 @@ -# Copyright (c) 2018 Cisco and/or its affiliates. -# -# 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 . -# -import re - -from ansible.module_utils._text import to_text -from ansible.module_utils.common.collections import is_string -from ansible.module_utils.six import iteritems - -INVALID_IDENTIFIER_SYMBOLS = r'[^a-zA-Z0-9_]' - -IDENTITY_PROPERTIES = ['id', 'version', 'ruleId'] -NON_COMPARABLE_PROPERTIES = IDENTITY_PROPERTIES + ['isSystemDefined', 'links', 'token', 'rulePosition'] - - -class HTTPMethod: - GET = 'get' - POST = 'post' - PUT = 'put' - DELETE = 'delete' - - -class ResponseParams: - SUCCESS = 'success' - STATUS_CODE = 'status_code' - RESPONSE = 'response' - - -class FtdConfigurationError(Exception): - def __init__(self, msg, obj=None): - super(FtdConfigurationError, self).__init__(msg) - self.msg = msg - self.obj = obj - - -class FtdServerError(Exception): - def __init__(self, response, code): - super(FtdServerError, self).__init__(response) - self.response = response - self.code = code - - -class FtdUnexpectedResponse(Exception): - """The exception to be raised in case of unexpected responses from 3d parties.""" - pass - - -def construct_ansible_facts(response, params): - facts = dict() - if response: - response_body = response['items'] if 'items' in response else response - if params.get('register_as'): - facts[params['register_as']] = response_body - elif type(response_body) is dict and response_body.get('name') and response_body.get('type'): - object_name = re.sub(INVALID_IDENTIFIER_SYMBOLS, '_', response_body['name'].lower()) - fact_name = '%s_%s' % (response_body['type'], object_name) - facts[fact_name] = response_body - return facts - - -def copy_identity_properties(source_obj, dest_obj): - for property_name in IDENTITY_PROPERTIES: - if property_name in source_obj: - dest_obj[property_name] = source_obj[property_name] - return dest_obj - - -def is_object_ref(d): - """ - Checks if a dictionary is a reference object. The dictionary is considered to be a - reference object when it contains non-empty 'id' and 'type' fields. - - :type d: dict - :return: True if passed dictionary is a reference object, otherwise False - """ - has_id = 'id' in d.keys() and d['id'] - has_type = 'type' in d.keys() and d['type'] - return has_id and has_type - - -def equal_object_refs(d1, d2): - """ - Checks whether two references point to the same object. - - :type d1: dict - :type d2: dict - :return: True if passed references point to the same object, otherwise False - """ - have_equal_ids = d1['id'] == d2['id'] - have_equal_types = d1['type'] == d2['type'] - return have_equal_ids and have_equal_types - - -def equal_lists(l1, l2): - """ - Checks whether two lists are equal. The order of elements in the arrays is important. - - :type l1: list - :type l2: list - :return: True if passed lists, their elements and order of elements are equal. Otherwise, returns False. - """ - if len(l1) != len(l2): - return False - - for v1, v2 in zip(l1, l2): - if not equal_values(v1, v2): - return False - - return True - - -def equal_dicts(d1, d2, compare_by_reference=True): - """ - Checks whether two dictionaries are equal. If `compare_by_reference` is set to True, dictionaries referencing - objects are compared using `equal_object_refs` method. Otherwise, every key and value is checked. - - :type d1: dict - :type d2: dict - :param compare_by_reference: if True, dictionaries referencing objects are compared using `equal_object_refs` method - :return: True if passed dicts are equal. Otherwise, returns False. - """ - if compare_by_reference and is_object_ref(d1) and is_object_ref(d2): - return equal_object_refs(d1, d2) - - if len(d1) != len(d2): - return False - - for key, v1 in d1.items(): - if key not in d2: - return False - - v2 = d2[key] - if not equal_values(v1, v2): - return False - - return True - - -def equal_values(v1, v2): - """ - Checks whether types and content of two values are the same. In case of complex objects, the method might be - called recursively. - - :param v1: first value - :param v2: second value - :return: True if types and content of passed values are equal. Otherwise, returns False. - :rtype: bool - """ - - # string-like values might have same text but different types, so checking them separately - if is_string(v1) and is_string(v2): - return to_text(v1) == to_text(v2) - - if type(v1) != type(v2): - return False - value_type = type(v1) - - if value_type == list: - return equal_lists(v1, v2) - elif value_type == dict: - return equal_dicts(v1, v2) - else: - return v1 == v2 - - -def equal_objects(d1, d2): - """ - Checks whether two objects are equal. Ignores special object properties (e.g. 'id', 'version') and - properties with None and empty values. In case properties contains a reference to the other object, - only object identities (ids and types) are checked. Also, if an array field contains multiple references - to the same object, duplicates are ignored when comparing objects. - - :type d1: dict - :type d2: dict - :return: True if passed objects and their properties are equal. Otherwise, returns False. - """ - - def prepare_data_for_comparison(d): - d = dict((k, d[k]) for k in d.keys() if k not in NON_COMPARABLE_PROPERTIES and d[k]) - d = delete_ref_duplicates(d) - return d - - d1 = prepare_data_for_comparison(d1) - d2 = prepare_data_for_comparison(d2) - return equal_dicts(d1, d2, compare_by_reference=False) - - -def delete_ref_duplicates(d): - """ - Removes reference duplicates from array fields: if an array contains multiple items and some of - them refer to the same object, only unique references are preserved (duplicates are removed). - - :param d: dict with data - :type d: dict - :return: dict without reference duplicates - """ - - def delete_ref_duplicates_from_list(refs): - if all(type(i) == dict and is_object_ref(i) for i in refs): - unique_refs = set() - unique_list = list() - for i in refs: - key = (i['id'], i['type']) - if key not in unique_refs: - unique_refs.add(key) - unique_list.append(i) - - return list(unique_list) - - else: - return refs - - if not d: - return d - - modified_d = {} - for k, v in iteritems(d): - if type(v) == list: - modified_d[k] = delete_ref_duplicates_from_list(v) - elif type(v) == dict: - modified_d[k] = delete_ref_duplicates(v) - else: - modified_d[k] = v - return modified_d diff --git a/plugins/module_utils/network/ftd/configuration.py b/plugins/module_utils/network/ftd/configuration.py deleted file mode 100644 index d8c92758f5..0000000000 --- a/plugins/module_utils/network/ftd/configuration.py +++ /dev/null @@ -1,565 +0,0 @@ -# Copyright (c) 2018 Cisco and/or its affiliates. -# -# 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 . -# -import copy -from functools import partial - -from ansible_collections.community.general.plugins.module_utils.network.ftd.common import HTTPMethod, equal_objects, FtdConfigurationError, \ - FtdServerError, ResponseParams, copy_identity_properties, FtdUnexpectedResponse -from ansible_collections.community.general.plugins.module_utils.network.ftd.fdm_swagger_client import OperationField, ValidationError -from ansible.module_utils.six import iteritems - -DEFAULT_PAGE_SIZE = 10 -DEFAULT_OFFSET = 0 - -UNPROCESSABLE_ENTITY_STATUS = 422 -INVALID_UUID_ERROR_MESSAGE = "Validation failed due to an invalid UUID" -DUPLICATE_NAME_ERROR_MESSAGE = "Validation failed due to a duplicate name" - -MULTIPLE_DUPLICATES_FOUND_ERROR = ( - "Multiple objects matching specified filters are found. " - "Please, define filters more precisely to match one object exactly." -) -DUPLICATE_ERROR = ( - "Cannot add a new object. " - "An object with the same name but different parameters already exists." -) -ADD_OPERATION_NOT_SUPPORTED_ERROR = ( - "Cannot add a new object while executing an upsert request. " - "Creation of objects with this type is not supported." -) - -PATH_PARAMS_FOR_DEFAULT_OBJ = {'objId': 'default'} - - -class OperationNamePrefix: - ADD = 'add' - EDIT = 'edit' - GET = 'get' - DELETE = 'delete' - UPSERT = 'upsert' - - -class QueryParams: - FILTER = 'filter' - - -class ParamName: - QUERY_PARAMS = 'query_params' - PATH_PARAMS = 'path_params' - DATA = 'data' - FILTERS = 'filters' - - -class CheckModeException(Exception): - pass - - -class FtdInvalidOperationNameError(Exception): - def __init__(self, operation_name): - super(FtdInvalidOperationNameError, self).__init__(operation_name) - self.operation_name = operation_name - - -class OperationChecker(object): - - @classmethod - def is_add_operation(cls, operation_name, operation_spec): - """ - Check if operation defined with 'operation_name' is add object operation according to 'operation_spec'. - - :param operation_name: name of the operation being called by the user - :type operation_name: str - :param operation_spec: specification of the operation being called by the user - :type operation_spec: dict - :return: True if the called operation is add object operation, otherwise False - :rtype: bool - """ - # Some endpoints have non-CRUD operations, so checking operation name is required in addition to the HTTP method - return operation_name.startswith(OperationNamePrefix.ADD) and is_post_request(operation_spec) - - @classmethod - def is_edit_operation(cls, operation_name, operation_spec): - """ - Check if operation defined with 'operation_name' is edit object operation according to 'operation_spec'. - - :param operation_name: name of the operation being called by the user - :type operation_name: str - :param operation_spec: specification of the operation being called by the user - :type operation_spec: dict - :return: True if the called operation is edit object operation, otherwise False - :rtype: bool - """ - # Some endpoints have non-CRUD operations, so checking operation name is required in addition to the HTTP method - return operation_name.startswith(OperationNamePrefix.EDIT) and is_put_request(operation_spec) - - @classmethod - def is_delete_operation(cls, operation_name, operation_spec): - """ - Check if operation defined with 'operation_name' is delete object operation according to 'operation_spec'. - - :param operation_name: name of the operation being called by the user - :type operation_name: str - :param operation_spec: specification of the operation being called by the user - :type operation_spec: dict - :return: True if the called operation is delete object operation, otherwise False - :rtype: bool - """ - # Some endpoints have non-CRUD operations, so checking operation name is required in addition to the HTTP method - return operation_name.startswith(OperationNamePrefix.DELETE) \ - and operation_spec[OperationField.METHOD] == HTTPMethod.DELETE - - @classmethod - def is_get_list_operation(cls, operation_name, operation_spec): - """ - Check if operation defined with 'operation_name' is get list of objects operation according to 'operation_spec'. - - :param operation_name: name of the operation being called by the user - :type operation_name: str - :param operation_spec: specification of the operation being called by the user - :type operation_spec: dict - :return: True if the called operation is get a list of objects operation, otherwise False - :rtype: bool - """ - return operation_spec[OperationField.METHOD] == HTTPMethod.GET \ - and operation_spec[OperationField.RETURN_MULTIPLE_ITEMS] - - @classmethod - def is_get_operation(cls, operation_name, operation_spec): - """ - Check if operation defined with 'operation_name' is get objects operation according to 'operation_spec'. - - :param operation_name: name of the operation being called by the user - :type operation_name: str - :param operation_spec: specification of the operation being called by the user - :type operation_spec: dict - :return: True if the called operation is get object operation, otherwise False - :rtype: bool - """ - return operation_spec[OperationField.METHOD] == HTTPMethod.GET \ - and not operation_spec[OperationField.RETURN_MULTIPLE_ITEMS] - - @classmethod - def is_upsert_operation(cls, operation_name): - """ - Check if operation defined with 'operation_name' is upsert objects operation according to 'operation_name'. - - :param operation_name: name of the operation being called by the user - :type operation_name: str - :return: True if the called operation is upsert object operation, otherwise False - :rtype: bool - """ - return operation_name.startswith(OperationNamePrefix.UPSERT) - - @classmethod - def is_find_by_filter_operation(cls, operation_name, params, operation_spec): - """ - Checks whether the called operation is 'find by filter'. This operation fetches all objects and finds - the matching ones by the given filter. As filtering is done on the client side, this operation should be used - only when selected filters are not implemented on the server side. - - :param operation_name: name of the operation being called by the user - :type operation_name: str - :param operation_spec: specification of the operation being called by the user - :type operation_spec: dict - :param params: params - params should contain 'filters' - :return: True if the called operation is find by filter, otherwise False - :rtype: bool - """ - is_get_list = cls.is_get_list_operation(operation_name, operation_spec) - return is_get_list and ParamName.FILTERS in params and params[ParamName.FILTERS] - - @classmethod - def is_upsert_operation_supported(cls, operations): - """ - Checks if all operations required for upsert object operation are defined in 'operations'. - - :param operations: specification of the operations supported by model - :type operations: dict - :return: True if all criteria required to provide requested called operation are satisfied, otherwise False - :rtype: bool - """ - has_edit_op = next((name for name, spec in iteritems(operations) if cls.is_edit_operation(name, spec)), None) - has_get_list_op = next((name for name, spec in iteritems(operations) - if cls.is_get_list_operation(name, spec)), None) - return has_edit_op and has_get_list_op - - -class BaseConfigurationResource(object): - - def __init__(self, conn, check_mode=False): - self._conn = conn - self.config_changed = False - self._operation_spec_cache = {} - self._models_operations_specs_cache = {} - self._check_mode = check_mode - self._operation_checker = OperationChecker - self._system_info = None - - def execute_operation(self, op_name, params): - """ - Allow user request execution of simple operations(natively supported by API provider) as well as complex - operations(operations that are implemented as a set of simple operations). - - :param op_name: name of the operation being called by the user - :type op_name: str - :param params: definition of the params that operation should be executed with - :type params: dict - :return: Result of the operation being executed - :rtype: dict - """ - if self._operation_checker.is_upsert_operation(op_name): - return self.upsert_object(op_name, params) - else: - return self.crud_operation(op_name, params) - - def crud_operation(self, op_name, params): - """ - Allow user request execution of simple operations(natively supported by API provider) only. - - :param op_name: name of the operation being called by the user - :type op_name: str - :param params: definition of the params that operation should be executed with - :type params: dict - :return: Result of the operation being executed - :rtype: dict - """ - op_spec = self.get_operation_spec(op_name) - if op_spec is None: - raise FtdInvalidOperationNameError(op_name) - - if self._operation_checker.is_add_operation(op_name, op_spec): - resp = self.add_object(op_name, params) - elif self._operation_checker.is_edit_operation(op_name, op_spec): - resp = self.edit_object(op_name, params) - elif self._operation_checker.is_delete_operation(op_name, op_spec): - resp = self.delete_object(op_name, params) - elif self._operation_checker.is_find_by_filter_operation(op_name, params, op_spec): - resp = list(self.get_objects_by_filter(op_name, params)) - else: - resp = self.send_general_request(op_name, params) - return resp - - def get_operation_spec(self, operation_name): - if operation_name not in self._operation_spec_cache: - self._operation_spec_cache[operation_name] = self._conn.get_operation_spec(operation_name) - return self._operation_spec_cache[operation_name] - - def get_operation_specs_by_model_name(self, model_name): - if model_name not in self._models_operations_specs_cache: - model_op_specs = self._conn.get_operation_specs_by_model_name(model_name) - self._models_operations_specs_cache[model_name] = model_op_specs - for op_name, op_spec in iteritems(model_op_specs): - self._operation_spec_cache.setdefault(op_name, op_spec) - return self._models_operations_specs_cache[model_name] - - def get_objects_by_filter(self, operation_name, params): - - def match_filters(filter_params, obj): - for k, v in iteritems(filter_params): - if k not in obj or obj[k] != v: - return False - return True - - dummy, query_params, path_params = _get_user_params(params) - # copy required params to avoid mutation of passed `params` dict - url_params = {ParamName.QUERY_PARAMS: dict(query_params), ParamName.PATH_PARAMS: dict(path_params)} - - filters = params.get(ParamName.FILTERS) or {} - if QueryParams.FILTER not in url_params[ParamName.QUERY_PARAMS] and 'name' in filters: - # most endpoints only support filtering by name, so remaining `filters` are applied on returned objects - url_params[ParamName.QUERY_PARAMS][QueryParams.FILTER] = self._stringify_name_filter(filters) - - item_generator = iterate_over_pageable_resource( - partial(self.send_general_request, operation_name=operation_name), url_params - ) - return (i for i in item_generator if match_filters(filters, i)) - - def _stringify_name_filter(self, filters): - build_version = self.get_build_version() - if build_version >= '6.4.0': - return "fts~%s" % filters['name'] - return "name:%s" % filters['name'] - - def _fetch_system_info(self): - if not self._system_info: - params = {ParamName.PATH_PARAMS: PATH_PARAMS_FOR_DEFAULT_OBJ} - self._system_info = self.send_general_request('getSystemInformation', params) - - return self._system_info - - def get_build_version(self): - system_info = self._fetch_system_info() - return system_info['databaseInfo']['buildVersion'] - - def add_object(self, operation_name, params): - def is_duplicate_name_error(err): - return err.code == UNPROCESSABLE_ENTITY_STATUS and DUPLICATE_NAME_ERROR_MESSAGE in str(err) - - try: - return self.send_general_request(operation_name, params) - except FtdServerError as e: - if is_duplicate_name_error(e): - return self._check_equality_with_existing_object(operation_name, params, e) - else: - raise e - - def _check_equality_with_existing_object(self, operation_name, params, e): - """ - Looks for an existing object that caused "object duplicate" error and - checks whether it corresponds to the one specified in `params`. - - In case a single object is found and it is equal to one we are trying - to create, the existing object is returned. - - When the existing object is not equal to the object being created or - several objects are returned, an exception is raised. - """ - model_name = self.get_operation_spec(operation_name)[OperationField.MODEL_NAME] - existing_obj = self._find_object_matching_params(model_name, params) - - if existing_obj is not None: - if equal_objects(existing_obj, params[ParamName.DATA]): - return existing_obj - else: - raise FtdConfigurationError(DUPLICATE_ERROR, existing_obj) - - raise e - - def _find_object_matching_params(self, model_name, params): - get_list_operation = self._find_get_list_operation(model_name) - if not get_list_operation: - return None - - data = params[ParamName.DATA] - if not params.get(ParamName.FILTERS): - params[ParamName.FILTERS] = {'name': data['name']} - - obj = None - filtered_objs = self.get_objects_by_filter(get_list_operation, params) - - for i, obj in enumerate(filtered_objs): - if i > 0: - raise FtdConfigurationError(MULTIPLE_DUPLICATES_FOUND_ERROR) - obj = obj - - return obj - - def _find_get_list_operation(self, model_name): - operations = self.get_operation_specs_by_model_name(model_name) or {} - return next(( - op for op, op_spec in operations.items() - if self._operation_checker.is_get_list_operation(op, op_spec)), None) - - def _find_get_operation(self, model_name): - operations = self.get_operation_specs_by_model_name(model_name) or {} - return next(( - op for op, op_spec in operations.items() - if self._operation_checker.is_get_operation(op, op_spec)), None) - - def delete_object(self, operation_name, params): - def is_invalid_uuid_error(err): - return err.code == UNPROCESSABLE_ENTITY_STATUS and INVALID_UUID_ERROR_MESSAGE in str(err) - - try: - return self.send_general_request(operation_name, params) - except FtdServerError as e: - if is_invalid_uuid_error(e): - return {'status': 'Referenced object does not exist'} - else: - raise e - - def edit_object(self, operation_name, params): - data, dummy, path_params = _get_user_params(params) - - model_name = self.get_operation_spec(operation_name)[OperationField.MODEL_NAME] - get_operation = self._find_get_operation(model_name) - - if get_operation: - existing_object = self.send_general_request(get_operation, {ParamName.PATH_PARAMS: path_params}) - if not existing_object: - raise FtdConfigurationError('Referenced object does not exist') - elif equal_objects(existing_object, data): - return existing_object - - return self.send_general_request(operation_name, params) - - def send_general_request(self, operation_name, params): - def stop_if_check_mode(): - if self._check_mode: - raise CheckModeException() - - self.validate_params(operation_name, params) - stop_if_check_mode() - - data, query_params, path_params = _get_user_params(params) - op_spec = self.get_operation_spec(operation_name) - url, method = op_spec[OperationField.URL], op_spec[OperationField.METHOD] - - return self._send_request(url, method, data, path_params, query_params) - - def _send_request(self, url_path, http_method, body_params=None, path_params=None, query_params=None): - def raise_for_failure(resp): - if not resp[ResponseParams.SUCCESS]: - raise FtdServerError(resp[ResponseParams.RESPONSE], resp[ResponseParams.STATUS_CODE]) - - response = self._conn.send_request(url_path=url_path, http_method=http_method, body_params=body_params, - path_params=path_params, query_params=query_params) - raise_for_failure(response) - if http_method != HTTPMethod.GET: - self.config_changed = True - return response[ResponseParams.RESPONSE] - - def validate_params(self, operation_name, params): - report = {} - op_spec = self.get_operation_spec(operation_name) - data, query_params, path_params = _get_user_params(params) - - def validate(validation_method, field_name, user_params): - key = 'Invalid %s provided' % field_name - try: - is_valid, validation_report = validation_method(operation_name, user_params) - if not is_valid: - report[key] = validation_report - except Exception as e: - report[key] = str(e) - return report - - validate(self._conn.validate_query_params, ParamName.QUERY_PARAMS, query_params) - validate(self._conn.validate_path_params, ParamName.PATH_PARAMS, path_params) - if is_post_request(op_spec) or is_put_request(op_spec): - validate(self._conn.validate_data, ParamName.DATA, data) - - if report: - raise ValidationError(report) - - @staticmethod - def _get_operation_name(checker, operations): - return next((op_name for op_name, op_spec in iteritems(operations) if checker(op_name, op_spec)), None) - - def _add_upserted_object(self, model_operations, params): - add_op_name = self._get_operation_name(self._operation_checker.is_add_operation, model_operations) - if not add_op_name: - raise FtdConfigurationError(ADD_OPERATION_NOT_SUPPORTED_ERROR) - return self.add_object(add_op_name, params) - - def _edit_upserted_object(self, model_operations, existing_object, params): - edit_op_name = self._get_operation_name(self._operation_checker.is_edit_operation, model_operations) - _set_default(params, 'path_params', {}) - _set_default(params, 'data', {}) - - params['path_params']['objId'] = existing_object['id'] - copy_identity_properties(existing_object, params['data']) - return self.edit_object(edit_op_name, params) - - def upsert_object(self, op_name, params): - """ - Updates an object if it already exists, or tries to create a new one if there is no - such object. If multiple objects match filter criteria, or add operation is not supported, - the exception is raised. - - :param op_name: upsert operation name - :type op_name: str - :param params: params that upsert operation should be executed with - :type params: dict - :return: upserted object representation - :rtype: dict - """ - - def extract_and_validate_model(): - model = op_name[len(OperationNamePrefix.UPSERT):] - if not self._conn.get_model_spec(model): - raise FtdInvalidOperationNameError(op_name) - return model - - model_name = extract_and_validate_model() - model_operations = self.get_operation_specs_by_model_name(model_name) - - if not self._operation_checker.is_upsert_operation_supported(model_operations): - raise FtdInvalidOperationNameError(op_name) - - existing_obj = self._find_object_matching_params(model_name, params) - if existing_obj: - equal_to_existing_obj = equal_objects(existing_obj, params[ParamName.DATA]) - return existing_obj if equal_to_existing_obj \ - else self._edit_upserted_object(model_operations, existing_obj, params) - else: - return self._add_upserted_object(model_operations, params) - - -def _set_default(params, field_name, value): - if field_name not in params or params[field_name] is None: - params[field_name] = value - - -def is_post_request(operation_spec): - return operation_spec[OperationField.METHOD] == HTTPMethod.POST - - -def is_put_request(operation_spec): - return operation_spec[OperationField.METHOD] == HTTPMethod.PUT - - -def _get_user_params(params): - return params.get(ParamName.DATA) or {}, params.get(ParamName.QUERY_PARAMS) or {}, params.get( - ParamName.PATH_PARAMS) or {} - - -def iterate_over_pageable_resource(resource_func, params): - """ - A generator function that iterates over a resource that supports pagination and lazily returns present items - one by one. - - :param resource_func: function that receives `params` argument and returns a page of objects - :type resource_func: callable - :param params: initial dictionary of parameters that will be passed to the resource_func. - Should contain `query_params` inside. - :type params: dict - :return: an iterator containing returned items - :rtype: iterator of dict - """ - # creating a copy not to mutate passed dict - params = copy.deepcopy(params) - params[ParamName.QUERY_PARAMS].setdefault('limit', DEFAULT_PAGE_SIZE) - params[ParamName.QUERY_PARAMS].setdefault('offset', DEFAULT_OFFSET) - limit = int(params[ParamName.QUERY_PARAMS]['limit']) - - def received_less_items_than_requested(items_in_response, items_expected): - if items_in_response == items_expected: - return False - elif items_in_response < items_expected: - return True - - raise FtdUnexpectedResponse( - "Get List of Objects Response from the server contains more objects than requested. " - "There are {0} item(s) in the response while {1} was(ere) requested".format( - items_in_response, items_expected) - ) - - while True: - result = resource_func(params=params) - - for item in result['items']: - yield item - - if received_less_items_than_requested(len(result['items']), limit): - break - - # creating a copy not to mutate existing dict - params = copy.deepcopy(params) - query_params = params[ParamName.QUERY_PARAMS] - query_params['offset'] = int(query_params['offset']) + limit diff --git a/plugins/module_utils/network/ftd/device.py b/plugins/module_utils/network/ftd/device.py deleted file mode 100644 index 47b0eb3a43..0000000000 --- a/plugins/module_utils/network/ftd/device.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2019 Cisco and/or its affiliates. -# -# 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 . -# - -from ansible.module_utils.six.moves.urllib.parse import urlparse - -try: - from kick.device2.ftd5500x.actions.ftd5500x import Ftd5500x - from kick.device2.kp.actions import Kp - - HAS_KICK = True -except ImportError: - HAS_KICK = False - - -def assert_kick_is_installed(module): - if not HAS_KICK: - module.fail_json(msg='Firepower-kickstart library is required to run this module. ' - 'Please, install the library with `pip install firepower-kickstart` ' - 'command and run the playbook again.') - - -class FtdModel: - FTD_ASA5506_X = 'Cisco ASA5506-X Threat Defense' - FTD_ASA5508_X = 'Cisco ASA5508-X Threat Defense' - FTD_ASA5516_X = 'Cisco ASA5516-X Threat Defense' - - FTD_2110 = 'Cisco Firepower 2110 Threat Defense' - FTD_2120 = 'Cisco Firepower 2120 Threat Defense' - FTD_2130 = 'Cisco Firepower 2130 Threat Defense' - FTD_2140 = 'Cisco Firepower 2140 Threat Defense' - - @classmethod - def supported_models(cls): - return [getattr(cls, item) for item in dir(cls) if item.startswith('FTD_')] - - -class FtdPlatformFactory(object): - - @staticmethod - def create(model, module_params): - for cls in AbstractFtdPlatform.__subclasses__(): - if cls.supports_ftd_model(model): - return cls(module_params) - raise ValueError("FTD model '%s' is not supported by this module." % model) - - -class AbstractFtdPlatform(object): - PLATFORM_MODELS = [] - - def install_ftd_image(self, params): - raise NotImplementedError('The method should be overridden in subclass') - - @classmethod - def supports_ftd_model(cls, model): - return model in cls.PLATFORM_MODELS - - @staticmethod - def parse_rommon_file_location(rommon_file_location): - rommon_url = urlparse(rommon_file_location) - if rommon_url.scheme != 'tftp': - raise ValueError('The ROMMON image must be downloaded from TFTP server, other protocols are not supported.') - return rommon_url.netloc, rommon_url.path - - -class Ftd2100Platform(AbstractFtdPlatform): - PLATFORM_MODELS = [FtdModel.FTD_2110, FtdModel.FTD_2120, FtdModel.FTD_2130, FtdModel.FTD_2140] - - def __init__(self, params): - self._ftd = Kp(hostname=params["device_hostname"], - login_username=params["device_username"], - login_password=params["device_password"], - sudo_password=params.get("device_sudo_password") or params["device_password"]) - - def install_ftd_image(self, params): - line = self._ftd.ssh_console(ip=params["console_ip"], - port=params["console_port"], - username=params["console_username"], - password=params["console_password"]) - - try: - rommon_server, rommon_path = self.parse_rommon_file_location(params["rommon_file_location"]) - line.baseline_fp2k_ftd(tftp_server=rommon_server, - rommon_file=rommon_path, - uut_hostname=params["device_hostname"], - uut_username=params["device_username"], - uut_password=params.get("device_new_password") or params["device_password"], - uut_ip=params["device_ip"], - uut_netmask=params["device_netmask"], - uut_gateway=params["device_gateway"], - dns_servers=params["dns_server"], - search_domains=params["search_domains"], - fxos_url=params["image_file_location"], - ftd_version=params["image_version"]) - finally: - line.disconnect() - - -class FtdAsa5500xPlatform(AbstractFtdPlatform): - PLATFORM_MODELS = [FtdModel.FTD_ASA5506_X, FtdModel.FTD_ASA5508_X, FtdModel.FTD_ASA5516_X] - - def __init__(self, params): - self._ftd = Ftd5500x(hostname=params["device_hostname"], - login_password=params["device_password"], - sudo_password=params.get("device_sudo_password") or params["device_password"]) - - def install_ftd_image(self, params): - line = self._ftd.ssh_console(ip=params["console_ip"], - port=params["console_port"], - username=params["console_username"], - password=params["console_password"]) - try: - rommon_server, rommon_path = self.parse_rommon_file_location(params["rommon_file_location"]) - line.rommon_to_new_image(rommon_tftp_server=rommon_server, - rommon_image=rommon_path, - pkg_image=params["image_file_location"], - uut_ip=params["device_ip"], - uut_netmask=params["device_netmask"], - uut_gateway=params["device_gateway"], - dns_server=params["dns_server"], - search_domains=params["search_domains"], - hostname=params["device_hostname"]) - finally: - line.disconnect() diff --git a/plugins/module_utils/network/ftd/fdm_swagger_client.py b/plugins/module_utils/network/ftd/fdm_swagger_client.py deleted file mode 100644 index f7d4114be2..0000000000 --- a/plugins/module_utils/network/ftd/fdm_swagger_client.py +++ /dev/null @@ -1,638 +0,0 @@ -# Copyright (c) 2018 Cisco and/or its affiliates. -# -# 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 . -# - -from ansible_collections.community.general.plugins.module_utils.network.ftd.common import HTTPMethod -from ansible.module_utils.six import integer_types, string_types, iteritems - -FILE_MODEL_NAME = '_File' -SUCCESS_RESPONSE_CODE = '200' -DELETE_PREFIX = 'delete' - - -class OperationField: - URL = 'url' - METHOD = 'method' - PARAMETERS = 'parameters' - MODEL_NAME = 'modelName' - DESCRIPTION = 'description' - RETURN_MULTIPLE_ITEMS = 'returnMultipleItems' - TAGS = "tags" - - -class SpecProp: - DEFINITIONS = 'definitions' - OPERATIONS = 'operations' - MODELS = 'models' - MODEL_OPERATIONS = 'model_operations' - - -class PropName: - ENUM = 'enum' - TYPE = 'type' - REQUIRED = 'required' - INVALID_TYPE = 'invalid_type' - REF = '$ref' - ALL_OF = 'allOf' - BASE_PATH = 'basePath' - PATHS = 'paths' - OPERATION_ID = 'operationId' - SCHEMA = 'schema' - ITEMS = 'items' - PROPERTIES = 'properties' - RESPONSES = 'responses' - NAME = 'name' - DESCRIPTION = 'description' - - -class PropType: - STRING = 'string' - BOOLEAN = 'boolean' - INTEGER = 'integer' - NUMBER = 'number' - OBJECT = 'object' - ARRAY = 'array' - FILE = 'file' - - -class OperationParams: - PATH = 'path' - QUERY = 'query' - - -class QueryParams: - FILTER = 'filter' - - -class PathParams: - OBJ_ID = 'objId' - - -def _get_model_name_from_url(schema_ref): - path = schema_ref.split('/') - return path[len(path) - 1] - - -class IllegalArgumentException(ValueError): - """ - Exception raised when the function parameters: - - not all passed - - empty string - - wrong type - """ - pass - - -class ValidationError(ValueError): - pass - - -class FdmSwaggerParser: - _definitions = None - _base_path = None - - def parse_spec(self, spec, docs=None): - """ - This method simplifies a swagger format, resolves a model name for each operation, and adds documentation for - each operation and model if it is provided. - - :param spec: An API specification in the swagger format, see - - :type spec: dict - :param spec: A documentation map containing descriptions for models, operations and operation parameters. - :type docs: dict - :rtype: dict - :return: - Ex. - The models field contains model definition from swagger see - <#https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#definitions> - { - 'models':{ - 'model_name':{...}, - ... - }, - 'operations':{ - 'operation_name':{ - 'method': 'get', #post, put, delete - 'url': '/api/fdm/v2/object/networks', #url already contains a value from `basePath` - 'modelName': 'NetworkObject', # it is a link to the model from 'models' - # None - for a delete operation or we don't have information - # '_File' - if an endpoint works with files - 'returnMultipleItems': False, # shows if the operation returns a single item or an item list - 'parameters': { - 'path':{ - 'param_name':{ - 'type': 'string'#integer, boolean, number - 'required' True #False - } - ... - }, - 'query':{ - 'param_name':{ - 'type': 'string'#integer, boolean, number - 'required' True #False - } - ... - } - } - }, - ... - }, - 'model_operations':{ - 'model_name':{ # a list of operations available for the current model - 'operation_name':{ - ... # the same as in the operations section - }, - ... - }, - ... - } - } - """ - self._definitions = spec[SpecProp.DEFINITIONS] - self._base_path = spec[PropName.BASE_PATH] - operations = self._get_operations(spec) - - if docs: - operations = self._enrich_operations_with_docs(operations, docs) - self._definitions = self._enrich_definitions_with_docs(self._definitions, docs) - - return { - SpecProp.MODELS: self._definitions, - SpecProp.OPERATIONS: operations, - SpecProp.MODEL_OPERATIONS: self._get_model_operations(operations) - } - - @property - def base_path(self): - return self._base_path - - def _get_model_operations(self, operations): - model_operations = {} - for operations_name, params in iteritems(operations): - model_name = params[OperationField.MODEL_NAME] - model_operations.setdefault(model_name, {})[operations_name] = params - return model_operations - - def _get_operations(self, spec): - paths_dict = spec[PropName.PATHS] - operations_dict = {} - for url, operation_params in iteritems(paths_dict): - for method, params in iteritems(operation_params): - operation = { - OperationField.METHOD: method, - OperationField.URL: self._base_path + url, - OperationField.MODEL_NAME: self._get_model_name(method, params), - OperationField.RETURN_MULTIPLE_ITEMS: self._return_multiple_items(params), - OperationField.TAGS: params.get(OperationField.TAGS, []) - } - if OperationField.PARAMETERS in params: - operation[OperationField.PARAMETERS] = self._get_rest_params(params[OperationField.PARAMETERS]) - - operation_id = params[PropName.OPERATION_ID] - operations_dict[operation_id] = operation - return operations_dict - - def _enrich_operations_with_docs(self, operations, docs): - def get_operation_docs(op): - op_url = op[OperationField.URL][len(self._base_path):] - return docs[PropName.PATHS].get(op_url, {}).get(op[OperationField.METHOD], {}) - - for operation in operations.values(): - operation_docs = get_operation_docs(operation) - operation[OperationField.DESCRIPTION] = operation_docs.get(PropName.DESCRIPTION, '') - - if OperationField.PARAMETERS in operation: - param_descriptions = dict(( - (p[PropName.NAME], p[PropName.DESCRIPTION]) - for p in operation_docs.get(OperationField.PARAMETERS, {}) - )) - - for param_name, params_spec in operation[OperationField.PARAMETERS][OperationParams.PATH].items(): - params_spec[OperationField.DESCRIPTION] = param_descriptions.get(param_name, '') - - for param_name, params_spec in operation[OperationField.PARAMETERS][OperationParams.QUERY].items(): - params_spec[OperationField.DESCRIPTION] = param_descriptions.get(param_name, '') - - return operations - - def _enrich_definitions_with_docs(self, definitions, docs): - for model_name, model_def in definitions.items(): - model_docs = docs[SpecProp.DEFINITIONS].get(model_name, {}) - model_def[PropName.DESCRIPTION] = model_docs.get(PropName.DESCRIPTION, '') - for prop_name, prop_spec in model_def.get(PropName.PROPERTIES, {}).items(): - prop_spec[PropName.DESCRIPTION] = model_docs.get(PropName.PROPERTIES, {}).get(prop_name, '') - prop_spec[PropName.REQUIRED] = prop_name in model_def.get(PropName.REQUIRED, []) - return definitions - - def _get_model_name(self, method, params): - if method == HTTPMethod.GET: - return self._get_model_name_from_responses(params) - elif method == HTTPMethod.POST or method == HTTPMethod.PUT: - return self._get_model_name_for_post_put_requests(params) - elif method == HTTPMethod.DELETE: - return self._get_model_name_from_delete_operation(params) - else: - return None - - @staticmethod - def _return_multiple_items(op_params): - """ - Defines if the operation returns one item or a list of items. - - :param op_params: operation specification - :return: True if the operation returns a list of items, otherwise False - """ - try: - schema = op_params[PropName.RESPONSES][SUCCESS_RESPONSE_CODE][PropName.SCHEMA] - return PropName.ITEMS in schema[PropName.PROPERTIES] - except KeyError: - return False - - def _get_model_name_from_delete_operation(self, params): - operation_id = params[PropName.OPERATION_ID] - if operation_id.startswith(DELETE_PREFIX): - model_name = operation_id[len(DELETE_PREFIX):] - if model_name in self._definitions: - return model_name - return None - - def _get_model_name_for_post_put_requests(self, params): - model_name = None - if OperationField.PARAMETERS in params: - body_param_dict = self._get_body_param_from_parameters(params[OperationField.PARAMETERS]) - if body_param_dict: - schema_ref = body_param_dict[PropName.SCHEMA][PropName.REF] - model_name = self._get_model_name_byschema_ref(schema_ref) - if model_name is None: - model_name = self._get_model_name_from_responses(params) - return model_name - - @staticmethod - def _get_body_param_from_parameters(params): - return next((param for param in params if param['in'] == 'body'), None) - - def _get_model_name_from_responses(self, params): - responses = params[PropName.RESPONSES] - if SUCCESS_RESPONSE_CODE in responses: - response = responses[SUCCESS_RESPONSE_CODE][PropName.SCHEMA] - if PropName.REF in response: - return self._get_model_name_byschema_ref(response[PropName.REF]) - elif PropName.PROPERTIES in response: - ref = response[PropName.PROPERTIES][PropName.ITEMS][PropName.ITEMS][PropName.REF] - return self._get_model_name_byschema_ref(ref) - elif (PropName.TYPE in response) and response[PropName.TYPE] == PropType.FILE: - return FILE_MODEL_NAME - else: - return None - - def _get_rest_params(self, params): - path = {} - query = {} - operation_param = { - OperationParams.PATH: path, - OperationParams.QUERY: query - } - for param in params: - in_param = param['in'] - if in_param == OperationParams.QUERY: - query[param[PropName.NAME]] = self._simplify_param_def(param) - elif in_param == OperationParams.PATH: - path[param[PropName.NAME]] = self._simplify_param_def(param) - return operation_param - - @staticmethod - def _simplify_param_def(param): - return { - PropName.TYPE: param[PropName.TYPE], - PropName.REQUIRED: param[PropName.REQUIRED] - } - - def _get_model_name_byschema_ref(self, schema_ref): - model_name = _get_model_name_from_url(schema_ref) - model_def = self._definitions[model_name] - if PropName.ALL_OF in model_def: - return self._get_model_name_byschema_ref(model_def[PropName.ALL_OF][0][PropName.REF]) - else: - return model_name - - -class FdmSwaggerValidator: - def __init__(self, spec): - """ - :param spec: dict - data from FdmSwaggerParser().parse_spec() - """ - self._operations = spec[SpecProp.OPERATIONS] - self._models = spec[SpecProp.MODELS] - - def validate_data(self, operation_name, data=None): - """ - Validate data for the post|put requests - :param operation_name: string - The value must be non empty string. - The operation name is used to get a model specification - :param data: dict - The value must be in the format that the model(from operation) expects - :rtype: (bool, string|dict) - :return: - (True, None) - if data valid - Invalid: - (False, { - 'required': [ #list of the fields that are required but were not present in the data - 'field_name', - 'patent.field_name',# when the nested field is omitted - 'patent.list[2].field_name' # if data is array and one of the field is omitted - ], - 'invalid_type':[ #list of the fields with invalid data - { - 'path': 'objId', #field name or path to the field. Ex. objects[3].id, parent.name - 'expected_type': 'string',# expected type. Ex. 'object', 'array', 'string', 'integer', - # 'boolean', 'number' - 'actually_value': 1 # the value that user passed - } - ] - }) - :raises IllegalArgumentException - 'The operation_name parameter must be a non-empty string' if operation_name is not valid - 'The data parameter must be a dict' if data neither dict or None - '{operation_name} operation does not support' if the spec does not contain the operation - """ - if data is None: - data = {} - - self._check_validate_data_params(data, operation_name) - - operation = self._operations[operation_name] - model = self._models[operation[OperationField.MODEL_NAME]] - status = self._init_report() - - self._validate_object(status, model, data, '') - - if len(status[PropName.REQUIRED]) > 0 or len(status[PropName.INVALID_TYPE]) > 0: - return False, self._delete_empty_field_from_report(status) - return True, None - - def _check_validate_data_params(self, data, operation_name): - if not operation_name or not isinstance(operation_name, string_types): - raise IllegalArgumentException("The operation_name parameter must be a non-empty string") - if not isinstance(data, dict): - raise IllegalArgumentException("The data parameter must be a dict") - if operation_name not in self._operations: - raise IllegalArgumentException("{0} operation does not support".format(operation_name)) - - def validate_query_params(self, operation_name, params): - """ - Validate params for the get requests. Use this method for validating the query part of the url. - :param operation_name: string - The value must be non empty string. - The operation name is used to get a params specification - :param params: dict - should be in the format that the specification(from operation) expects - Ex. - { - 'objId': "string_value", - 'p_integer': 1, - 'p_boolean': True, - 'p_number': 2.3 - } - :rtype:(Boolean, msg) - :return: - (True, None) - if params valid - Invalid: - (False, { - 'required': [ #list of the fields that are required but are not present in the params - 'field_name' - ], - 'invalid_type':[ #list of the fields with invalid data and expected type of the params - { - 'path': 'objId', #field name - 'expected_type': 'string',#expected type. Ex. 'string', 'integer', 'boolean', 'number' - 'actually_value': 1 # the value that user passed - } - ] - }) - :raises IllegalArgumentException - 'The operation_name parameter must be a non-empty string' if operation_name is not valid - 'The params parameter must be a dict' if params neither dict or None - '{operation_name} operation does not support' if the spec does not contain the operation - """ - return self._validate_url_params(operation_name, params, resource=OperationParams.QUERY) - - def validate_path_params(self, operation_name, params): - """ - Validate params for the get requests. Use this method for validating the path part of the url. - :param operation_name: string - The value must be non empty string. - The operation name is used to get a params specification - :param params: dict - should be in the format that the specification(from operation) expects - - Ex. - { - 'objId': "string_value", - 'p_integer': 1, - 'p_boolean': True, - 'p_number': 2.3 - } - :rtype:(Boolean, msg) - :return: - (True, None) - if params valid - Invalid: - (False, { - 'required': [ #list of the fields that are required but are not present in the params - 'field_name' - ], - 'invalid_type':[ #list of the fields with invalid data and expected type of the params - { - 'path': 'objId', #field name - 'expected_type': 'string',#expected type. Ex. 'string', 'integer', 'boolean', 'number' - 'actually_value': 1 # the value that user passed - } - ] - }) - :raises IllegalArgumentException - 'The operation_name parameter must be a non-empty string' if operation_name is not valid - 'The params parameter must be a dict' if params neither dict or None - '{operation_name} operation does not support' if the spec does not contain the operation - """ - return self._validate_url_params(operation_name, params, resource=OperationParams.PATH) - - def _validate_url_params(self, operation, params, resource): - if params is None: - params = {} - - self._check_validate_url_params(operation, params) - - operation = self._operations[operation] - if OperationField.PARAMETERS in operation and resource in operation[OperationField.PARAMETERS]: - spec = operation[OperationField.PARAMETERS][resource] - status = self._init_report() - self._check_url_params(status, spec, params) - - if len(status[PropName.REQUIRED]) > 0 or len(status[PropName.INVALID_TYPE]) > 0: - return False, self._delete_empty_field_from_report(status) - return True, None - else: - return True, None - - def _check_validate_url_params(self, operation, params): - if not operation or not isinstance(operation, string_types): - raise IllegalArgumentException("The operation_name parameter must be a non-empty string") - if not isinstance(params, dict): - raise IllegalArgumentException("The params parameter must be a dict") - if operation not in self._operations: - raise IllegalArgumentException("{0} operation does not support".format(operation)) - - def _check_url_params(self, status, spec, params): - for prop_name in spec.keys(): - prop = spec[prop_name] - if prop[PropName.REQUIRED] and prop_name not in params: - status[PropName.REQUIRED].append(prop_name) - continue - if prop_name in params: - expected_type = prop[PropName.TYPE] - value = params[prop_name] - if prop_name in params and not self._is_correct_simple_types(expected_type, value, allow_null=False): - self._add_invalid_type_report(status, '', prop_name, expected_type, value) - - def _validate_object(self, status, model, data, path): - if self._is_enum(model): - self._check_enum(status, model, data, path) - elif self._is_object(model): - self._check_object(status, model, data, path) - - def _is_enum(self, model): - return self._is_string_type(model) and PropName.ENUM in model - - def _check_enum(self, status, model, value, path): - if value is not None and value not in model[PropName.ENUM]: - self._add_invalid_type_report(status, path, '', PropName.ENUM, value) - - def _add_invalid_type_report(self, status, path, prop_name, expected_type, actually_value): - status[PropName.INVALID_TYPE].append({ - 'path': self._create_path_to_field(path, prop_name), - 'expected_type': expected_type, - 'actually_value': actually_value - }) - - def _check_object(self, status, model, data, path): - if data is None: - return - - if not isinstance(data, dict): - self._add_invalid_type_report(status, path, '', PropType.OBJECT, data) - return None - - if PropName.REQUIRED in model: - self._check_required_fields(status, model[PropName.REQUIRED], data, path) - - model_properties = model[PropName.PROPERTIES] - for prop in model_properties.keys(): - if prop in data: - model_prop_val = model_properties[prop] - expected_type = model_prop_val[PropName.TYPE] - actually_value = data[prop] - self._check_types(status, actually_value, expected_type, model_prop_val, path, prop) - - def _check_types(self, status, actually_value, expected_type, model, path, prop_name): - if expected_type == PropType.OBJECT: - ref_model = self._get_model_by_ref(model) - - self._validate_object(status, ref_model, actually_value, - path=self._create_path_to_field(path, prop_name)) - elif expected_type == PropType.ARRAY: - self._check_array(status, model, actually_value, - path=self._create_path_to_field(path, prop_name)) - elif not self._is_correct_simple_types(expected_type, actually_value): - self._add_invalid_type_report(status, path, prop_name, expected_type, actually_value) - - def _get_model_by_ref(self, model_prop_val): - model = _get_model_name_from_url(model_prop_val[PropName.REF]) - return self._models[model] - - def _check_required_fields(self, status, required_fields, data, path): - missed_required_fields = [self._create_path_to_field(path, field) for field in - required_fields if field not in data.keys() or data[field] is None] - if len(missed_required_fields) > 0: - status[PropName.REQUIRED] += missed_required_fields - - def _check_array(self, status, model, data, path): - if data is None: - return - elif not isinstance(data, list): - self._add_invalid_type_report(status, path, '', PropType.ARRAY, data) - else: - item_model = model[PropName.ITEMS] - for i, item_data in enumerate(data): - self._check_types(status, item_data, item_model[PropName.TYPE], item_model, "{0}[{1}]".format(path, i), - '') - - @staticmethod - def _is_correct_simple_types(expected_type, value, allow_null=True): - def is_numeric_string(s): - try: - float(s) - return True - except ValueError: - return False - - if value is None and allow_null: - return True - elif expected_type == PropType.STRING: - return isinstance(value, string_types) - elif expected_type == PropType.BOOLEAN: - return isinstance(value, bool) - elif expected_type == PropType.INTEGER: - is_integer = isinstance(value, integer_types) and not isinstance(value, bool) - is_digit_string = isinstance(value, string_types) and value.isdigit() - return is_integer or is_digit_string - elif expected_type == PropType.NUMBER: - is_number = isinstance(value, (integer_types, float)) and not isinstance(value, bool) - is_numeric_string = isinstance(value, string_types) and is_numeric_string(value) - return is_number or is_numeric_string - return False - - @staticmethod - def _is_string_type(model): - return PropName.TYPE in model and model[PropName.TYPE] == PropType.STRING - - @staticmethod - def _init_report(): - return { - PropName.REQUIRED: [], - PropName.INVALID_TYPE: [] - } - - @staticmethod - def _delete_empty_field_from_report(status): - if not status[PropName.REQUIRED]: - del status[PropName.REQUIRED] - if not status[PropName.INVALID_TYPE]: - del status[PropName.INVALID_TYPE] - return status - - @staticmethod - def _create_path_to_field(path='', field=''): - separator = '' - if path and field: - separator = '.' - return "{0}{1}{2}".format(path, separator, field) - - @staticmethod - def _is_object(model): - return PropName.TYPE in model and model[PropName.TYPE] == PropType.OBJECT diff --git a/plugins/module_utils/network/ftd/operation.py b/plugins/module_utils/network/ftd/operation.py deleted file mode 100644 index ecba70e6b5..0000000000 --- a/plugins/module_utils/network/ftd/operation.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2018 Cisco and/or its affiliates. -# -# 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 . -# - -from ansible_collections.community.general.plugins.module_utils.network.ftd.configuration import ParamName, PATH_PARAMS_FOR_DEFAULT_OBJ - - -class FtdOperations: - """ - Utility class for common operation names - """ - GET_SYSTEM_INFO = 'getSystemInformation' - GET_MANAGEMENT_IP_LIST = 'getManagementIPList' - GET_DNS_SETTING_LIST = 'getDeviceDNSSettingsList' - GET_DNS_SERVER_GROUP = 'getDNSServerGroup' - - -def get_system_info(resource): - """ - Executes `getSystemInformation` operation and returns information about the system. - - :param resource: a BaseConfigurationResource object to connect to the device - :return: a dictionary with system information about the device and its software - """ - path_params = {ParamName.PATH_PARAMS: PATH_PARAMS_FOR_DEFAULT_OBJ} - system_info = resource.execute_operation(FtdOperations.GET_SYSTEM_INFO, path_params) - return system_info diff --git a/plugins/module_utils/network/icx/__init__.py b/plugins/module_utils/network/icx/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/icx/icx.py b/plugins/module_utils/network/icx/icx.py deleted file mode 100644 index cabece78d4..0000000000 --- a/plugins/module_utils/network/icx/icx.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright: (c) 2019, Ansible Project -# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import json -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.module_utils.connection import Connection, ConnectionError - -_DEVICE_CONFIGS = {} - - -def get_connection(module): - return Connection(module._socket_path) - - -def load_config(module, commands): - connection = get_connection(module) - - try: - resp = connection.edit_config(candidate=commands) - return resp.get('response') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - try: - return connection.run_commands(commands=commands, check_rc=check_rc) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def exec_scp(module, command): - connection = Connection(module._socket_path) - return connection.scp(**command) - - -def get_config(module, flags=None, compare=None): - flag_str = ' '.join(to_list(flags)) - try: - return _DEVICE_CONFIGS[flag_str] - except KeyError: - connection = get_connection(module) - try: - out = connection.get_config(flags=flags, compare=compare) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - cfg = to_text(out, errors='surrogate_then_replace').strip() - _DEVICE_CONFIGS[flag_str] = cfg - return cfg - - -def check_args(module, warnings): - pass - - -def get_defaults_flag(module): - connection = get_connection(module) - try: - out = connection.get_defaults_flag() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return to_text(out, errors='surrogate_then_replace').strip() diff --git a/plugins/module_utils/network/ingate/__init__.py b/plugins/module_utils/network/ingate/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/ingate/common.py b/plugins/module_utils/network/ingate/common.py deleted file mode 100644 index ff632520b0..0000000000 --- a/plugins/module_utils/network/ingate/common.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Ingate Systems AB -# 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 - - -try: - from ingate import ingatesdk - HAS_INGATESDK = True -except ImportError: - HAS_INGATESDK = False - - -def ingate_argument_spec(**kwargs): - client_options = dict( - version=dict(choices=['v1'], default='v1'), - scheme=dict(choices=['http', 'https'], required=True), - address=dict(type='str', required=True), - username=dict(type='str', required=True), - password=dict(type='str', required=True, no_log=True), - port=dict(type='int'), - timeout=dict(type='int'), - validate_certs=dict(default=True, type='bool', aliases=['verify_ssl']), - ) - argument_spec = dict( - client=dict(type='dict', required=True, - options=client_options), - ) - argument_spec.update(kwargs) - return argument_spec - - -def ingate_create_client(**kwargs): - api_client = ingate_create_client_noauth(**kwargs) - - # Authenticate and get hold of a security token. - api_client.authenticate() - - # Return the client. - return api_client - - -def ingate_create_client_noauth(**kwargs): - client_params = kwargs['client'] - - # Create API client. - api_client = ingatesdk.Client(client_params['version'], - client_params['scheme'], - client_params['address'], - client_params['username'], - client_params['password'], - port=client_params['port'], - timeout=client_params['timeout']) - - # Check if we should skip SSL Certificate verification. - verify_ssl = client_params.get('validate_certs') - if not verify_ssl: - api_client.skip_verify_certificate() - - # Return the client. - return api_client - - -def is_ingatesdk_installed(module): - if not HAS_INGATESDK: - module.fail_json(msg="The Ingate Python SDK module is required for this module.") diff --git a/plugins/module_utils/network/ironware/__init__.py b/plugins/module_utils/network/ironware/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/ironware/ironware.py b/plugins/module_utils/network/ironware/ironware.py deleted file mode 100644 index f09338de16..0000000000 --- a/plugins/module_utils/network/ironware/ironware.py +++ /dev/null @@ -1,113 +0,0 @@ -# -# Copyright (c) 2017, Paul Baker -# -# 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 . -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, EntityCollection -from ansible.module_utils.connection import Connection, exec_command - -_DEVICE_CONFIG = None -_CONNECTION = None - -ironware_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'), -} - -ironware_argument_spec = { - 'provider': dict(type='dict', options=ironware_provider_spec) -} - -command_spec = { - 'command': dict(key=True), - 'prompt': dict(), - 'answer': dict() -} - - -def get_provider_argspec(): - return ironware_provider_spec - - -def check_args(module): - pass - - -def get_connection(module): - global _CONNECTION - if _CONNECTION: - return _CONNECTION - _CONNECTION = Connection(module._socket_path) - - return _CONNECTION - - -def to_commands(module, commands): - if not isinstance(commands, list): - raise AssertionError('argument must be of type ') - - transform = EntityCollection(module, command_spec) - commands = transform(commands) - - for index, item in enumerate(commands): - if module.check_mode and not item['command'].startswith('show'): - module.warn('only show commands are supported when using check ' - 'mode, not executing `%s`' % item['command']) - - return commands - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - - commands = to_commands(module, to_list(commands)) - - responses = list() - - for cmd in commands: - out = connection.get(**cmd) - responses.append(to_text(out, errors='surrogate_then_replace')) - - return responses - - -def get_config(module, source='running', flags=None): - global _DEVICE_CONFIG - if source == 'running' and flags is None and _DEVICE_CONFIG is not None: - return _DEVICE_CONFIG - else: - conn = get_connection(module) - out = conn.get_config(source=source, flags=flags) - cfg = to_text(out, errors='surrogate_then_replace').strip() - if source == 'running' and flags is None: - _DEVICE_CONFIG = cfg - return cfg - - -def load_config(module, config): - conn = get_connection(module) - conn.edit_config(config) diff --git a/plugins/module_utils/network/netscaler/__init__.py b/plugins/module_utils/network/netscaler/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/netscaler/netscaler.py b/plugins/module_utils/network/netscaler/netscaler.py deleted file mode 100644 index ccf0dbff8f..0000000000 --- a/plugins/module_utils/network/netscaler/netscaler.py +++ /dev/null @@ -1,322 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# -# 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. -# - -import json -import re -import sys - -from ansible.module_utils.basic import env_fallback -from ansible.module_utils.six import binary_type, text_type -from ansible.module_utils._text import to_native - - -class ConfigProxy(object): - - def __init__(self, actual, client, attribute_values_dict, readwrite_attrs, transforms=None, readonly_attrs=None, immutable_attrs=None, json_encodes=None): - transforms = {} if transforms is None else transforms - readonly_attrs = [] if readonly_attrs is None else readonly_attrs - immutable_attrs = [] if immutable_attrs is None else immutable_attrs - json_encodes = [] if json_encodes is None else json_encodes - - # Actual config object from nitro sdk - self.actual = actual - - # nitro client - self.client = client - - # ansible attribute_values_dict - self.attribute_values_dict = attribute_values_dict - - self.readwrite_attrs = readwrite_attrs - self.readonly_attrs = readonly_attrs - self.immutable_attrs = immutable_attrs - self.json_encodes = json_encodes - self.transforms = transforms - - self.attribute_values_processed = {} - for attribute, value in self.attribute_values_dict.items(): - if value is None: - continue - if attribute in transforms: - for transform in self.transforms[attribute]: - if transform == 'bool_yes_no': - if value is True: - value = 'YES' - elif value is False: - value = 'NO' - elif transform == 'bool_on_off': - if value is True: - value = 'ON' - elif value is False: - value = 'OFF' - elif callable(transform): - value = transform(value) - else: - raise Exception('Invalid transform %s' % transform) - self.attribute_values_processed[attribute] = value - - self._copy_attributes_to_actual() - - def _copy_attributes_to_actual(self): - for attribute in self.readwrite_attrs: - if attribute in self.attribute_values_processed: - attribute_value = self.attribute_values_processed[attribute] - - if attribute_value is None: - continue - - # Fallthrough - if attribute in self.json_encodes: - attribute_value = json.JSONEncoder().encode(attribute_value).strip('"') - setattr(self.actual, attribute, attribute_value) - - def __getattr__(self, name): - if name in self.attribute_values_dict: - return self.attribute_values_dict[name] - else: - raise AttributeError('No attribute %s found' % name) - - def add(self): - self.actual.__class__.add(self.client, self.actual) - - def update(self): - return self.actual.__class__.update(self.client, self.actual) - - def delete(self): - self.actual.__class__.delete(self.client, self.actual) - - def get(self, *args, **kwargs): - result = self.actual.__class__.get(self.client, *args, **kwargs) - - return result - - def has_equal_attributes(self, other): - if self.diff_object(other) == {}: - return True - else: - return False - - def diff_object(self, other): - diff_dict = {} - for attribute in self.attribute_values_processed: - # Skip readonly attributes - if attribute not in self.readwrite_attrs: - continue - - # Skip attributes not present in module arguments - if self.attribute_values_processed[attribute] is None: - continue - - # Check existence - if hasattr(other, attribute): - attribute_value = getattr(other, attribute) - else: - diff_dict[attribute] = 'missing from other' - continue - - # Compare values - param_type = self.attribute_values_processed[attribute].__class__ - if attribute_value is None or param_type(attribute_value) != self.attribute_values_processed[attribute]: - str_tuple = ( - type(self.attribute_values_processed[attribute]), - self.attribute_values_processed[attribute], - type(attribute_value), - attribute_value, - ) - diff_dict[attribute] = 'difference. ours: (%s) %s other: (%s) %s' % str_tuple - return diff_dict - - def get_actual_rw_attributes(self, filter='name'): - if self.actual.__class__.count_filtered(self.client, '%s:%s' % (filter, self.attribute_values_dict[filter])) == 0: - return {} - server_list = self.actual.__class__.get_filtered(self.client, '%s:%s' % (filter, self.attribute_values_dict[filter])) - actual_instance = server_list[0] - ret_val = {} - for attribute in self.readwrite_attrs: - if not hasattr(actual_instance, attribute): - continue - ret_val[attribute] = getattr(actual_instance, attribute) - return ret_val - - def get_actual_ro_attributes(self, filter='name'): - if self.actual.__class__.count_filtered(self.client, '%s:%s' % (filter, self.attribute_values_dict[filter])) == 0: - return {} - server_list = self.actual.__class__.get_filtered(self.client, '%s:%s' % (filter, self.attribute_values_dict[filter])) - actual_instance = server_list[0] - ret_val = {} - for attribute in self.readonly_attrs: - if not hasattr(actual_instance, attribute): - continue - ret_val[attribute] = getattr(actual_instance, attribute) - return ret_val - - def get_missing_rw_attributes(self): - return list(set(self.readwrite_attrs) - set(self.get_actual_rw_attributes().keys())) - - def get_missing_ro_attributes(self): - return list(set(self.readonly_attrs) - set(self.get_actual_ro_attributes().keys())) - - -def get_immutables_intersection(config_proxy, keys): - immutables_set = set(config_proxy.immutable_attrs) - keys_set = set(keys) - # Return list of sets' intersection - return list(immutables_set & keys_set) - - -def ensure_feature_is_enabled(client, feature_str): - enabled_features = client.get_enabled_features() - - if enabled_features is None: - enabled_features = [] - - if feature_str not in enabled_features: - client.enable_features(feature_str) - client.save_config() - - -def get_nitro_client(module): - from nssrc.com.citrix.netscaler.nitro.service.nitro_service import nitro_service - - client = nitro_service(module.params['nsip'], module.params['nitro_protocol']) - client.set_credential(module.params['nitro_user'], module.params['nitro_pass']) - client.timeout = float(module.params['nitro_timeout']) - client.certvalidation = module.params['validate_certs'] - return client - - -netscaler_common_arguments = dict( - nsip=dict( - required=True, - fallback=(env_fallback, ['NETSCALER_NSIP']), - ), - nitro_user=dict( - required=True, - fallback=(env_fallback, ['NETSCALER_NITRO_USER']), - no_log=True - ), - nitro_pass=dict( - required=True, - fallback=(env_fallback, ['NETSCALER_NITRO_PASS']), - no_log=True - ), - nitro_protocol=dict( - choices=['http', 'https'], - fallback=(env_fallback, ['NETSCALER_NITRO_PROTOCOL']), - default='http' - ), - validate_certs=dict( - default=True, - type='bool' - ), - nitro_timeout=dict(default=310, type='float'), - state=dict( - choices=[ - 'present', - 'absent', - ], - default='present', - ), - save_config=dict( - type='bool', - default=True, - ), -) - - -loglines = [] - - -def complete_missing_attributes(actual, attrs_list, fill_value=None): - for attribute in attrs_list: - if not hasattr(actual, attribute): - setattr(actual, attribute, fill_value) - - -def log(msg): - loglines.append(msg) - - -def get_ns_version(client): - from nssrc.com.citrix.netscaler.nitro.resource.config.ns.nsversion import nsversion - result = nsversion.get(client) - m = re.match(r'^.*NS(\d+)\.(\d+).*$', result[0].version) - if m is None: - return None - else: - return int(m.group(1)), int(m.group(2)) - - -def get_ns_hardware(client): - from nssrc.com.citrix.netscaler.nitro.resource.config.ns.nshardware import nshardware - result = nshardware.get(client) - return result - - -def monkey_patch_nitro_api(): - - from nssrc.com.citrix.netscaler.nitro.resource.base.Json import Json - - def new_resource_to_string_convert(self, resrc): - # Line below is the actual patch - dict_valid_values = dict((k.replace('_', '', 1), v) for k, v in resrc.__dict__.items() if v) - return json.dumps(dict_valid_values) - Json.resource_to_string_convert = new_resource_to_string_convert - - from nssrc.com.citrix.netscaler.nitro.util.nitro_util import nitro_util - - @classmethod - def object_to_string_new(cls, obj): - output = [] - flds = obj.__dict__ - for k, v in ((k.replace('_', '', 1), v) for k, v in flds.items() if v): - if isinstance(v, bool): - output.append('"%s":%s' % (k, v)) - elif isinstance(v, (binary_type, text_type)): - v = to_native(v, errors='surrogate_or_strict') - output.append('"%s":"%s"' % (k, v)) - elif isinstance(v, int): - output.append('"%s":"%s"' % (k, v)) - return ','.join(output) - - @classmethod - def object_to_string_withoutquotes_new(cls, obj): - output = [] - flds = obj.__dict__ - for k, v in ((k.replace('_', '', 1), v) for k, v in flds.items() if v): - if isinstance(v, (int, bool)): - output.append('%s:%s' % (k, v)) - elif isinstance(v, (binary_type, text_type)): - v = to_native(v, errors='surrogate_or_strict') - output.append('%s:%s' % (k, cls.encode(v))) - return ','.join(output) - - nitro_util.object_to_string = object_to_string_new - nitro_util.object_to_string_withoutquotes = object_to_string_withoutquotes_new diff --git a/plugins/module_utils/network/netvisor/__init__.py b/plugins/module_utils/network/netvisor/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/netvisor/netvisor.py b/plugins/module_utils/network/netvisor/netvisor.py deleted file mode 100644 index 0be1af2e3d..0000000000 --- a/plugins/module_utils/network/netvisor/netvisor.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright: (c) 2018, Pluribus Networks -# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import json -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import Connection, ConnectionError -from ansible.module_utils.connection import exec_command - - -def get_connection(module): - if hasattr(module, '_nvos_connection'): - return module._nvos_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module._nvos_connection = Connection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type %s' % network_api) - - return module._nvos_connection - - -def get_capabilities(module): - if hasattr(module, '_nvos_capabilities'): - return module._nvos_capabilities - try: - capabilities = Connection(module._socket_path).get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module._nvos_capabilities = json.loads(capabilities) - return module._nvos_capabilities - - -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): - 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 = (to_text(out, errors='surrogate_or_strict')) - - return rc, out, err diff --git a/plugins/module_utils/network/netvisor/pn_nvos.py b/plugins/module_utils/network/netvisor/pn_nvos.py deleted file mode 100644 index cd5cdc598f..0000000000 --- a/plugins/module_utils/network/netvisor/pn_nvos.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright: (c) 2018, Pluribus Networks -# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def pn_cli(module, switch=None, username=None, password=None, switch_local=None): - """ - Method to generate the cli portion to launch the Netvisor cli. - :param module: The Ansible module to fetch username and password. - :return: The cli string for further processing. - """ - - cli = '' - - if username and password: - cli += '--user "%s":"%s" ' % (username, password) - if switch: - cli += ' switch ' + switch - if switch_local: - cli += ' switch-local ' - - return cli - - -def booleanArgs(arg, trueString, falseString): - if arg is True: - return " %s " % trueString - elif arg is False: - return " %s " % falseString - else: - return "" - - -def run_cli(module, cli, state_map): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param state_map: Provides state of the command. - :param module: The Ansible module to fetch command - """ - state = module.params['state'] - command = state_map[state] - - result, out, err = run_commands(module, cli) - - results = dict( - command=cli, - msg="%s operation completed" % cli, - changed=True - ) - # Response in JSON format - if result != 0: - module.exit_json( - command=cli, - msg="%s operation failed" % cli, - changed=False - ) - - module.exit_json(**results) diff --git a/plugins/module_utils/network/nos/__init__.py b/plugins/module_utils/network/nos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/nos/nos.py b/plugins/module_utils/network/nos/nos.py deleted file mode 100644 index c87b0644f6..0000000000 --- a/plugins/module_utils/network/nos/nos.py +++ /dev/null @@ -1,160 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -import json -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible.module_utils.connection import Connection, ConnectionError - - -def get_connection(module): - """Get switch connection - - Creates reusable SSH connection to the switch described in a given module. - - Args: - module: A valid AnsibleModule instance. - - Returns: - An instance of `ansible.module_utils.connection.Connection` with a - connection to the switch described in the provided module. - - Raises: - AnsibleConnectionFailure: An error occurred connecting to the device - """ - if hasattr(module, 'nos_connection'): - return module.nos_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module.nos_connection = Connection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type %s' % network_api) - - return module.nos_connection - - -def get_capabilities(module): - """Get switch capabilities - - Collects and returns a python object with the switch capabilities. - - Args: - module: A valid AnsibleModule instance. - - Returns: - A dictionary containing the switch capabilities. - """ - if hasattr(module, 'nos_capabilities'): - return module.nos_capabilities - - try: - capabilities = Connection(module._socket_path).get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module.nos_capabilities = json.loads(capabilities) - return module.nos_capabilities - - -def run_commands(module, commands): - """Run command list against connection. - - Get new or previously used connection and send commands to it one at a time, - collecting response. - - Args: - module: A valid AnsibleModule instance. - commands: Iterable of command strings. - - Returns: - A list of output strings. - """ - responses = list() - connection = get_connection(module) - - for cmd in to_list(commands): - if isinstance(cmd, dict): - command = cmd['command'] - prompt = cmd['prompt'] - answer = cmd['answer'] - else: - command = cmd - prompt = None - answer = None - - try: - out = connection.get(command, prompt, answer) - out = to_text(out, errors='surrogate_or_strict') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - except UnicodeError: - module.fail_json(msg=u'Failed to decode output from %s: %s' % (cmd, to_text(out))) - - responses.append(out) - - return responses - - -def get_config(module): - """Get switch configuration - - Gets the described device's current configuration. If a configuration has - already been retrieved it will return the previously obtained configuration. - - Args: - module: A valid AnsibleModule instance. - - Returns: - A string containing the configuration. - """ - if not hasattr(module, 'device_configs'): - module.device_configs = {} - elif module.device_configs != {}: - return module.device_configs - - connection = get_connection(module) - try: - out = connection.get_config() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - cfg = to_text(out, errors='surrogate_then_replace').strip() - module.device_configs = cfg - return cfg - - -def load_config(module, commands): - """Apply a list of commands to a device. - - Given a list of commands apply them to the device to modify the - configuration in bulk. - - Args: - module: A valid AnsibleModule instance. - commands: Iterable of command strings. - - Returns: - None - """ - connection = get_connection(module) - - try: - resp = connection.edit_config(commands) - return resp.get('response') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) diff --git a/plugins/module_utils/network/nso/__init__.py b/plugins/module_utils/network/nso/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/nso/nso.py b/plugins/module_utils/network/nso/nso.py deleted file mode 100644 index 217ac5dd8b..0000000000 --- a/plugins/module_utils/network/nso/nso.py +++ /dev/null @@ -1,822 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Cisco and/or its affiliates. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from ansible.module_utils.basic import env_fallback -from ansible.module_utils.urls import open_url -from ansible.module_utils._text import to_text - -import json -import re -import socket - -try: - unicode - HAVE_UNICODE = True -except NameError: - unicode = str - HAVE_UNICODE = False - - -nso_argument_spec = dict( - url=dict(type='str', required=True), - username=dict(type='str', required=True, fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])), - password=dict(type='str', required=True, no_log=True, fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD'])), - timeout=dict(type='int', default=300), - validate_certs=dict(type='bool', default=False) -) - - -class State(object): - SET = 'set' - PRESENT = 'present' - ABSENT = 'absent' - CHECK_SYNC = 'check-sync' - DEEP_CHECK_SYNC = 'deep-check-sync' - IN_SYNC = 'in-sync' - DEEP_IN_SYNC = 'deep-in-sync' - - SYNC_STATES = ('check-sync', 'deep-check-sync', 'in-sync', 'deep-in-sync') - - -class ModuleFailException(Exception): - def __init__(self, message): - super(ModuleFailException, self).__init__(message) - self.message = message - - -class NsoException(Exception): - def __init__(self, message, error): - super(NsoException, self).__init__(message) - self.message = message - self.error = error - - -class JsonRpc(object): - def __init__(self, url, timeout, validate_certs): - self._url = url - self._timeout = timeout - self._validate_certs = validate_certs - self._id = 0 - self._trans = {} - self._headers = {'Content-Type': 'application/json'} - self._conn = None - self._system_settings = {} - - def login(self, user, passwd): - payload = { - 'method': 'login', - 'params': {'user': user, 'passwd': passwd} - } - resp, resp_json = self._call(payload) - self._headers['Cookie'] = resp.headers['set-cookie'] - - def logout(self): - payload = {'method': 'logout', 'params': {}} - self._call(payload) - - def get_system_setting(self, setting): - if setting not in self._system_settings: - payload = {'method': 'get_system_setting', 'params': {'operation': setting}} - resp, resp_json = self._call(payload) - self._system_settings[setting] = resp_json['result'] - return self._system_settings[setting] - - def new_trans(self, **kwargs): - payload = {'method': 'new_trans', 'params': kwargs} - resp, resp_json = self._call(payload) - return resp_json['result']['th'] - - def get_trans(self, mode): - if mode not in self._trans: - th = self.new_trans(mode=mode) - self._trans[mode] = th - return self._trans[mode] - - def delete_trans(self, th): - payload = {'method': 'delete_trans', 'params': {'th': th}} - resp, resp_json = self._call(payload) - self._maybe_delete_trans(th) - - def validate_trans(self, th): - payload = {'method': 'validate_trans', 'params': {'th': th}} - resp, resp_json = self._write_call(payload) - return resp_json['result'] - - def get_trans_changes(self, th): - payload = {'method': 'get_trans_changes', 'params': {'th': th}} - resp, resp_json = self._write_call(payload) - return resp_json['result']['changes'] - - def validate_commit(self, th): - payload = {'method': 'validate_commit', 'params': {'th': th}} - resp, resp_json = self._write_call(payload) - return resp_json['result'].get('warnings', []) - - def commit(self, th): - payload = {'method': 'commit', 'params': {'th': th}} - resp, resp_json = self._write_call(payload) - if len(resp_json['result']) == 0: - self._maybe_delete_trans(th) - return resp_json['result'] - - def get_schema(self, **kwargs): - payload = {'method': 'get_schema', 'params': kwargs} - resp, resp_json = self._maybe_write_call(payload) - return resp_json['result'] - - def get_module_prefix_map(self, path=None): - if path is None: - payload = {'method': 'get_module_prefix_map', 'params': {}} - resp, resp_json = self._call(payload) - else: - payload = {'method': 'get_module_prefix_map', 'params': {'path': path}} - resp, resp_json = self._maybe_write_call(payload) - return resp_json['result'] - - def get_value(self, path): - payload = { - 'method': 'get_value', - 'params': {'path': path} - } - resp, resp_json = self._read_call(payload) - return resp_json['result'] - - def exists(self, path): - payload = {'method': 'exists', 'params': {'path': path}} - try: - resp, resp_json = self._read_call(payload) - return resp_json['result']['exists'] - except NsoException as ex: - # calling exists on a sub-list when the parent list does - # not exists will cause data.not_found errors on recent - # NSO - if 'type' in ex.error and ex.error['type'] == 'data.not_found': - return False - raise - - def create(self, th, path): - payload = {'method': 'create', 'params': {'th': th, 'path': path}} - self._write_call(payload) - - def delete(self, th, path): - payload = {'method': 'delete', 'params': {'th': th, 'path': path}} - self._write_call(payload) - - def set_value(self, th, path, value): - payload = { - 'method': 'set_value', - 'params': {'th': th, 'path': path, 'value': value} - } - resp, resp_json = self._write_call(payload) - return resp_json['result'] - - def show_config(self, path, operational=False): - payload = { - 'method': 'show_config', - 'params': { - 'path': path, - 'result_as': 'json', - 'with_oper': operational} - } - resp, resp_json = self._read_call(payload) - return resp_json['result'] - - def query(self, xpath, fields): - payload = { - 'method': 'query', - 'params': { - 'xpath_expr': xpath, - 'selection': fields - } - } - resp, resp_json = self._read_call(payload) - return resp_json['result']['results'] - - def run_action(self, th, path, params=None): - if params is None: - params = {} - - if is_version(self, [(4, 5), (4, 4, 3)]): - result_format = 'json' - else: - result_format = 'normal' - - payload = { - 'method': 'run_action', - 'params': { - 'format': result_format, - 'path': path, - 'params': params - } - } - if th is None: - resp, resp_json = self._read_call(payload) - else: - payload['params']['th'] = th - resp, resp_json = self._call(payload) - - if result_format == 'normal': - # this only works for one-level results, list entries, - # containers etc will have / in their name. - result = {} - for info in resp_json['result']: - result[info['name']] = info['value'] - else: - result = resp_json['result'] - - return result - - def _call(self, payload): - self._id += 1 - if 'id' not in payload: - payload['id'] = self._id - - if 'jsonrpc' not in payload: - payload['jsonrpc'] = '2.0' - - data = json.dumps(payload) - try: - resp = open_url( - self._url, timeout=self._timeout, - method='POST', data=data, headers=self._headers, - validate_certs=self._validate_certs) - if resp.code != 200: - raise NsoException( - 'NSO returned HTTP code {0}, expected 200'.format(resp.status), {}) - except socket.timeout: - raise NsoException('request timed out against NSO at {0}'.format(self._url), {}) - - resp_body = resp.read() - resp_json = json.loads(resp_body) - - if 'error' in resp_json: - self._handle_call_error(payload, resp_json) - return resp, resp_json - - def _handle_call_error(self, payload, resp_json): - method = payload['method'] - - error = resp_json['error'] - error_type = error['type'][len('rpc.method.'):] - if error_type in ('unexpected_params', - 'unknown_params_value', - 'invalid_params', - 'invalid_params_type', - 'data_not_found'): - key = error['data']['param'] - error_type_s = error_type.replace('_', ' ') - if key == 'path': - msg = 'NSO {0} {1}. path = {2}'.format( - method, error_type_s, payload['params']['path']) - else: - path = payload['params'].get('path', 'unknown') - msg = 'NSO {0} {1}. path = {2}. {3} = {4}'.format( - method, error_type_s, path, key, payload['params'][key]) - else: - msg = 'NSO {0} returned JSON-RPC error: {1}'.format(method, error) - - raise NsoException(msg, error) - - def _read_call(self, payload): - if 'th' not in payload['params']: - payload['params']['th'] = self.get_trans(mode='read') - return self._call(payload) - - def _write_call(self, payload): - if 'th' not in payload['params']: - payload['params']['th'] = self.get_trans(mode='read_write') - return self._call(payload) - - def _maybe_write_call(self, payload): - if 'read_write' in self._trans: - return self._write_call(payload) - else: - return self._read_call(payload) - - def _maybe_delete_trans(self, th): - for mode in ('read', 'read_write'): - if th == self._trans.get(mode, None): - del self._trans[mode] - - -class ValueBuilder(object): - PATH_RE = re.compile('{[^}]*}') - PATH_RE_50 = re.compile('{[^}]*}$') - - class Value(object): - __slots__ = ['path', 'tag_path', 'state', 'value', 'deps'] - - def __init__(self, path, state, value, deps): - self.path = path - self.tag_path = ValueBuilder.PATH_RE.sub('', path) - self.state = state - self.value = value - self.deps = deps - - # nodes can depend on themselves - if self.tag_path in self.deps: - self.deps.remove(self.tag_path) - - def __lt__(self, rhs): - l_len = len(self.path.split('/')) - r_len = len(rhs.path.split('/')) - if l_len == r_len: - return self.path.__lt__(rhs.path) - return l_len < r_len - - def __str__(self): - return 'Value'.format( - self.path, self.state, self.value) - - class ValueIterator(object): - def __init__(self, client, values, delayed_values): - self._client = client - self._values = values - self._delayed_values = delayed_values - self._pos = 0 - - def __iter__(self): - return self - - def __next__(self): - return self.next() - - def next(self): - if self._pos >= len(self._values): - if len(self._delayed_values) == 0: - raise StopIteration() - - builder = ValueBuilder(self._client, delay=False) - for (parent, maybe_qname, value) in self._delayed_values: - builder.build(parent, maybe_qname, value) - del self._delayed_values[:] - self._values.extend(builder.values) - - return self.next() - - value = self._values[self._pos] - self._pos += 1 - return value - - def __init__(self, client, mode='config', delay=None): - self._client = client - self._mode = mode - self._schema_cache = {} - self._module_prefix_map_cache = {} - self._values = [] - self._values_dirty = False - self._delay = delay is None and mode == 'config' and is_version(self._client, [(5, 0)]) - self._delayed_values = [] - - def build(self, parent, maybe_qname, value, schema=None): - qname, name = self.get_prefix_name(parent, maybe_qname) - if name is None: - path = parent - else: - path = '{0}/{1}'.format(parent, qname) - - if schema is None: - schema = self._get_schema(path) - - if self._delay and schema.get('is_mount_point', False): - # delay conversion of mounted values, required to get - # shema information on 5.0 and later. - self._delayed_values.append((parent, maybe_qname, value)) - elif self._is_leaf_list(schema) and is_version(self._client, [(4, 5)]): - self._build_leaf_list(path, schema, value) - elif self._is_leaf(schema): - deps = schema.get('deps', []) - if self._is_empty_leaf(schema): - exists = self._client.exists(path) - if exists and value != [None]: - self._add_value(path, State.ABSENT, None, deps) - elif not exists and value == [None]: - self._add_value(path, State.PRESENT, None, deps) - else: - if maybe_qname is None: - value_type = self.get_type(path) - else: - value_type = self._get_child_type(parent, qname) - - if 'identityref' in value_type: - if isinstance(value, list): - value = [ll_v for ll_v, t_ll_v - in [self.get_prefix_name(parent, v) for v in value]] - else: - value, t_value = self.get_prefix_name(parent, value) - self._add_value(path, State.SET, value, deps) - elif isinstance(value, dict): - self._build_dict(path, schema, value) - elif isinstance(value, list): - self._build_list(path, schema, value) - else: - raise ModuleFailException( - 'unsupported schema {0} at {1}'.format( - schema['kind'], path)) - - @property - def values(self): - if self._values_dirty: - self._values = ValueBuilder.sort_values(self._values) - self._values_dirty = False - - return ValueBuilder.ValueIterator(self._client, self._values, self._delayed_values) - - @staticmethod - def sort_values(values): - class N(object): - def __init__(self, v): - self.tmp_mark = False - self.mark = False - self.v = v - - sorted_values = [] - nodes = [N(v) for v in sorted(values)] - - def get_node(tag_path): - return next((m for m in nodes - if m.v.tag_path == tag_path), None) - - def is_cycle(n, dep, visited): - visited.add(n.v.tag_path) - if dep in visited: - return True - - dep_n = get_node(dep) - if dep_n is not None: - for sub_dep in dep_n.v.deps: - if is_cycle(dep_n, sub_dep, visited): - return True - - return False - - # check for dependency cycles, remove if detected. sort will - # not be 100% but allows for a best-effort to work around - # issue in NSO. - for n in nodes: - for dep in n.v.deps: - if is_cycle(n, dep, set()): - n.v.deps.remove(dep) - - def visit(n): - if n.tmp_mark: - return False - if not n.mark: - n.tmp_mark = True - for m in nodes: - if m.v.tag_path in n.v.deps: - if not visit(m): - return False - - n.tmp_mark = False - n.mark = True - - sorted_values.insert(0, n.v) - - return True - - n = next((n for n in nodes if not n.mark), None) - while n is not None: - visit(n) - n = next((n for n in nodes if not n.mark), None) - - return sorted_values[::-1] - - def _build_dict(self, path, schema, value): - keys = schema.get('key', []) - for dict_key, dict_value in value.items(): - qname, name = self.get_prefix_name(path, dict_key) - if dict_key in ('__state', ) or name in keys: - continue - - child_schema = self._find_child(path, schema, qname) - self.build(path, dict_key, dict_value, child_schema) - - def _build_leaf_list(self, path, schema, value): - deps = schema.get('deps', []) - entry_type = self.get_type(path, schema) - - if self._mode == 'verify': - for entry in value: - if 'identityref' in entry_type: - entry, t_entry = self.get_prefix_name(path, entry) - entry_path = '{0}{{{1}}}'.format(path, entry) - if not self._client.exists(entry_path): - self._add_value(entry_path, State.ABSENT, None, deps) - else: - # remove leaf list if treated as a list and then re-create the - # expected list entries. - self._add_value(path, State.ABSENT, None, deps) - - for entry in value: - if 'identityref' in entry_type: - entry, t_entry = self.get_prefix_name(path, entry) - entry_path = '{0}{{{1}}}'.format(path, entry) - self._add_value(entry_path, State.PRESENT, None, deps) - - def _build_list(self, path, schema, value): - deps = schema.get('deps', []) - for entry in value: - entry_key = self._build_key(path, entry, schema['key']) - entry_path = '{0}{{{1}}}'.format(path, entry_key) - entry_state = entry.get('__state', 'present') - entry_exists = self._client.exists(entry_path) - - if entry_state == 'absent': - if entry_exists: - self._add_value(entry_path, State.ABSENT, None, deps) - else: - if not entry_exists: - self._add_value(entry_path, State.PRESENT, None, deps) - if entry_state in State.SYNC_STATES: - self._add_value(entry_path, entry_state, None, deps) - - self.build(entry_path, None, entry) - - def _build_key(self, path, entry, schema_keys): - key_parts = [] - for key in schema_keys: - value = entry.get(key, None) - if value is None: - raise ModuleFailException( - 'required leaf {0} in {1} not set in data'.format( - key, path)) - - value_type = self._get_child_type(path, key) - if 'identityref' in value_type: - value, t_value = self.get_prefix_name(path, value) - key_parts.append(self._quote_key(value)) - return ' '.join(key_parts) - - def _quote_key(self, key): - if isinstance(key, bool): - return key and 'true' or 'false' - - q_key = [] - for c in str(key): - if c in ('{', '}', "'", '\\'): - q_key.append('\\') - q_key.append(c) - q_key = ''.join(q_key) - if ' ' in q_key: - return '"{0}"'.format(q_key) - return q_key - - def _find_child(self, path, schema, qname): - if 'children' not in schema: - schema = self._get_schema(path) - - # look for the qualified name if : is in the name - child_schema = self._get_child(schema, qname) - if child_schema is not None: - return child_schema - - # no child was found, look for a choice with a child matching - for child_schema in schema['children']: - if child_schema['kind'] != 'choice': - continue - choice_child_schema = self._get_choice_child(child_schema, qname) - if choice_child_schema is not None: - return choice_child_schema - - raise ModuleFailException( - 'no child in {0} with name {1}. children {2}'.format( - path, qname, ','.join((c.get('qname', c.get('name', None)) for c in schema['children'])))) - - def _add_value(self, path, state, value, deps): - self._values.append(ValueBuilder.Value(path, state, value, deps)) - self._values_dirty = True - - def get_prefix_name(self, path, qname): - if not isinstance(qname, (str, unicode)): - return qname, None - if ':' not in qname: - return qname, qname - - module_prefix_map = self._get_module_prefix_map(path) - module, name = qname.split(':', 1) - if module not in module_prefix_map: - raise ModuleFailException( - 'no module mapping for module {0}. loaded modules {1}'.format( - module, ','.join(sorted(module_prefix_map.keys())))) - - return '{0}:{1}'.format(module_prefix_map[module], name), name - - def _get_schema(self, path): - return self._ensure_schema_cached(path)['data'] - - def _get_child_type(self, parent_path, key): - all_schema = self._ensure_schema_cached(parent_path) - parent_schema = all_schema['data'] - meta = all_schema['meta'] - schema = self._find_child(parent_path, parent_schema, key) - return self.get_type(parent_path, schema, meta) - - def get_type(self, path, schema=None, meta=None): - if schema is None or meta is None: - all_schema = self._ensure_schema_cached(path) - schema = all_schema['data'] - meta = all_schema['meta'] - - if self._is_leaf(schema): - def get_type(meta, curr_type): - if curr_type.get('primitive', False): - return [curr_type['name']] - if 'namespace' in curr_type: - curr_type_key = '{0}:{1}'.format( - curr_type['namespace'], curr_type['name']) - type_info = meta['types'][curr_type_key][-1] - return get_type(meta, type_info) - if 'leaf_type' in curr_type: - return get_type(meta, curr_type['leaf_type'][-1]) - if 'union' in curr_type: - union_types = [] - for union_type in curr_type['union']: - union_types.extend(get_type(meta, union_type[-1])) - return union_types - return [curr_type.get('name', 'unknown')] - - return get_type(meta, schema['type']) - return None - - def _ensure_schema_cached(self, path): - if not self._delay and is_version(self._client, [(5, 0)]): - # newer versions of NSO support multiple different schemas - # for different devices, thus the device is required to - # look up the schema. Remove the key entry to get schema - # logic working ok. - path = ValueBuilder.PATH_RE_50.sub('', path) - else: - path = ValueBuilder.PATH_RE.sub('', path) - - if path not in self._schema_cache: - schema = self._client.get_schema(path=path, levels=1) - self._schema_cache[path] = schema - return self._schema_cache[path] - - def _get_module_prefix_map(self, path): - # newer versions of NSO support multiple mappings from module - # to prefix depending on which device is used. - if path != '' and is_version(self._client, [(5, 0)]): - if path not in self._module_prefix_map_cache: - self._module_prefix_map_cache[path] = self._client.get_module_prefix_map(path) - return self._module_prefix_map_cache[path] - - if '' not in self._module_prefix_map_cache: - self._module_prefix_map_cache[''] = self._client.get_module_prefix_map() - return self._module_prefix_map_cache[''] - - def _get_child(self, schema, qname): - # no child specified, return parent - if qname is None: - return schema - - name_key = ':' in qname and 'qname' or 'name' - return next((c for c in schema['children'] - if c.get(name_key, None) == qname), None) - - def _get_choice_child(self, schema, qname): - name_key = ':' in qname and 'qname' or 'name' - for child_case in schema['cases']: - # look for direct child - choice_child_schema = next( - (c for c in child_case['children'] - if c.get(name_key, None) == qname), None) - if choice_child_schema is not None: - return choice_child_schema - - # look for nested choice - for child_schema in child_case['children']: - if child_schema['kind'] != 'choice': - continue - choice_child_schema = self._get_choice_child(child_schema, qname) - if choice_child_schema is not None: - return choice_child_schema - return None - - def _is_leaf_list(self, schema): - return schema.get('kind', None) == 'leaf-list' - - def _is_leaf(self, schema): - # still checking for leaf-list here to be compatible with pre - # 4.5 versions of NSO. - return schema.get('kind', None) in ('key', 'leaf', 'leaf-list') - - def _is_empty_leaf(self, schema): - return (schema.get('kind', None) == 'leaf' and - schema['type'].get('primitive', False) and - schema['type'].get('name', '') == 'empty') - - -def connect(params): - client = JsonRpc(params['url'], - params['timeout'], - params['validate_certs']) - client.login(params['username'], params['password']) - return client - - -def verify_version(client, required_versions): - version_str = client.get_system_setting('version') - if not verify_version_str(version_str, required_versions): - supported_versions = ', '.join( - ['.'.join([str(p) for p in required_version]) - for required_version in required_versions]) - raise ModuleFailException( - 'unsupported NSO version {0}. {1} or later supported'.format( - version_str, supported_versions)) - - -def is_version(client, required_versions): - version_str = client.get_system_setting('version') - return verify_version_str(version_str, required_versions) - - -def verify_version_str(version_str, required_versions): - version_str = re.sub('_.*', '', version_str) - - version = [int(p) for p in version_str.split('.')] - if len(version) < 2: - raise ModuleFailException( - 'unsupported NSO version format {0}'.format(version_str)) - - def check_version(required_version, version): - for pos in range(len(required_version)): - if pos >= len(version): - return False - if version[pos] > required_version[pos]: - return True - if version[pos] < required_version[pos]: - return False - return True - - for required_version in required_versions: - if check_version(required_version, version): - return True - return False - - -def normalize_value(expected_value, value, key): - if value is None: - return None - if (isinstance(expected_value, bool) and - isinstance(value, (str, unicode))): - return value == 'true' - if isinstance(expected_value, int): - try: - return int(value) - except TypeError: - raise ModuleFailException( - 'returned value {0} for {1} is not a valid integer'.format( - key, value)) - if isinstance(expected_value, float): - try: - return float(value) - except TypeError: - raise ModuleFailException( - 'returned value {0} for {1} is not a valid float'.format( - key, value)) - if isinstance(expected_value, (list, tuple)): - if not isinstance(value, (list, tuple)): - raise ModuleFailException( - 'returned value {0} for {1} is not a list'.format(value, key)) - if len(expected_value) != len(value): - raise ModuleFailException( - 'list length mismatch for {0}'.format(key)) - - normalized_value = [] - for i in range(len(expected_value)): - normalized_value.append( - normalize_value(expected_value[i], value[i], '{0}[{1}]'.format(key, i))) - return normalized_value - - if isinstance(expected_value, dict): - if not isinstance(value, dict): - raise ModuleFailException( - 'returned value {0} for {1} is not a dict'.format(value, key)) - if len(expected_value) != len(value): - raise ModuleFailException( - 'dict length mismatch for {0}'.format(key)) - - normalized_value = {} - for k in expected_value.keys(): - n_k = normalize_value(k, k, '{0}[{1}]'.format(key, k)) - if n_k not in value: - raise ModuleFailException('missing {0} in value'.format(n_k)) - normalized_value[n_k] = normalize_value(expected_value[k], value[k], '{0}[{1}]'.format(key, k)) - return normalized_value - - if HAVE_UNICODE: - if isinstance(expected_value, unicode) and isinstance(value, str): - return value.decode('utf-8') - if isinstance(expected_value, str) and isinstance(value, unicode): - return value.encode('utf-8') - else: - if hasattr(expected_value, 'encode') and hasattr(value, 'decode'): - return value.decode('utf-8') - if hasattr(expected_value, 'decode') and hasattr(value, 'encode'): - return value.encode('utf-8') - - return value diff --git a/plugins/module_utils/network/onyx/__init__.py b/plugins/module_utils/network/onyx/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/onyx/onyx.py b/plugins/module_utils/network/onyx/onyx.py deleted file mode 100644 index d537e048b9..0000000000 --- a/plugins/module_utils/network/onyx/onyx.py +++ /dev/null @@ -1,261 +0,0 @@ -# -*- coding: utf-8 -*- -# -# (c) 2017, Ansible by Red Hat, inc -# -# This file is part of Ansible by Red Hat -# -# 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 . -# -import json - -from ansible.module_utils._text import to_text -from ansible.module_utils.connection import Connection, ConnectionError -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, EntityCollection - -_DEVICE_CONFIGS = {} -_CONNECTION = None - -_COMMAND_SPEC = { - 'command': dict(key=True), - 'prompt': dict(), - 'answer': dict() -} - - -def get_connection(module): - global _CONNECTION - if _CONNECTION: - return _CONNECTION - _CONNECTION = Connection(module._socket_path) - return _CONNECTION - - -def to_commands(module, commands): - if not isinstance(commands, list): - raise AssertionError('argument must be of type ') - - transform = EntityCollection(module, _COMMAND_SPEC) - commands = transform(commands) - return commands - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - - commands = to_commands(module, to_list(commands)) - - responses = list() - - for cmd in commands: - out = connection.get(**cmd) - responses.append(to_text(out, errors='surrogate_then_replace')) - - return responses - - -def get_config(module, source='running'): - conn = get_connection(module) - out = conn.get_config(source) - cfg = to_text(out, errors='surrogate_then_replace').strip() - return cfg - - -def load_config(module, config): - try: - conn = get_connection(module) - conn.edit_config(config) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def _parse_json_output(out): - out_list = out.split('\n') - first_index = 0 - opening_char = None - lines_count = len(out_list) - while first_index < lines_count: - first_line = out_list[first_index].strip() - if not first_line or first_line[0] not in ("[", "{"): - first_index += 1 - continue - opening_char = first_line[0] - break - if not opening_char: - return "null" - closing_char = ']' if opening_char == '[' else '}' - last_index = lines_count - 1 - found = False - while last_index > first_index: - last_line = out_list[last_index].strip() - if not last_line or last_line[0] != closing_char: - last_index -= 1 - continue - found = True - break - if not found: - return opening_char + closing_char - return "".join(out_list[first_index:last_index + 1]) - - -def show_cmd(module, cmd, json_fmt=True, fail_on_error=True): - if json_fmt: - cmd += " | json-print" - conn = get_connection(module) - command_obj = to_commands(module, to_list(cmd))[0] - try: - out = conn.get(**command_obj) - except ConnectionError: - if fail_on_error: - raise - return None - if json_fmt: - out = _parse_json_output(out) - try: - cfg = json.loads(out) - except ValueError: - module.fail_json( - msg="got invalid json", - stderr=to_text(out, errors='surrogate_then_replace')) - else: - cfg = to_text(out, errors='surrogate_then_replace').strip() - return cfg - - -def get_interfaces_config(module, interface_type, flags=None, json_fmt=True): - cmd = "show interfaces %s" % interface_type - if flags: - cmd += " %s" % flags - return show_cmd(module, cmd, json_fmt) - - -def get_bgp_summary(module): - cmd = "show running-config protocol bgp" - return show_cmd(module, cmd, json_fmt=False, fail_on_error=False) - - -def get_capabilities(module): - """Returns platform info of the remove device - """ - if hasattr(module, '_capabilities'): - return module._capabilities - - connection = get_connection(module) - try: - capabilities = connection.get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - module._capabilities = json.loads(capabilities) - return module._capabilities - - -class BaseOnyxModule(object): - ONYX_API_VERSION = "3.6.6000" - - def __init__(self): - self._module = None - self._commands = list() - self._current_config = None - self._required_config = None - self._os_version = None - - def init_module(self): - pass - - def load_current_config(self): - pass - - def get_required_config(self): - pass - - def _get_os_version(self): - capabilities = get_capabilities(self._module) - device_info = capabilities['device_info'] - return device_info['network_os_version'] - - # pylint: disable=unused-argument - def check_declarative_intent_params(self, result): - return None - - def _validate_key(self, param, key): - validator = getattr(self, 'validate_%s' % key) - if callable(validator): - validator(param.get(key)) - - def validate_param_values(self, obj, param=None): - if param is None: - param = self._module.params - for key in obj: - # validate the param value (if validator func exists) - try: - self._validate_key(param, key) - except AttributeError: - pass - - @classmethod - def get_config_attr(cls, item, arg): - return item.get(arg) - - @classmethod - def get_mtu(cls, item): - mtu = cls.get_config_attr(item, "MTU") - mtu_parts = mtu.split() - try: - return int(mtu_parts[0]) - except ValueError: - return None - - def _validate_range(self, attr_name, min_val, max_val, value): - if value is None: - return True - if not min_val <= int(value) <= max_val: - msg = '%s must be between %s and %s' % ( - attr_name, min_val, max_val) - self._module.fail_json(msg=msg) - - def validate_mtu(self, value): - self._validate_range('mtu', 1500, 9612, value) - - def generate_commands(self): - pass - - def run(self): - self.init_module() - - result = {'changed': False} - - self.get_required_config() - self.load_current_config() - - self.generate_commands() - result['commands'] = self._commands - - if self._commands: - if not self._module.check_mode: - load_config(self._module, self._commands) - result['changed'] = True - - failed_conditions = self.check_declarative_intent_params(result) - - if failed_conditions: - msg = 'One or more conditional statements have not been satisfied' - self._module.fail_json(msg=msg, - failed_conditions=failed_conditions) - - self._module.exit_json(**result) - - @classmethod - def main(cls): - app = cls() - app.run() diff --git a/plugins/module_utils/network/ordnance/__init__.py b/plugins/module_utils/network/ordnance/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/ordnance/ordnance.py b/plugins/module_utils/network/ordnance/ordnance.py deleted file mode 100644 index 070a86d3e1..0000000000 --- a/plugins/module_utils/network/ordnance/ordnance.py +++ /dev/null @@ -1,19 +0,0 @@ -_DEVICE_CONFIGS = {} - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - cmd = 'show running-config ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - try: - return _DEVICE_CONFIGS[cmd] - except KeyError: - rc, out, err = module.exec_command(cmd) - if rc != 0: - module.fail_json(msg='unable to retrieve current config', stderr=err) - cfg = str(out).strip() - _DEVICE_CONFIGS[cmd] = cfg - return cfg diff --git a/plugins/module_utils/network/panos/__init__.py b/plugins/module_utils/network/panos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/panos/panos.py b/plugins/module_utils/network/panos/panos.py deleted file mode 100644 index f50257dcf2..0000000000 --- a/plugins/module_utils/network/panos/panos.py +++ /dev/null @@ -1,418 +0,0 @@ -# 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. -# -# Copyright (c) 2018 Palo Alto Networks techbizdev, -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -_MIN_VERSION_ERROR = '{0} version ({1}) < minimum version ({2})' -HAS_PANDEVICE = True -try: - import pandevice - from pandevice.base import PanDevice - from pandevice.firewall import Firewall - from pandevice.panorama import DeviceGroup, Template, TemplateStack - from pandevice.policies import PreRulebase, PostRulebase, Rulebase - from pandevice.device import Vsys - from pandevice.errors import PanDeviceError -except ImportError: - HAS_PANDEVICE = False - - -def _vstr(val): - return '{0}.{1}.{2}'.format(*val) - - -class ConnectionHelper(object): - def __init__(self, min_pandevice_version, min_panos_version, - panorama_error, firewall_error): - """Performs connection initialization and determines params.""" - # Params for AnsibleModule. - self.argument_spec = {} - self.required_one_of = [] - - # Params for pandevice tree construction. - self.vsys = None - self.device_group = None - self.vsys_dg = None - self.rulebase = None - self.template = None - self.template_stack = None - self.vsys_importable = None - self.min_pandevice_version = min_pandevice_version - self.min_panos_version = min_panos_version - self.panorama_error = panorama_error - self.firewall_error = firewall_error - - # The PAN-OS device. - self.device = None - - def get_pandevice_parent(self, module): - """Builds the pandevice object tree, returning the parent object. - - If pandevice is not installed, then module.fail_json() will be - invoked. - - Arguments: - * module(AnsibleModule): the ansible module. - - Returns: - * The parent pandevice object based on the spec given to - get_connection(). - """ - # Sanity check. - if not HAS_PANDEVICE: - module.fail_json(msg='Missing required library "pandevice".') - - # Verify pandevice minimum version. - if self.min_pandevice_version is not None: - pdv = tuple(int(x) for x in pandevice.__version__.split('.')) - if pdv < self.min_pandevice_version: - module.fail_json(msg=_MIN_VERSION_ERROR.format( - 'pandevice', pandevice.__version__, - _vstr(self.min_pandevice_version))) - - pan_device_auth, serial_number = None, None - if module.params['provider'] and module.params['provider']['ip_address']: - pan_device_auth = ( - module.params['provider']['ip_address'], - module.params['provider']['username'], - module.params['provider']['password'], - module.params['provider']['api_key'], - module.params['provider']['port'], - ) - serial_number = module.params['provider']['serial_number'] - elif module.params.get('ip_address', None) is not None: - pan_device_auth = ( - module.params['ip_address'], - module.params['username'], - module.params['password'], - module.params['api_key'], - module.params['port'], - ) - msg = 'Classic provider params are deprecated; use "provider" instead' - module.deprecate(msg, '2.12') - else: - module.fail_json(msg='Provider params are required.') - - # Create the connection object. - try: - self.device = PanDevice.create_from_device(*pan_device_auth) - except PanDeviceError as e: - module.fail_json(msg='Failed connection: {0}'.format(e)) - - # Verify PAN-OS minimum version. - if self.min_panos_version is not None: - if self.device._version_info < self.min_panos_version: - module.fail_json(msg=_MIN_VERSION_ERROR.format( - 'PAN-OS', _vstr(self.device._version_info), - _vstr(self.min_panos_version))) - - # Optional: Firewall via Panorama connectivity specified. - if hasattr(self.device, 'refresh_devices') and serial_number: - fw = Firewall(serial=serial_number) - self.device.add(fw) - self.device = fw - - parent = self.device - not_found = '{0} "{1}" is not present.' - pano_mia_param = 'Param "{0}" is required for Panorama but not specified.' - ts_error = 'Specify either the template or the template stack{0}.' - if hasattr(self.device, 'refresh_devices'): - # Panorama connection. - # Error if Panorama is not supported. - if self.panorama_error is not None: - module.fail_json(msg=self.panorama_error) - - # Spec: template stack. - tmpl_required = False - added_template = False - if self.template_stack is not None: - name = module.params[self.template_stack] - if name is not None: - stacks = TemplateStack.refreshall(parent, name_only=True) - for ts in stacks: - if ts.name == name: - parent = ts - added_template = True - break - else: - module.fail_json(msg=not_found.format( - 'Template stack', name, - )) - elif self.template is not None: - tmpl_required = True - else: - module.fail_json(msg=pano_mia_param.format(self.template_stack)) - - # Spec: template. - if self.template is not None: - name = module.params[self.template] - if name is not None: - if added_template: - module.fail_json(msg=ts_error.format(', not both')) - templates = Template.refreshall(parent, name_only=True) - for t in templates: - if t.name == name: - parent = t - break - else: - module.fail_json(msg=not_found.format( - 'Template', name, - )) - elif tmpl_required: - module.fail_json(msg=ts_error.format('')) - else: - module.fail_json(msg=pano_mia_param.format(self.template)) - - # Spec: vsys importable. - vsys_name = self.vsys_importable or self.vsys - if vsys_name is not None: - name = module.params[vsys_name] - if name not in (None, 'shared'): - vo = Vsys(name) - parent.add(vo) - parent = vo - - # Spec: vsys_dg or device_group. - dg_name = self.vsys_dg or self.device_group - if dg_name is not None: - name = module.params[dg_name] - if name not in (None, 'shared'): - groups = DeviceGroup.refreshall(parent, name_only=True) - for dg in groups: - if dg.name == name: - parent = dg - break - else: - module.fail_json(msg=not_found.format( - 'Device group', name, - )) - - # Spec: rulebase. - if self.rulebase is not None: - if module.params[self.rulebase] in (None, 'pre-rulebase'): - rb = PreRulebase() - parent.add(rb) - parent = rb - elif module.params[self.rulebase] == 'rulebase': - rb = Rulebase() - parent.add(rb) - parent = rb - elif module.params[self.rulebase] == 'post-rulebase': - rb = PostRulebase() - parent.add(rb) - parent = rb - else: - module.fail_json(msg=not_found.format( - 'Rulebase', module.params[self.rulebase])) - else: - # Firewall connection. - # Error if firewalls are not supported. - if self.firewall_error is not None: - module.fail_json(msg=self.firewall_error) - - # Spec: vsys or vsys_dg or vsys_importable. - vsys_name = self.vsys_dg or self.vsys or self.vsys_importable - if vsys_name is not None: - parent.vsys = module.params[vsys_name] - - # Spec: rulebase. - if self.rulebase is not None: - rb = Rulebase() - parent.add(rb) - parent = rb - - # Done. - return parent - - -def get_connection(vsys=None, device_group=None, - vsys_dg=None, vsys_importable=None, - rulebase=None, template=None, template_stack=None, - with_classic_provider_spec=False, with_state=True, - argument_spec=None, required_one_of=None, - min_pandevice_version=None, min_panos_version=None, - panorama_error=None, firewall_error=None): - """Returns a helper object that handles pandevice object tree init. - - The `vsys`, `device_group`, `vsys_dg`, `vsys_importable`, `rulebase`, - `template`, and `template_stack` params can be any of the following types: - - * None - do not include this in the spec - * True - use the default param name - * string - use this string for the param name - - The `min_pandevice_version` and `min_panos_version` args expect a 3 element - tuple of ints. For example, `(0, 6, 0)` or `(8, 1, 0)`. - - If you are including template support (by defining either `template` and/or - `template_stack`), and the thing the module is enabling the management of is - an "importable", you should define either `vsys_importable` (whose default - value is None) or `vsys` (whose default value is 'vsys1'). - - Arguments: - vsys: The vsys (default: 'vsys1'). - device_group: Panorama only - The device group (default: 'shared'). - vsys_dg: The param name if vsys and device_group are a shared param. - vsys_importable: Either this or `vsys` should be specified. For: - - Interfaces - - VLANs - - Virtual Wires - - Virtual Routers - rulebase: This is a policy of some sort. - template: Panorama - The template name. - template_stack: Panorama - The template stack name. - with_classic_provider_spec(bool): Include the ip_address, username, - password, api_key, and port params in the base spec, and make the - "provider" param optional. - with_state(bool): Include the standard 'state' param. - argument_spec(dict): The argument spec to mixin with the - generated spec based on the given parameters. - required_one_of(list): List of lists to extend into required_one_of. - min_pandevice_version(tuple): Minimum pandevice version allowed. - min_panos_version(tuple): Minimum PAN-OS version allowed. - panorama_error(str): The error message if the device is Panorama. - firewall_error(str): The error message if the device is a firewall. - - Returns: - ConnectionHelper - """ - helper = ConnectionHelper( - min_pandevice_version, min_panos_version, - panorama_error, firewall_error) - req = [] - spec = { - 'provider': { - 'required': True, - 'type': 'dict', - 'required_one_of': [['password', 'api_key'], ], - 'options': { - 'ip_address': {'required': True}, - 'username': {'default': 'admin'}, - 'password': {'no_log': True}, - 'api_key': {'no_log': True}, - 'port': {'default': 443, 'type': 'int'}, - 'serial_number': {'no_log': True}, - }, - }, - } - - if with_classic_provider_spec: - spec['provider']['required'] = False - spec['provider']['options']['ip_address']['required'] = False - del(spec['provider']['required_one_of']) - spec.update({ - 'ip_address': {'required': False}, - 'username': {'default': 'admin'}, - 'password': {'no_log': True}, - 'api_key': {'no_log': True}, - 'port': {'default': 443, 'type': 'int'}, - }) - req.extend([ - ['provider', 'ip_address'], - ['provider', 'password', 'api_key'], - ]) - - if with_state: - spec['state'] = { - 'default': 'present', - 'choices': ['present', 'absent'], - } - - if vsys_dg is not None: - if isinstance(vsys_dg, bool): - param = 'vsys_dg' - else: - param = vsys_dg - spec[param] = {} - helper.vsys_dg = param - else: - if vsys is not None: - if isinstance(vsys, bool): - param = 'vsys' - else: - param = vsys - spec[param] = {'default': 'vsys1'} - helper.vsys = param - if device_group is not None: - if isinstance(device_group, bool): - param = 'device_group' - else: - param = device_group - spec[param] = {'default': 'shared'} - helper.device_group = param - if vsys_importable is not None: - if vsys is not None: - raise KeyError('Define "vsys" or "vsys_importable", not both.') - if isinstance(vsys_importable, bool): - param = 'vsys' - else: - param = vsys_importable - spec[param] = {} - helper.vsys_importable = param - - if rulebase is not None: - if isinstance(rulebase, bool): - param = 'rulebase' - else: - param = rulebase - spec[param] = { - 'default': None, - 'choices': ['pre-rulebase', 'rulebase', 'post-rulebase'], - } - helper.rulebase = param - - if template is not None: - if isinstance(template, bool): - param = 'template' - else: - param = template - spec[param] = {} - helper.template = param - - if template_stack is not None: - if isinstance(template_stack, bool): - param = 'template_stack' - else: - param = template_stack - spec[param] = {} - helper.template_stack = param - - if argument_spec is not None: - for k in argument_spec.keys(): - if k in spec: - raise KeyError('{0}: key used by connection helper.'.format(k)) - spec[k] = argument_spec[k] - - if required_one_of is not None: - req.extend(required_one_of) - - # Done. - helper.argument_spec = spec - helper.required_one_of = req - return helper diff --git a/plugins/module_utils/network/routeros/__init__.py b/plugins/module_utils/network/routeros/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/routeros/routeros.py b/plugins/module_utils/network/routeros/routeros.py deleted file mode 100644 index 63eb3c0470..0000000000 --- a/plugins/module_utils/network/routeros/routeros.py +++ /dev/null @@ -1,156 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2016 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -import json -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import Connection, ConnectionError - -_DEVICE_CONFIGS = {} - -routeros_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'), - 'timeout': dict(type='int') -} -routeros_argument_spec = {} - - -def get_provider_argspec(): - return routeros_provider_spec - - -def get_connection(module): - if hasattr(module, '_routeros_connection'): - return module._routeros_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module._routeros_connection = Connection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type %s' % network_api) - - return module._routeros_connection - - -def get_capabilities(module): - if hasattr(module, '_routeros_capabilities'): - return module._routeros_capabilities - - capabilities = Connection(module._socket_path).get_capabilities() - module._routeros_capabilities = json.loads(capabilities) - return module._routeros_capabilities - - -def get_defaults_flag(module): - connection = get_connection(module) - - try: - out = connection.get('/system default-configuration print') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - out = to_text(out, errors='surrogate_then_replace') - - commands = set() - for line in out.splitlines(): - if line.strip(): - commands.add(line.strip().split()[0]) - - if 'all' in commands: - return ['all'] - else: - return ['full'] - - -def get_config(module, flags=None): - flag_str = ' '.join(to_list(flags)) - - try: - return _DEVICE_CONFIGS[flag_str] - except KeyError: - connection = get_connection(module) - - try: - out = connection.get_config(flags=flags) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - cfg = to_text(out, errors='surrogate_then_replace').strip() - _DEVICE_CONFIGS[flag_str] = cfg - return cfg - - -def 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() - connection = get_connection(module) - - for cmd in to_list(commands): - if isinstance(cmd, dict): - command = cmd['command'] - prompt = cmd['prompt'] - answer = cmd['answer'] - else: - command = cmd - prompt = None - answer = None - - try: - out = connection.get(command, prompt, answer) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - try: - out = to_text(out, errors='surrogate_or_strict') - except UnicodeError: - module.fail_json( - msg=u'Failed to decode output from %s: %s' % (cmd, to_text(out))) - - responses.append(out) - - return responses - - -def load_config(module, commands): - connection = get_connection(module) - - out = connection.edit_config(commands) diff --git a/plugins/module_utils/network/slxos/__init__.py b/plugins/module_utils/network/slxos/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/slxos/slxos.py b/plugins/module_utils/network/slxos/slxos.py deleted file mode 100644 index d9971840de..0000000000 --- a/plugins/module_utils/network/slxos/slxos.py +++ /dev/null @@ -1,148 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -import json -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import Connection - - -def get_connection(module): - """Get switch connection - - Creates reusable SSH connection to the switch described in a given module. - - Args: - module: A valid AnsibleModule instance. - - Returns: - An instance of `ansible.module_utils.connection.Connection` with a - connection to the switch described in the provided module. - - Raises: - AnsibleConnectionFailure: An error occurred connecting to the device - """ - if hasattr(module, 'slxos_connection'): - return module.slxos_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module.slxos_connection = Connection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type %s' % network_api) - - return module.slxos_connection - - -def get_capabilities(module): - """Get switch capabilities - - Collects and returns a python object with the switch capabilities. - - Args: - module: A valid AnsibleModule instance. - - Returns: - A dictionary containing the switch capabilities. - """ - if hasattr(module, 'slxos_capabilities'): - return module.slxos_capabilities - - capabilities = Connection(module._socket_path).get_capabilities() - module.slxos_capabilities = json.loads(capabilities) - return module.slxos_capabilities - - -def run_commands(module, commands): - """Run command list against connection. - - Get new or previously used connection and send commands to it one at a time, - collecting response. - - Args: - module: A valid AnsibleModule instance. - commands: Iterable of command strings. - - Returns: - A list of output strings. - """ - responses = list() - connection = get_connection(module) - - for cmd in to_list(commands): - if isinstance(cmd, dict): - command = cmd['command'] - prompt = cmd['prompt'] - answer = cmd['answer'] - else: - command = cmd - prompt = None - answer = None - - out = connection.get(command, prompt, answer) - - try: - out = to_text(out, errors='surrogate_or_strict') - except UnicodeError: - module.fail_json(msg=u'Failed to decode output from %s: %s' % (cmd, to_text(out))) - - responses.append(out) - - return responses - - -def get_config(module): - """Get switch configuration - - Gets the described device's current configuration. If a configuration has - already been retrieved it will return the previously obtained configuration. - - Args: - module: A valid AnsibleModule instance. - - Returns: - A string containing the configuration. - """ - if not hasattr(module, 'device_configs'): - module.device_configs = {} - elif module.device_configs != {}: - return module.device_configs - - connection = get_connection(module) - out = connection.get_config() - cfg = to_text(out, errors='surrogate_then_replace').strip() - module.device_configs = cfg - return cfg - - -def load_config(module, commands): - """Apply a list of commands to a device. - - Given a list of commands apply them to the device to modify the - configuration in bulk. - - Args: - module: A valid AnsibleModule instance. - commands: Iterable of command strings. - - Returns: - None - """ - connection = get_connection(module) - connection.edit_config(commands) diff --git a/plugins/module_utils/network/sros/__init__.py b/plugins/module_utils/network/sros/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/sros/sros.py b/plugins/module_utils/network/sros/sros.py deleted file mode 100644 index 4bbce73903..0000000000 --- a/plugins/module_utils/network/sros/sros.py +++ /dev/null @@ -1,111 +0,0 @@ -# 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. -# -# Copyright (c) 2016 Peter Sprygada, -# -# 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. -# -import re - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import exec_command - -_DEVICE_CONFIGS = {} - -sros_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'), - 'timeout': dict(type='int'), -} -sros_argument_spec = { - 'provider': dict(type='dict', options=sros_provider_spec), -} -sros_top_spec = { - 'host': dict(removed_in_version=2.9), - 'port': dict(removed_in_version=2.9, type='int'), - 'username': dict(removed_in_version=2.9), - 'password': dict(removed_in_version=2.9, no_log=True), - 'ssh_keyfile': dict(removed_in_version=2.9, type='path'), - 'timeout': dict(removed_in_version=2.9, type='int'), -} -sros_argument_spec.update(sros_top_spec) - - -def check_args(module, warnings): - pass - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - cmd = 'admin display-config ' - 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): - for command in to_list(commands): - 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, 'exit all') diff --git a/plugins/module_utils/network/voss/__init__.py b/plugins/module_utils/network/voss/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/network/voss/voss.py b/plugins/module_utils/network/voss/voss.py deleted file mode 100644 index 0de269e440..0000000000 --- a/plugins/module_utils/network/voss/voss.py +++ /dev/null @@ -1,219 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2018 Extreme Networks Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -import json -import re - -from ansible.module_utils._text import to_native, to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list, ComplexList -from ansible.module_utils.connection import Connection, ConnectionError -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, ConfigLine - -_DEVICE_CONFIGS = {} - -DEFAULT_COMMENT_TOKENS = ['#', '!', '/*', '*/', 'echo'] - -DEFAULT_IGNORE_LINES_RE = set([ - re.compile(r"Preparing to Display Configuration\.\.\.") -]) - - -def get_connection(module): - if hasattr(module, '_voss_connection'): - return module._voss_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module._voss_connection = Connection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type %s' % network_api) - - return module._voss_connection - - -def get_capabilities(module): - if hasattr(module, '_voss_capabilities'): - return module._voss_capabilities - try: - capabilities = Connection(module._socket_path).get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module._voss_capabilities = json.loads(capabilities) - return module._voss_capabilities - - -def get_defaults_flag(module): - connection = get_connection(module) - try: - out = connection.get_defaults_flag() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return to_text(out, errors='surrogate_then_replace').strip() - - -def get_config(module, source='running', flags=None): - flag_str = ' '.join(to_list(flags)) - - try: - return _DEVICE_CONFIGS[flag_str] - except KeyError: - connection = get_connection(module) - try: - out = connection.get_config(source=source, flags=flags) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - cfg = to_text(out, errors='surrogate_then_replace').strip() - _DEVICE_CONFIGS[flag_str] = cfg - return cfg - - -def 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): - connection = get_connection(module) - try: - out = connection.run_commands(commands=commands, check_rc=check_rc) - return out - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def load_config(module, commands): - connection = get_connection(module) - - try: - resp = connection.edit_config(commands) - return resp.get('response') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def get_sublevel_config(running_config, module): - contents = list() - current_config_contents = list() - sublevel_config = VossNetworkConfig(indent=0) - obj = running_config.get_object(module.params['parents']) - if obj: - contents = obj._children - for c in contents: - if isinstance(c, ConfigLine): - current_config_contents.append(c.raw) - sublevel_config.add(current_config_contents, module.params['parents']) - return sublevel_config - - -def ignore_line(text, tokens=None): - for item in (tokens or DEFAULT_COMMENT_TOKENS): - if text.startswith(item): - return True - for regex in DEFAULT_IGNORE_LINES_RE: - if regex.match(text): - return True - - -def voss_parse(lines, indent=None, comment_tokens=None): - toplevel = re.compile(r'(^interface.*$)|(^router \w+$)|(^router vrf \w+$)') - exitline = re.compile(r'^exit$') - entry_reg = re.compile(r'([{};])') - - ancestors = list() - config = list() - dup_parent_index = None - - for line in to_native(lines, errors='surrogate_or_strict').split('\n'): - text = entry_reg.sub('', line).strip() - - cfg = ConfigLine(text) - - if not text or ignore_line(text, comment_tokens): - continue - - # Handle top level commands - if toplevel.match(text): - # Looking to see if we have existing parent - for index, item in enumerate(config): - if item.text == text: - # This means we have an existing parent with same label - dup_parent_index = index - break - ancestors = [cfg] - config.append(cfg) - - # Handle 'exit' line - elif exitline.match(text): - ancestors = list() - - if dup_parent_index is not None: - # We're working with a duplicate parent - # Don't need to store exit, just go to next line in config - dup_parent_index = None - else: - cfg._parents = ancestors[:1] - config.append(cfg) - - # Handle sub-level commands. Only have single sub-level - elif ancestors: - cfg._parents = ancestors[:1] - if dup_parent_index is not None: - # Update existing entry, since this already exists in config - config[int(dup_parent_index)].add_child(cfg) - new_index = dup_parent_index + 1 - config.insert(new_index, cfg) - else: - ancestors[0].add_child(cfg) - config.append(cfg) - - else: - # Global command, no further special handling needed - config.append(cfg) - return config - - -class VossNetworkConfig(NetworkConfig): - - def load(self, s): - self._config_text = s - self._items = voss_parse(s, self._indent) - - def _diff_line(self, other): - updates = list() - for item in self.items: - if str(item) == "exit": - if updates and updates[-1]._parents: - updates.append(item) - elif item not in other: - updates.append(item) - return updates diff --git a/plugins/modules/a10_server.py b/plugins/modules/a10_server.py deleted file mode 120000 index 99fd065069..0000000000 --- a/plugins/modules/a10_server.py +++ /dev/null @@ -1 +0,0 @@ -./network/a10/a10_server.py \ No newline at end of file diff --git a/plugins/modules/a10_server_axapi3.py b/plugins/modules/a10_server_axapi3.py deleted file mode 120000 index d1b24a82aa..0000000000 --- a/plugins/modules/a10_server_axapi3.py +++ /dev/null @@ -1 +0,0 @@ -./network/a10/a10_server_axapi3.py \ No newline at end of file diff --git a/plugins/modules/a10_service_group.py b/plugins/modules/a10_service_group.py deleted file mode 120000 index 423b0b7a5e..0000000000 --- a/plugins/modules/a10_service_group.py +++ /dev/null @@ -1 +0,0 @@ -./network/a10/a10_service_group.py \ No newline at end of file diff --git a/plugins/modules/a10_virtual_server.py b/plugins/modules/a10_virtual_server.py deleted file mode 120000 index 2ea33dccb7..0000000000 --- a/plugins/modules/a10_virtual_server.py +++ /dev/null @@ -1 +0,0 @@ -./network/a10/a10_virtual_server.py \ No newline at end of file diff --git a/plugins/modules/aci_interface_policy_fc.py b/plugins/modules/aci_interface_policy_fc.py deleted file mode 120000 index e1390129c5..0000000000 --- a/plugins/modules/aci_interface_policy_fc.py +++ /dev/null @@ -1 +0,0 @@ -./network/aci/aci_interface_policy_fc.py \ No newline at end of file diff --git a/plugins/modules/aci_interface_policy_l2.py b/plugins/modules/aci_interface_policy_l2.py deleted file mode 120000 index cecdeeaa61..0000000000 --- a/plugins/modules/aci_interface_policy_l2.py +++ /dev/null @@ -1 +0,0 @@ -./network/aci/aci_interface_policy_l2.py \ No newline at end of file diff --git a/plugins/modules/aci_interface_policy_lldp.py b/plugins/modules/aci_interface_policy_lldp.py deleted file mode 120000 index 5b37254528..0000000000 --- a/plugins/modules/aci_interface_policy_lldp.py +++ /dev/null @@ -1 +0,0 @@ -./network/aci/aci_interface_policy_lldp.py \ No newline at end of file diff --git a/plugins/modules/aci_interface_policy_mcp.py b/plugins/modules/aci_interface_policy_mcp.py deleted file mode 120000 index 9e17981928..0000000000 --- a/plugins/modules/aci_interface_policy_mcp.py +++ /dev/null @@ -1 +0,0 @@ -./network/aci/aci_interface_policy_mcp.py \ No newline at end of file diff --git a/plugins/modules/aci_interface_policy_port_channel.py b/plugins/modules/aci_interface_policy_port_channel.py deleted file mode 120000 index ea297f0714..0000000000 --- a/plugins/modules/aci_interface_policy_port_channel.py +++ /dev/null @@ -1 +0,0 @@ -./network/aci/aci_interface_policy_port_channel.py \ No newline at end of file diff --git a/plugins/modules/aci_interface_policy_port_security.py b/plugins/modules/aci_interface_policy_port_security.py deleted file mode 120000 index 214bdc11df..0000000000 --- a/plugins/modules/aci_interface_policy_port_security.py +++ /dev/null @@ -1 +0,0 @@ -./network/aci/aci_interface_policy_port_security.py \ No newline at end of file diff --git a/plugins/modules/aireos_command.py b/plugins/modules/aireos_command.py deleted file mode 120000 index 03f4c2f9dc..0000000000 --- a/plugins/modules/aireos_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/aireos/aireos_command.py \ No newline at end of file diff --git a/plugins/modules/aireos_config.py b/plugins/modules/aireos_config.py deleted file mode 120000 index ed05f96c95..0000000000 --- a/plugins/modules/aireos_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/aireos/aireos_config.py \ No newline at end of file diff --git a/plugins/modules/apconos_command.py b/plugins/modules/apconos_command.py deleted file mode 120000 index ef8172f4f1..0000000000 --- a/plugins/modules/apconos_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/apconos/apconos_command.py \ No newline at end of file diff --git a/plugins/modules/aruba_command.py b/plugins/modules/aruba_command.py deleted file mode 120000 index fa7d70fcbd..0000000000 --- a/plugins/modules/aruba_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/aruba/aruba_command.py \ No newline at end of file diff --git a/plugins/modules/aruba_config.py b/plugins/modules/aruba_config.py deleted file mode 120000 index 269a7038b7..0000000000 --- a/plugins/modules/aruba_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/aruba/aruba_config.py \ No newline at end of file diff --git a/plugins/modules/avi_actiongroupconfig.py b/plugins/modules/avi_actiongroupconfig.py deleted file mode 120000 index 84caa3c14e..0000000000 --- a/plugins/modules/avi_actiongroupconfig.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_actiongroupconfig.py \ No newline at end of file diff --git a/plugins/modules/avi_alertconfig.py b/plugins/modules/avi_alertconfig.py deleted file mode 120000 index 42df98cc51..0000000000 --- a/plugins/modules/avi_alertconfig.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_alertconfig.py \ No newline at end of file diff --git a/plugins/modules/avi_alertemailconfig.py b/plugins/modules/avi_alertemailconfig.py deleted file mode 120000 index 3ef61f542c..0000000000 --- a/plugins/modules/avi_alertemailconfig.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_alertemailconfig.py \ No newline at end of file diff --git a/plugins/modules/avi_alertscriptconfig.py b/plugins/modules/avi_alertscriptconfig.py deleted file mode 120000 index 448dc7e2c6..0000000000 --- a/plugins/modules/avi_alertscriptconfig.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_alertscriptconfig.py \ No newline at end of file diff --git a/plugins/modules/avi_alertsyslogconfig.py b/plugins/modules/avi_alertsyslogconfig.py deleted file mode 120000 index 0191598d40..0000000000 --- a/plugins/modules/avi_alertsyslogconfig.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_alertsyslogconfig.py \ No newline at end of file diff --git a/plugins/modules/avi_analyticsprofile.py b/plugins/modules/avi_analyticsprofile.py deleted file mode 120000 index 146f3848d9..0000000000 --- a/plugins/modules/avi_analyticsprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_analyticsprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_api_session.py b/plugins/modules/avi_api_session.py deleted file mode 120000 index c8fcc38c37..0000000000 --- a/plugins/modules/avi_api_session.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_api_session.py \ No newline at end of file diff --git a/plugins/modules/avi_api_version.py b/plugins/modules/avi_api_version.py deleted file mode 120000 index 987b817a46..0000000000 --- a/plugins/modules/avi_api_version.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_api_version.py \ No newline at end of file diff --git a/plugins/modules/avi_applicationpersistenceprofile.py b/plugins/modules/avi_applicationpersistenceprofile.py deleted file mode 120000 index 386672b470..0000000000 --- a/plugins/modules/avi_applicationpersistenceprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_applicationpersistenceprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_applicationprofile.py b/plugins/modules/avi_applicationprofile.py deleted file mode 120000 index 5c66d51882..0000000000 --- a/plugins/modules/avi_applicationprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_applicationprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_authprofile.py b/plugins/modules/avi_authprofile.py deleted file mode 120000 index d5a8fe4aa4..0000000000 --- a/plugins/modules/avi_authprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_authprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_autoscalelaunchconfig.py b/plugins/modules/avi_autoscalelaunchconfig.py deleted file mode 120000 index 353ef3dbb6..0000000000 --- a/plugins/modules/avi_autoscalelaunchconfig.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_autoscalelaunchconfig.py \ No newline at end of file diff --git a/plugins/modules/avi_backup.py b/plugins/modules/avi_backup.py deleted file mode 120000 index 7d24992bc7..0000000000 --- a/plugins/modules/avi_backup.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_backup.py \ No newline at end of file diff --git a/plugins/modules/avi_backupconfiguration.py b/plugins/modules/avi_backupconfiguration.py deleted file mode 120000 index 539cd3543a..0000000000 --- a/plugins/modules/avi_backupconfiguration.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_backupconfiguration.py \ No newline at end of file diff --git a/plugins/modules/avi_certificatemanagementprofile.py b/plugins/modules/avi_certificatemanagementprofile.py deleted file mode 120000 index 3b799bbae5..0000000000 --- a/plugins/modules/avi_certificatemanagementprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_certificatemanagementprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_cloud.py b/plugins/modules/avi_cloud.py deleted file mode 120000 index 9b8b484630..0000000000 --- a/plugins/modules/avi_cloud.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_cloud.py \ No newline at end of file diff --git a/plugins/modules/avi_cloudconnectoruser.py b/plugins/modules/avi_cloudconnectoruser.py deleted file mode 120000 index 7b6b034525..0000000000 --- a/plugins/modules/avi_cloudconnectoruser.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_cloudconnectoruser.py \ No newline at end of file diff --git a/plugins/modules/avi_cloudproperties.py b/plugins/modules/avi_cloudproperties.py deleted file mode 120000 index 8dbec3a746..0000000000 --- a/plugins/modules/avi_cloudproperties.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_cloudproperties.py \ No newline at end of file diff --git a/plugins/modules/avi_cluster.py b/plugins/modules/avi_cluster.py deleted file mode 120000 index c6f71755e8..0000000000 --- a/plugins/modules/avi_cluster.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_cluster.py \ No newline at end of file diff --git a/plugins/modules/avi_clusterclouddetails.py b/plugins/modules/avi_clusterclouddetails.py deleted file mode 120000 index cc0d4856ef..0000000000 --- a/plugins/modules/avi_clusterclouddetails.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_clusterclouddetails.py \ No newline at end of file diff --git a/plugins/modules/avi_controllerproperties.py b/plugins/modules/avi_controllerproperties.py deleted file mode 120000 index a7abc19bd0..0000000000 --- a/plugins/modules/avi_controllerproperties.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_controllerproperties.py \ No newline at end of file diff --git a/plugins/modules/avi_customipamdnsprofile.py b/plugins/modules/avi_customipamdnsprofile.py deleted file mode 120000 index 572c54f0da..0000000000 --- a/plugins/modules/avi_customipamdnsprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_customipamdnsprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_dnspolicy.py b/plugins/modules/avi_dnspolicy.py deleted file mode 120000 index af16af0fa6..0000000000 --- a/plugins/modules/avi_dnspolicy.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_dnspolicy.py \ No newline at end of file diff --git a/plugins/modules/avi_errorpagebody.py b/plugins/modules/avi_errorpagebody.py deleted file mode 120000 index 41297c0eb1..0000000000 --- a/plugins/modules/avi_errorpagebody.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_errorpagebody.py \ No newline at end of file diff --git a/plugins/modules/avi_errorpageprofile.py b/plugins/modules/avi_errorpageprofile.py deleted file mode 120000 index 832f151e4a..0000000000 --- a/plugins/modules/avi_errorpageprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_errorpageprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_gslb.py b/plugins/modules/avi_gslb.py deleted file mode 120000 index 8303d3d01a..0000000000 --- a/plugins/modules/avi_gslb.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_gslb.py \ No newline at end of file diff --git a/plugins/modules/avi_gslbgeodbprofile.py b/plugins/modules/avi_gslbgeodbprofile.py deleted file mode 120000 index e0dba0b569..0000000000 --- a/plugins/modules/avi_gslbgeodbprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_gslbgeodbprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_gslbservice.py b/plugins/modules/avi_gslbservice.py deleted file mode 120000 index bf506833bd..0000000000 --- a/plugins/modules/avi_gslbservice.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_gslbservice.py \ No newline at end of file diff --git a/plugins/modules/avi_gslbservice_patch_member.py b/plugins/modules/avi_gslbservice_patch_member.py deleted file mode 120000 index 8e565ff7df..0000000000 --- a/plugins/modules/avi_gslbservice_patch_member.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_gslbservice_patch_member.py \ No newline at end of file diff --git a/plugins/modules/avi_hardwaresecuritymodulegroup.py b/plugins/modules/avi_hardwaresecuritymodulegroup.py deleted file mode 120000 index 4e53e1edce..0000000000 --- a/plugins/modules/avi_hardwaresecuritymodulegroup.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_hardwaresecuritymodulegroup.py \ No newline at end of file diff --git a/plugins/modules/avi_healthmonitor.py b/plugins/modules/avi_healthmonitor.py deleted file mode 120000 index 37b015e75d..0000000000 --- a/plugins/modules/avi_healthmonitor.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_healthmonitor.py \ No newline at end of file diff --git a/plugins/modules/avi_httppolicyset.py b/plugins/modules/avi_httppolicyset.py deleted file mode 120000 index 8e627d01fa..0000000000 --- a/plugins/modules/avi_httppolicyset.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_httppolicyset.py \ No newline at end of file diff --git a/plugins/modules/avi_ipaddrgroup.py b/plugins/modules/avi_ipaddrgroup.py deleted file mode 120000 index d88d8f6110..0000000000 --- a/plugins/modules/avi_ipaddrgroup.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_ipaddrgroup.py \ No newline at end of file diff --git a/plugins/modules/avi_ipamdnsproviderprofile.py b/plugins/modules/avi_ipamdnsproviderprofile.py deleted file mode 120000 index 9850e577e2..0000000000 --- a/plugins/modules/avi_ipamdnsproviderprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_ipamdnsproviderprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_l4policyset.py b/plugins/modules/avi_l4policyset.py deleted file mode 120000 index 55c7a049e1..0000000000 --- a/plugins/modules/avi_l4policyset.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_l4policyset.py \ No newline at end of file diff --git a/plugins/modules/avi_microservicegroup.py b/plugins/modules/avi_microservicegroup.py deleted file mode 120000 index e0477e46ec..0000000000 --- a/plugins/modules/avi_microservicegroup.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_microservicegroup.py \ No newline at end of file diff --git a/plugins/modules/avi_network.py b/plugins/modules/avi_network.py deleted file mode 120000 index 191e944812..0000000000 --- a/plugins/modules/avi_network.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_network.py \ No newline at end of file diff --git a/plugins/modules/avi_networkprofile.py b/plugins/modules/avi_networkprofile.py deleted file mode 120000 index b416bebcca..0000000000 --- a/plugins/modules/avi_networkprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_networkprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_networksecuritypolicy.py b/plugins/modules/avi_networksecuritypolicy.py deleted file mode 120000 index 0e99b261ac..0000000000 --- a/plugins/modules/avi_networksecuritypolicy.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_networksecuritypolicy.py \ No newline at end of file diff --git a/plugins/modules/avi_pkiprofile.py b/plugins/modules/avi_pkiprofile.py deleted file mode 120000 index 0d69e1ec5b..0000000000 --- a/plugins/modules/avi_pkiprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_pkiprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_pool.py b/plugins/modules/avi_pool.py deleted file mode 120000 index 4c7b4d5335..0000000000 --- a/plugins/modules/avi_pool.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_pool.py \ No newline at end of file diff --git a/plugins/modules/avi_poolgroup.py b/plugins/modules/avi_poolgroup.py deleted file mode 120000 index 743581442f..0000000000 --- a/plugins/modules/avi_poolgroup.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_poolgroup.py \ No newline at end of file diff --git a/plugins/modules/avi_poolgroupdeploymentpolicy.py b/plugins/modules/avi_poolgroupdeploymentpolicy.py deleted file mode 120000 index 53e56e7816..0000000000 --- a/plugins/modules/avi_poolgroupdeploymentpolicy.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_poolgroupdeploymentpolicy.py \ No newline at end of file diff --git a/plugins/modules/avi_prioritylabels.py b/plugins/modules/avi_prioritylabels.py deleted file mode 120000 index 9d8790b5b0..0000000000 --- a/plugins/modules/avi_prioritylabels.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_prioritylabels.py \ No newline at end of file diff --git a/plugins/modules/avi_role.py b/plugins/modules/avi_role.py deleted file mode 120000 index 284e9a8bb2..0000000000 --- a/plugins/modules/avi_role.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_role.py \ No newline at end of file diff --git a/plugins/modules/avi_scheduler.py b/plugins/modules/avi_scheduler.py deleted file mode 120000 index 1be9d96850..0000000000 --- a/plugins/modules/avi_scheduler.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_scheduler.py \ No newline at end of file diff --git a/plugins/modules/avi_seproperties.py b/plugins/modules/avi_seproperties.py deleted file mode 120000 index 0a4857945b..0000000000 --- a/plugins/modules/avi_seproperties.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_seproperties.py \ No newline at end of file diff --git a/plugins/modules/avi_serverautoscalepolicy.py b/plugins/modules/avi_serverautoscalepolicy.py deleted file mode 120000 index 43195a3fe9..0000000000 --- a/plugins/modules/avi_serverautoscalepolicy.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_serverautoscalepolicy.py \ No newline at end of file diff --git a/plugins/modules/avi_serviceengine.py b/plugins/modules/avi_serviceengine.py deleted file mode 120000 index f465dab512..0000000000 --- a/plugins/modules/avi_serviceengine.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_serviceengine.py \ No newline at end of file diff --git a/plugins/modules/avi_serviceenginegroup.py b/plugins/modules/avi_serviceenginegroup.py deleted file mode 120000 index a2b01114b3..0000000000 --- a/plugins/modules/avi_serviceenginegroup.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_serviceenginegroup.py \ No newline at end of file diff --git a/plugins/modules/avi_snmptrapprofile.py b/plugins/modules/avi_snmptrapprofile.py deleted file mode 120000 index c2adc10307..0000000000 --- a/plugins/modules/avi_snmptrapprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_snmptrapprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_sslkeyandcertificate.py b/plugins/modules/avi_sslkeyandcertificate.py deleted file mode 120000 index 9e623b82b0..0000000000 --- a/plugins/modules/avi_sslkeyandcertificate.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_sslkeyandcertificate.py \ No newline at end of file diff --git a/plugins/modules/avi_sslprofile.py b/plugins/modules/avi_sslprofile.py deleted file mode 120000 index 4b6d15d032..0000000000 --- a/plugins/modules/avi_sslprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_sslprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_stringgroup.py b/plugins/modules/avi_stringgroup.py deleted file mode 120000 index 63d5e4e5fa..0000000000 --- a/plugins/modules/avi_stringgroup.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_stringgroup.py \ No newline at end of file diff --git a/plugins/modules/avi_systemconfiguration.py b/plugins/modules/avi_systemconfiguration.py deleted file mode 120000 index 70c77b6fbb..0000000000 --- a/plugins/modules/avi_systemconfiguration.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_systemconfiguration.py \ No newline at end of file diff --git a/plugins/modules/avi_tenant.py b/plugins/modules/avi_tenant.py deleted file mode 120000 index 4ae0dd1086..0000000000 --- a/plugins/modules/avi_tenant.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_tenant.py \ No newline at end of file diff --git a/plugins/modules/avi_trafficcloneprofile.py b/plugins/modules/avi_trafficcloneprofile.py deleted file mode 120000 index 905e20481e..0000000000 --- a/plugins/modules/avi_trafficcloneprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_trafficcloneprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_user.py b/plugins/modules/avi_user.py deleted file mode 120000 index 6868e4e14e..0000000000 --- a/plugins/modules/avi_user.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_user.py \ No newline at end of file diff --git a/plugins/modules/avi_useraccount.py b/plugins/modules/avi_useraccount.py deleted file mode 120000 index 33c782d5c9..0000000000 --- a/plugins/modules/avi_useraccount.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_useraccount.py \ No newline at end of file diff --git a/plugins/modules/avi_useraccountprofile.py b/plugins/modules/avi_useraccountprofile.py deleted file mode 120000 index c6eb252d02..0000000000 --- a/plugins/modules/avi_useraccountprofile.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_useraccountprofile.py \ No newline at end of file diff --git a/plugins/modules/avi_virtualservice.py b/plugins/modules/avi_virtualservice.py deleted file mode 120000 index 554185407d..0000000000 --- a/plugins/modules/avi_virtualservice.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_virtualservice.py \ No newline at end of file diff --git a/plugins/modules/avi_vrfcontext.py b/plugins/modules/avi_vrfcontext.py deleted file mode 120000 index 5c7bb937fe..0000000000 --- a/plugins/modules/avi_vrfcontext.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_vrfcontext.py \ No newline at end of file diff --git a/plugins/modules/avi_vsdatascriptset.py b/plugins/modules/avi_vsdatascriptset.py deleted file mode 120000 index 9a683f5e05..0000000000 --- a/plugins/modules/avi_vsdatascriptset.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_vsdatascriptset.py \ No newline at end of file diff --git a/plugins/modules/avi_vsvip.py b/plugins/modules/avi_vsvip.py deleted file mode 120000 index 9e8a063f4d..0000000000 --- a/plugins/modules/avi_vsvip.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_vsvip.py \ No newline at end of file diff --git a/plugins/modules/avi_webhook.py b/plugins/modules/avi_webhook.py deleted file mode 120000 index 2e5dcf4a38..0000000000 --- a/plugins/modules/avi_webhook.py +++ /dev/null @@ -1 +0,0 @@ -./network/avi/avi_webhook.py \ No newline at end of file diff --git a/plugins/modules/bcf_switch.py b/plugins/modules/bcf_switch.py deleted file mode 120000 index 374b19d591..0000000000 --- a/plugins/modules/bcf_switch.py +++ /dev/null @@ -1 +0,0 @@ -./network/bigswitch/bcf_switch.py \ No newline at end of file diff --git a/plugins/modules/bigip_asm_policy.py b/plugins/modules/bigip_asm_policy.py deleted file mode 120000 index 0eecd61082..0000000000 --- a/plugins/modules/bigip_asm_policy.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigip_asm_policy.py \ No newline at end of file diff --git a/plugins/modules/bigip_device_info.py b/plugins/modules/bigip_device_info.py deleted file mode 120000 index 45c144358e..0000000000 --- a/plugins/modules/bigip_device_info.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigip_device_info.py \ No newline at end of file diff --git a/plugins/modules/bigip_device_traffic_group.py b/plugins/modules/bigip_device_traffic_group.py deleted file mode 120000 index 0c80b87f6b..0000000000 --- a/plugins/modules/bigip_device_traffic_group.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigip_device_traffic_group.py \ No newline at end of file diff --git a/plugins/modules/bigip_facts.py b/plugins/modules/bigip_facts.py deleted file mode 120000 index a8038eeab5..0000000000 --- a/plugins/modules/bigip_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigip_facts.py \ No newline at end of file diff --git a/plugins/modules/bigip_firewall_address_list.py b/plugins/modules/bigip_firewall_address_list.py deleted file mode 120000 index 71634ff950..0000000000 --- a/plugins/modules/bigip_firewall_address_list.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigip_firewall_address_list.py \ No newline at end of file diff --git a/plugins/modules/bigip_firewall_port_list.py b/plugins/modules/bigip_firewall_port_list.py deleted file mode 120000 index a1e0da8f1c..0000000000 --- a/plugins/modules/bigip_firewall_port_list.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigip_firewall_port_list.py \ No newline at end of file diff --git a/plugins/modules/bigip_gtm_facts.py b/plugins/modules/bigip_gtm_facts.py deleted file mode 120000 index b18537ce3f..0000000000 --- a/plugins/modules/bigip_gtm_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigip_gtm_facts.py \ No newline at end of file diff --git a/plugins/modules/bigip_lx_package.py b/plugins/modules/bigip_lx_package.py deleted file mode 120000 index 712175a9bf..0000000000 --- a/plugins/modules/bigip_lx_package.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigip_lx_package.py \ No newline at end of file diff --git a/plugins/modules/bigiq_device_info.py b/plugins/modules/bigiq_device_info.py deleted file mode 120000 index 589fb5afc8..0000000000 --- a/plugins/modules/bigiq_device_info.py +++ /dev/null @@ -1 +0,0 @@ -./network/f5/bigiq_device_info.py \ No newline at end of file diff --git a/plugins/modules/bigmon_chain.py b/plugins/modules/bigmon_chain.py deleted file mode 120000 index 849bf2f6de..0000000000 --- a/plugins/modules/bigmon_chain.py +++ /dev/null @@ -1 +0,0 @@ -./network/bigswitch/bigmon_chain.py \ No newline at end of file diff --git a/plugins/modules/bigmon_policy.py b/plugins/modules/bigmon_policy.py deleted file mode 120000 index de140bab6c..0000000000 --- a/plugins/modules/bigmon_policy.py +++ /dev/null @@ -1 +0,0 @@ -./network/bigswitch/bigmon_policy.py \ No newline at end of file diff --git a/plugins/modules/ce_aaa_server.py b/plugins/modules/ce_aaa_server.py deleted file mode 120000 index 29e9b5301b..0000000000 --- a/plugins/modules/ce_aaa_server.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_aaa_server.py \ No newline at end of file diff --git a/plugins/modules/ce_aaa_server_host.py b/plugins/modules/ce_aaa_server_host.py deleted file mode 120000 index a2b8f01d10..0000000000 --- a/plugins/modules/ce_aaa_server_host.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_aaa_server_host.py \ No newline at end of file diff --git a/plugins/modules/ce_acl.py b/plugins/modules/ce_acl.py deleted file mode 120000 index 5d83d9afec..0000000000 --- a/plugins/modules/ce_acl.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_acl.py \ No newline at end of file diff --git a/plugins/modules/ce_acl_advance.py b/plugins/modules/ce_acl_advance.py deleted file mode 120000 index e46f738617..0000000000 --- a/plugins/modules/ce_acl_advance.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_acl_advance.py \ No newline at end of file diff --git a/plugins/modules/ce_acl_interface.py b/plugins/modules/ce_acl_interface.py deleted file mode 120000 index 2abd3140b4..0000000000 --- a/plugins/modules/ce_acl_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_acl_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_bfd_global.py b/plugins/modules/ce_bfd_global.py deleted file mode 120000 index 34f873f45c..0000000000 --- a/plugins/modules/ce_bfd_global.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_bfd_global.py \ No newline at end of file diff --git a/plugins/modules/ce_bfd_session.py b/plugins/modules/ce_bfd_session.py deleted file mode 120000 index 28aa41749a..0000000000 --- a/plugins/modules/ce_bfd_session.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_bfd_session.py \ No newline at end of file diff --git a/plugins/modules/ce_bfd_view.py b/plugins/modules/ce_bfd_view.py deleted file mode 120000 index d11fe25051..0000000000 --- a/plugins/modules/ce_bfd_view.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_bfd_view.py \ No newline at end of file diff --git a/plugins/modules/ce_bgp.py b/plugins/modules/ce_bgp.py deleted file mode 120000 index cc0294c2dc..0000000000 --- a/plugins/modules/ce_bgp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_bgp.py \ No newline at end of file diff --git a/plugins/modules/ce_bgp_af.py b/plugins/modules/ce_bgp_af.py deleted file mode 120000 index 3fcd850f3f..0000000000 --- a/plugins/modules/ce_bgp_af.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_bgp_af.py \ No newline at end of file diff --git a/plugins/modules/ce_bgp_neighbor.py b/plugins/modules/ce_bgp_neighbor.py deleted file mode 120000 index c4adea5e39..0000000000 --- a/plugins/modules/ce_bgp_neighbor.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_bgp_neighbor.py \ No newline at end of file diff --git a/plugins/modules/ce_bgp_neighbor_af.py b/plugins/modules/ce_bgp_neighbor_af.py deleted file mode 120000 index 765d9b7d63..0000000000 --- a/plugins/modules/ce_bgp_neighbor_af.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_bgp_neighbor_af.py \ No newline at end of file diff --git a/plugins/modules/ce_command.py b/plugins/modules/ce_command.py deleted file mode 120000 index 1662530542..0000000000 --- a/plugins/modules/ce_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_command.py \ No newline at end of file diff --git a/plugins/modules/ce_config.py b/plugins/modules/ce_config.py deleted file mode 120000 index 4ebe1271fc..0000000000 --- a/plugins/modules/ce_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_config.py \ No newline at end of file diff --git a/plugins/modules/ce_dldp.py b/plugins/modules/ce_dldp.py deleted file mode 120000 index fb467a9527..0000000000 --- a/plugins/modules/ce_dldp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_dldp.py \ No newline at end of file diff --git a/plugins/modules/ce_dldp_interface.py b/plugins/modules/ce_dldp_interface.py deleted file mode 120000 index d2fcd41442..0000000000 --- a/plugins/modules/ce_dldp_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_dldp_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_eth_trunk.py b/plugins/modules/ce_eth_trunk.py deleted file mode 120000 index 0726398a7e..0000000000 --- a/plugins/modules/ce_eth_trunk.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_eth_trunk.py \ No newline at end of file diff --git a/plugins/modules/ce_evpn_bd_vni.py b/plugins/modules/ce_evpn_bd_vni.py deleted file mode 120000 index e4189d8a07..0000000000 --- a/plugins/modules/ce_evpn_bd_vni.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_evpn_bd_vni.py \ No newline at end of file diff --git a/plugins/modules/ce_evpn_bgp.py b/plugins/modules/ce_evpn_bgp.py deleted file mode 120000 index 337478cfb8..0000000000 --- a/plugins/modules/ce_evpn_bgp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_evpn_bgp.py \ No newline at end of file diff --git a/plugins/modules/ce_evpn_bgp_rr.py b/plugins/modules/ce_evpn_bgp_rr.py deleted file mode 120000 index a21bf94464..0000000000 --- a/plugins/modules/ce_evpn_bgp_rr.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_evpn_bgp_rr.py \ No newline at end of file diff --git a/plugins/modules/ce_evpn_global.py b/plugins/modules/ce_evpn_global.py deleted file mode 120000 index a1dc809de9..0000000000 --- a/plugins/modules/ce_evpn_global.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_evpn_global.py \ No newline at end of file diff --git a/plugins/modules/ce_facts.py b/plugins/modules/ce_facts.py deleted file mode 120000 index a918251a12..0000000000 --- a/plugins/modules/ce_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_facts.py \ No newline at end of file diff --git a/plugins/modules/ce_file_copy.py b/plugins/modules/ce_file_copy.py deleted file mode 120000 index cad0b3db10..0000000000 --- a/plugins/modules/ce_file_copy.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_file_copy.py \ No newline at end of file diff --git a/plugins/modules/ce_info_center_debug.py b/plugins/modules/ce_info_center_debug.py deleted file mode 120000 index decc9d6276..0000000000 --- a/plugins/modules/ce_info_center_debug.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_info_center_debug.py \ No newline at end of file diff --git a/plugins/modules/ce_info_center_global.py b/plugins/modules/ce_info_center_global.py deleted file mode 120000 index 1b7e46a5ae..0000000000 --- a/plugins/modules/ce_info_center_global.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_info_center_global.py \ No newline at end of file diff --git a/plugins/modules/ce_info_center_log.py b/plugins/modules/ce_info_center_log.py deleted file mode 120000 index f1bb1149ac..0000000000 --- a/plugins/modules/ce_info_center_log.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_info_center_log.py \ No newline at end of file diff --git a/plugins/modules/ce_info_center_trap.py b/plugins/modules/ce_info_center_trap.py deleted file mode 120000 index b3e89dfd56..0000000000 --- a/plugins/modules/ce_info_center_trap.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_info_center_trap.py \ No newline at end of file diff --git a/plugins/modules/ce_interface.py b/plugins/modules/ce_interface.py deleted file mode 120000 index 596a9dee5d..0000000000 --- a/plugins/modules/ce_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_interface_ospf.py b/plugins/modules/ce_interface_ospf.py deleted file mode 120000 index 0c88a16d40..0000000000 --- a/plugins/modules/ce_interface_ospf.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_interface_ospf.py \ No newline at end of file diff --git a/plugins/modules/ce_ip_interface.py b/plugins/modules/ce_ip_interface.py deleted file mode 120000 index 9a191b3a82..0000000000 --- a/plugins/modules/ce_ip_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_ip_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_is_is_instance.py b/plugins/modules/ce_is_is_instance.py deleted file mode 120000 index 4ac23b113c..0000000000 --- a/plugins/modules/ce_is_is_instance.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_is_is_instance.py \ No newline at end of file diff --git a/plugins/modules/ce_is_is_interface.py b/plugins/modules/ce_is_is_interface.py deleted file mode 120000 index d75b377d5a..0000000000 --- a/plugins/modules/ce_is_is_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_is_is_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_is_is_view.py b/plugins/modules/ce_is_is_view.py deleted file mode 120000 index 24a5c272d5..0000000000 --- a/plugins/modules/ce_is_is_view.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_is_is_view.py \ No newline at end of file diff --git a/plugins/modules/ce_lacp.py b/plugins/modules/ce_lacp.py deleted file mode 120000 index da54997b1b..0000000000 --- a/plugins/modules/ce_lacp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_lacp.py \ No newline at end of file diff --git a/plugins/modules/ce_link_status.py b/plugins/modules/ce_link_status.py deleted file mode 120000 index 20c075c484..0000000000 --- a/plugins/modules/ce_link_status.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_link_status.py \ No newline at end of file diff --git a/plugins/modules/ce_lldp.py b/plugins/modules/ce_lldp.py deleted file mode 120000 index 5c2b88a2b6..0000000000 --- a/plugins/modules/ce_lldp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_lldp.py \ No newline at end of file diff --git a/plugins/modules/ce_lldp_interface.py b/plugins/modules/ce_lldp_interface.py deleted file mode 120000 index 2a2e4be1e3..0000000000 --- a/plugins/modules/ce_lldp_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_lldp_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_mdn_interface.py b/plugins/modules/ce_mdn_interface.py deleted file mode 120000 index 41bfdd699d..0000000000 --- a/plugins/modules/ce_mdn_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_mdn_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_mlag_config.py b/plugins/modules/ce_mlag_config.py deleted file mode 120000 index 2043dcf3ae..0000000000 --- a/plugins/modules/ce_mlag_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_mlag_config.py \ No newline at end of file diff --git a/plugins/modules/ce_mlag_interface.py b/plugins/modules/ce_mlag_interface.py deleted file mode 120000 index 11e01c958f..0000000000 --- a/plugins/modules/ce_mlag_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_mlag_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_mtu.py b/plugins/modules/ce_mtu.py deleted file mode 120000 index b241140489..0000000000 --- a/plugins/modules/ce_mtu.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_mtu.py \ No newline at end of file diff --git a/plugins/modules/ce_multicast_global.py b/plugins/modules/ce_multicast_global.py deleted file mode 120000 index 10395fbe0e..0000000000 --- a/plugins/modules/ce_multicast_global.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_multicast_global.py \ No newline at end of file diff --git a/plugins/modules/ce_multicast_igmp_enable.py b/plugins/modules/ce_multicast_igmp_enable.py deleted file mode 120000 index 4f2a5bd5f6..0000000000 --- a/plugins/modules/ce_multicast_igmp_enable.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_multicast_igmp_enable.py \ No newline at end of file diff --git a/plugins/modules/ce_netconf.py b/plugins/modules/ce_netconf.py deleted file mode 120000 index 29551885a9..0000000000 --- a/plugins/modules/ce_netconf.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_netconf.py \ No newline at end of file diff --git a/plugins/modules/ce_netstream_aging.py b/plugins/modules/ce_netstream_aging.py deleted file mode 120000 index 43b9b6de55..0000000000 --- a/plugins/modules/ce_netstream_aging.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_netstream_aging.py \ No newline at end of file diff --git a/plugins/modules/ce_netstream_export.py b/plugins/modules/ce_netstream_export.py deleted file mode 120000 index 177eb813d5..0000000000 --- a/plugins/modules/ce_netstream_export.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_netstream_export.py \ No newline at end of file diff --git a/plugins/modules/ce_netstream_global.py b/plugins/modules/ce_netstream_global.py deleted file mode 120000 index 911ccfb550..0000000000 --- a/plugins/modules/ce_netstream_global.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_netstream_global.py \ No newline at end of file diff --git a/plugins/modules/ce_netstream_template.py b/plugins/modules/ce_netstream_template.py deleted file mode 120000 index 4ff5927c4c..0000000000 --- a/plugins/modules/ce_netstream_template.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_netstream_template.py \ No newline at end of file diff --git a/plugins/modules/ce_ntp.py b/plugins/modules/ce_ntp.py deleted file mode 120000 index adda3bb8e1..0000000000 --- a/plugins/modules/ce_ntp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_ntp.py \ No newline at end of file diff --git a/plugins/modules/ce_ntp_auth.py b/plugins/modules/ce_ntp_auth.py deleted file mode 120000 index 6004090fb8..0000000000 --- a/plugins/modules/ce_ntp_auth.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_ntp_auth.py \ No newline at end of file diff --git a/plugins/modules/ce_ospf.py b/plugins/modules/ce_ospf.py deleted file mode 120000 index 32d1a68297..0000000000 --- a/plugins/modules/ce_ospf.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_ospf.py \ No newline at end of file diff --git a/plugins/modules/ce_ospf_vrf.py b/plugins/modules/ce_ospf_vrf.py deleted file mode 120000 index 05ccc7c97b..0000000000 --- a/plugins/modules/ce_ospf_vrf.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_ospf_vrf.py \ No newline at end of file diff --git a/plugins/modules/ce_reboot.py b/plugins/modules/ce_reboot.py deleted file mode 120000 index 86c21846f7..0000000000 --- a/plugins/modules/ce_reboot.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_reboot.py \ No newline at end of file diff --git a/plugins/modules/ce_rollback.py b/plugins/modules/ce_rollback.py deleted file mode 120000 index 904394d753..0000000000 --- a/plugins/modules/ce_rollback.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_rollback.py \ No newline at end of file diff --git a/plugins/modules/ce_sflow.py b/plugins/modules/ce_sflow.py deleted file mode 120000 index f7fefce3d0..0000000000 --- a/plugins/modules/ce_sflow.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_sflow.py \ No newline at end of file diff --git a/plugins/modules/ce_snmp_community.py b/plugins/modules/ce_snmp_community.py deleted file mode 120000 index ff808f9b69..0000000000 --- a/plugins/modules/ce_snmp_community.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_snmp_community.py \ No newline at end of file diff --git a/plugins/modules/ce_snmp_contact.py b/plugins/modules/ce_snmp_contact.py deleted file mode 120000 index 0ae4f7c0c5..0000000000 --- a/plugins/modules/ce_snmp_contact.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_snmp_contact.py \ No newline at end of file diff --git a/plugins/modules/ce_snmp_location.py b/plugins/modules/ce_snmp_location.py deleted file mode 120000 index 1c4102a80c..0000000000 --- a/plugins/modules/ce_snmp_location.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_snmp_location.py \ No newline at end of file diff --git a/plugins/modules/ce_snmp_target_host.py b/plugins/modules/ce_snmp_target_host.py deleted file mode 120000 index a7ba244102..0000000000 --- a/plugins/modules/ce_snmp_target_host.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_snmp_target_host.py \ No newline at end of file diff --git a/plugins/modules/ce_snmp_traps.py b/plugins/modules/ce_snmp_traps.py deleted file mode 120000 index a3d1d35014..0000000000 --- a/plugins/modules/ce_snmp_traps.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_snmp_traps.py \ No newline at end of file diff --git a/plugins/modules/ce_snmp_user.py b/plugins/modules/ce_snmp_user.py deleted file mode 120000 index abbe135d88..0000000000 --- a/plugins/modules/ce_snmp_user.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_snmp_user.py \ No newline at end of file diff --git a/plugins/modules/ce_startup.py b/plugins/modules/ce_startup.py deleted file mode 120000 index ab98ad2f07..0000000000 --- a/plugins/modules/ce_startup.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_startup.py \ No newline at end of file diff --git a/plugins/modules/ce_static_route.py b/plugins/modules/ce_static_route.py deleted file mode 120000 index e3b1d7abd1..0000000000 --- a/plugins/modules/ce_static_route.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_static_route.py \ No newline at end of file diff --git a/plugins/modules/ce_static_route_bfd.py b/plugins/modules/ce_static_route_bfd.py deleted file mode 120000 index 84ad2de6fa..0000000000 --- a/plugins/modules/ce_static_route_bfd.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_static_route_bfd.py \ No newline at end of file diff --git a/plugins/modules/ce_stp.py b/plugins/modules/ce_stp.py deleted file mode 120000 index 71632098a4..0000000000 --- a/plugins/modules/ce_stp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_stp.py \ No newline at end of file diff --git a/plugins/modules/ce_switchport.py b/plugins/modules/ce_switchport.py deleted file mode 120000 index 29c8e4230f..0000000000 --- a/plugins/modules/ce_switchport.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_switchport.py \ No newline at end of file diff --git a/plugins/modules/ce_vlan.py b/plugins/modules/ce_vlan.py deleted file mode 120000 index cb6d11b712..0000000000 --- a/plugins/modules/ce_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vlan.py \ No newline at end of file diff --git a/plugins/modules/ce_vrf.py b/plugins/modules/ce_vrf.py deleted file mode 120000 index b7534a74bc..0000000000 --- a/plugins/modules/ce_vrf.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vrf.py \ No newline at end of file diff --git a/plugins/modules/ce_vrf_af.py b/plugins/modules/ce_vrf_af.py deleted file mode 120000 index a9a3676927..0000000000 --- a/plugins/modules/ce_vrf_af.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vrf_af.py \ No newline at end of file diff --git a/plugins/modules/ce_vrf_interface.py b/plugins/modules/ce_vrf_interface.py deleted file mode 120000 index 94ff7f0f3a..0000000000 --- a/plugins/modules/ce_vrf_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vrf_interface.py \ No newline at end of file diff --git a/plugins/modules/ce_vrrp.py b/plugins/modules/ce_vrrp.py deleted file mode 120000 index f8910f02eb..0000000000 --- a/plugins/modules/ce_vrrp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vrrp.py \ No newline at end of file diff --git a/plugins/modules/ce_vxlan_arp.py b/plugins/modules/ce_vxlan_arp.py deleted file mode 120000 index 04a187157f..0000000000 --- a/plugins/modules/ce_vxlan_arp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vxlan_arp.py \ No newline at end of file diff --git a/plugins/modules/ce_vxlan_gateway.py b/plugins/modules/ce_vxlan_gateway.py deleted file mode 120000 index 83ab3b8a4f..0000000000 --- a/plugins/modules/ce_vxlan_gateway.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vxlan_gateway.py \ No newline at end of file diff --git a/plugins/modules/ce_vxlan_global.py b/plugins/modules/ce_vxlan_global.py deleted file mode 120000 index aa6032cc10..0000000000 --- a/plugins/modules/ce_vxlan_global.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vxlan_global.py \ No newline at end of file diff --git a/plugins/modules/ce_vxlan_tunnel.py b/plugins/modules/ce_vxlan_tunnel.py deleted file mode 120000 index 60adcf7a30..0000000000 --- a/plugins/modules/ce_vxlan_tunnel.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vxlan_tunnel.py \ No newline at end of file diff --git a/plugins/modules/ce_vxlan_vap.py b/plugins/modules/ce_vxlan_vap.py deleted file mode 120000 index fe9710bed7..0000000000 --- a/plugins/modules/ce_vxlan_vap.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudengine/ce_vxlan_vap.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_access_layer_facts.py b/plugins/modules/checkpoint_access_layer_facts.py deleted file mode 120000 index 1c8ddf8cc3..0000000000 --- a/plugins/modules/checkpoint_access_layer_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_access_layer_facts.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_access_rule.py b/plugins/modules/checkpoint_access_rule.py deleted file mode 120000 index d0d064b070..0000000000 --- a/plugins/modules/checkpoint_access_rule.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_access_rule.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_access_rule_facts.py b/plugins/modules/checkpoint_access_rule_facts.py deleted file mode 120000 index 6b84f9ad45..0000000000 --- a/plugins/modules/checkpoint_access_rule_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_access_rule_facts.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_host.py b/plugins/modules/checkpoint_host.py deleted file mode 120000 index 1bd4813c17..0000000000 --- a/plugins/modules/checkpoint_host.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_host.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_host_facts.py b/plugins/modules/checkpoint_host_facts.py deleted file mode 120000 index 80ac928bba..0000000000 --- a/plugins/modules/checkpoint_host_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_host_facts.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_object_facts.py b/plugins/modules/checkpoint_object_facts.py deleted file mode 120000 index ef7886f3b5..0000000000 --- a/plugins/modules/checkpoint_object_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_object_facts.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_run_script.py b/plugins/modules/checkpoint_run_script.py deleted file mode 120000 index fcbdb0bb71..0000000000 --- a/plugins/modules/checkpoint_run_script.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_run_script.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_session.py b/plugins/modules/checkpoint_session.py deleted file mode 120000 index d70cb2373e..0000000000 --- a/plugins/modules/checkpoint_session.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_session.py \ No newline at end of file diff --git a/plugins/modules/checkpoint_task_facts.py b/plugins/modules/checkpoint_task_facts.py deleted file mode 120000 index 2524f306a5..0000000000 --- a/plugins/modules/checkpoint_task_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/checkpoint_task_facts.py \ No newline at end of file diff --git a/plugins/modules/cnos_backup.py b/plugins/modules/cnos_backup.py deleted file mode 120000 index 850557a743..0000000000 --- a/plugins/modules/cnos_backup.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_backup.py \ No newline at end of file diff --git a/plugins/modules/cnos_banner.py b/plugins/modules/cnos_banner.py deleted file mode 120000 index 6fff4900a2..0000000000 --- a/plugins/modules/cnos_banner.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_banner.py \ No newline at end of file diff --git a/plugins/modules/cnos_bgp.py b/plugins/modules/cnos_bgp.py deleted file mode 120000 index 416bb98821..0000000000 --- a/plugins/modules/cnos_bgp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_bgp.py \ No newline at end of file diff --git a/plugins/modules/cnos_command.py b/plugins/modules/cnos_command.py deleted file mode 120000 index e6829fcf50..0000000000 --- a/plugins/modules/cnos_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_command.py \ No newline at end of file diff --git a/plugins/modules/cnos_conditional_command.py b/plugins/modules/cnos_conditional_command.py deleted file mode 120000 index 91d637a535..0000000000 --- a/plugins/modules/cnos_conditional_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_conditional_command.py \ No newline at end of file diff --git a/plugins/modules/cnos_conditional_template.py b/plugins/modules/cnos_conditional_template.py deleted file mode 120000 index 76c8bebae7..0000000000 --- a/plugins/modules/cnos_conditional_template.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_conditional_template.py \ No newline at end of file diff --git a/plugins/modules/cnos_config.py b/plugins/modules/cnos_config.py deleted file mode 120000 index 3facf7f0bb..0000000000 --- a/plugins/modules/cnos_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_config.py \ No newline at end of file diff --git a/plugins/modules/cnos_factory.py b/plugins/modules/cnos_factory.py deleted file mode 120000 index a3f6d7eb87..0000000000 --- a/plugins/modules/cnos_factory.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_factory.py \ No newline at end of file diff --git a/plugins/modules/cnos_facts.py b/plugins/modules/cnos_facts.py deleted file mode 120000 index be01441995..0000000000 --- a/plugins/modules/cnos_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_facts.py \ No newline at end of file diff --git a/plugins/modules/cnos_image.py b/plugins/modules/cnos_image.py deleted file mode 120000 index c727c306dc..0000000000 --- a/plugins/modules/cnos_image.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_image.py \ No newline at end of file diff --git a/plugins/modules/cnos_interface.py b/plugins/modules/cnos_interface.py deleted file mode 120000 index d274ab5eb4..0000000000 --- a/plugins/modules/cnos_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_interface.py \ No newline at end of file diff --git a/plugins/modules/cnos_l2_interface.py b/plugins/modules/cnos_l2_interface.py deleted file mode 120000 index f7b879d6af..0000000000 --- a/plugins/modules/cnos_l2_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_l2_interface.py \ No newline at end of file diff --git a/plugins/modules/cnos_l3_interface.py b/plugins/modules/cnos_l3_interface.py deleted file mode 120000 index 285b66bcc5..0000000000 --- a/plugins/modules/cnos_l3_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_l3_interface.py \ No newline at end of file diff --git a/plugins/modules/cnos_linkagg.py b/plugins/modules/cnos_linkagg.py deleted file mode 120000 index f91e038187..0000000000 --- a/plugins/modules/cnos_linkagg.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_linkagg.py \ No newline at end of file diff --git a/plugins/modules/cnos_lldp.py b/plugins/modules/cnos_lldp.py deleted file mode 120000 index 03e13146ae..0000000000 --- a/plugins/modules/cnos_lldp.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_lldp.py \ No newline at end of file diff --git a/plugins/modules/cnos_logging.py b/plugins/modules/cnos_logging.py deleted file mode 120000 index b2abeb6547..0000000000 --- a/plugins/modules/cnos_logging.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_logging.py \ No newline at end of file diff --git a/plugins/modules/cnos_reload.py b/plugins/modules/cnos_reload.py deleted file mode 120000 index 06c7e8a303..0000000000 --- a/plugins/modules/cnos_reload.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_reload.py \ No newline at end of file diff --git a/plugins/modules/cnos_rollback.py b/plugins/modules/cnos_rollback.py deleted file mode 120000 index f5f6c11cec..0000000000 --- a/plugins/modules/cnos_rollback.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_rollback.py \ No newline at end of file diff --git a/plugins/modules/cnos_save.py b/plugins/modules/cnos_save.py deleted file mode 120000 index 8569b6cd29..0000000000 --- a/plugins/modules/cnos_save.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_save.py \ No newline at end of file diff --git a/plugins/modules/cnos_showrun.py b/plugins/modules/cnos_showrun.py deleted file mode 120000 index 52840a7a4d..0000000000 --- a/plugins/modules/cnos_showrun.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_showrun.py \ No newline at end of file diff --git a/plugins/modules/cnos_static_route.py b/plugins/modules/cnos_static_route.py deleted file mode 120000 index 7f6e25dd4f..0000000000 --- a/plugins/modules/cnos_static_route.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_static_route.py \ No newline at end of file diff --git a/plugins/modules/cnos_system.py b/plugins/modules/cnos_system.py deleted file mode 120000 index 634aa875c2..0000000000 --- a/plugins/modules/cnos_system.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_system.py \ No newline at end of file diff --git a/plugins/modules/cnos_template.py b/plugins/modules/cnos_template.py deleted file mode 120000 index 37bdf83e87..0000000000 --- a/plugins/modules/cnos_template.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_template.py \ No newline at end of file diff --git a/plugins/modules/cnos_user.py b/plugins/modules/cnos_user.py deleted file mode 120000 index adb93fbd84..0000000000 --- a/plugins/modules/cnos_user.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_user.py \ No newline at end of file diff --git a/plugins/modules/cnos_vlag.py b/plugins/modules/cnos_vlag.py deleted file mode 120000 index de9b337abf..0000000000 --- a/plugins/modules/cnos_vlag.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_vlag.py \ No newline at end of file diff --git a/plugins/modules/cnos_vlan.py b/plugins/modules/cnos_vlan.py deleted file mode 120000 index da4e32c373..0000000000 --- a/plugins/modules/cnos_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_vlan.py \ No newline at end of file diff --git a/plugins/modules/cnos_vrf.py b/plugins/modules/cnos_vrf.py deleted file mode 120000 index 6095d30754..0000000000 --- a/plugins/modules/cnos_vrf.py +++ /dev/null @@ -1 +0,0 @@ -./network/cnos/cnos_vrf.py \ No newline at end of file diff --git a/plugins/modules/cp_publish.py b/plugins/modules/cp_publish.py deleted file mode 120000 index 76f82d7c4a..0000000000 --- a/plugins/modules/cp_publish.py +++ /dev/null @@ -1 +0,0 @@ -./network/check_point/cp_publish.py \ No newline at end of file diff --git a/plugins/modules/cv_server_provision.py b/plugins/modules/cv_server_provision.py deleted file mode 120000 index cc5d1489eb..0000000000 --- a/plugins/modules/cv_server_provision.py +++ /dev/null @@ -1 +0,0 @@ -./network/cloudvision/cv_server_provision.py \ No newline at end of file diff --git a/plugins/modules/dladm_etherstub.py b/plugins/modules/dladm_etherstub.py deleted file mode 120000 index 85402173ed..0000000000 --- a/plugins/modules/dladm_etherstub.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/dladm_etherstub.py \ No newline at end of file diff --git a/plugins/modules/dladm_iptun.py b/plugins/modules/dladm_iptun.py deleted file mode 120000 index b05a8b25cc..0000000000 --- a/plugins/modules/dladm_iptun.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/dladm_iptun.py \ No newline at end of file diff --git a/plugins/modules/dladm_linkprop.py b/plugins/modules/dladm_linkprop.py deleted file mode 120000 index 2b39c6bd3d..0000000000 --- a/plugins/modules/dladm_linkprop.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/dladm_linkprop.py \ No newline at end of file diff --git a/plugins/modules/dladm_vlan.py b/plugins/modules/dladm_vlan.py deleted file mode 120000 index 35e1d8c010..0000000000 --- a/plugins/modules/dladm_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/dladm_vlan.py \ No newline at end of file diff --git a/plugins/modules/dladm_vnic.py b/plugins/modules/dladm_vnic.py deleted file mode 120000 index 4b23d2da54..0000000000 --- a/plugins/modules/dladm_vnic.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/dladm_vnic.py \ No newline at end of file diff --git a/plugins/modules/edgeos_command.py b/plugins/modules/edgeos_command.py deleted file mode 120000 index f6d5b9fc2e..0000000000 --- a/plugins/modules/edgeos_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/edgeos/edgeos_command.py \ No newline at end of file diff --git a/plugins/modules/edgeos_config.py b/plugins/modules/edgeos_config.py deleted file mode 120000 index cadeff13f4..0000000000 --- a/plugins/modules/edgeos_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/edgeos/edgeos_config.py \ No newline at end of file diff --git a/plugins/modules/edgeos_facts.py b/plugins/modules/edgeos_facts.py deleted file mode 120000 index 4f528ac81f..0000000000 --- a/plugins/modules/edgeos_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/edgeos/edgeos_facts.py \ No newline at end of file diff --git a/plugins/modules/edgeswitch_facts.py b/plugins/modules/edgeswitch_facts.py deleted file mode 120000 index 5c71aae2a6..0000000000 --- a/plugins/modules/edgeswitch_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/edgeswitch/edgeswitch_facts.py \ No newline at end of file diff --git a/plugins/modules/edgeswitch_vlan.py b/plugins/modules/edgeswitch_vlan.py deleted file mode 120000 index e4ac3b1fc2..0000000000 --- a/plugins/modules/edgeswitch_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/edgeswitch/edgeswitch_vlan.py \ No newline at end of file diff --git a/plugins/modules/enos_command.py b/plugins/modules/enos_command.py deleted file mode 120000 index 3d52c0215f..0000000000 --- a/plugins/modules/enos_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/enos/enos_command.py \ No newline at end of file diff --git a/plugins/modules/enos_config.py b/plugins/modules/enos_config.py deleted file mode 120000 index 46a93ca6ce..0000000000 --- a/plugins/modules/enos_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/enos/enos_config.py \ No newline at end of file diff --git a/plugins/modules/enos_facts.py b/plugins/modules/enos_facts.py deleted file mode 120000 index 82a7af9f35..0000000000 --- a/plugins/modules/enos_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/enos/enos_facts.py \ No newline at end of file diff --git a/plugins/modules/eric_eccli_command.py b/plugins/modules/eric_eccli_command.py deleted file mode 120000 index 2e7d85232f..0000000000 --- a/plugins/modules/eric_eccli_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/eric_eccli/eric_eccli_command.py \ No newline at end of file diff --git a/plugins/modules/exos_command.py b/plugins/modules/exos_command.py deleted file mode 120000 index 6d53af8204..0000000000 --- a/plugins/modules/exos_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/exos/exos_command.py \ No newline at end of file diff --git a/plugins/modules/exos_config.py b/plugins/modules/exos_config.py deleted file mode 120000 index cee202971f..0000000000 --- a/plugins/modules/exos_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/exos/exos_config.py \ No newline at end of file diff --git a/plugins/modules/exos_facts.py b/plugins/modules/exos_facts.py deleted file mode 120000 index e23ecab36c..0000000000 --- a/plugins/modules/exos_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/exos/exos_facts.py \ No newline at end of file diff --git a/plugins/modules/exos_l2_interfaces.py b/plugins/modules/exos_l2_interfaces.py deleted file mode 120000 index 01200d00b1..0000000000 --- a/plugins/modules/exos_l2_interfaces.py +++ /dev/null @@ -1 +0,0 @@ -./network/exos/exos_l2_interfaces.py \ No newline at end of file diff --git a/plugins/modules/exos_lldp_global.py b/plugins/modules/exos_lldp_global.py deleted file mode 120000 index 61d06a1c61..0000000000 --- a/plugins/modules/exos_lldp_global.py +++ /dev/null @@ -1 +0,0 @@ -./network/exos/exos_lldp_global.py \ No newline at end of file diff --git a/plugins/modules/exos_lldp_interfaces.py b/plugins/modules/exos_lldp_interfaces.py deleted file mode 120000 index 351bf1abfe..0000000000 --- a/plugins/modules/exos_lldp_interfaces.py +++ /dev/null @@ -1 +0,0 @@ -./network/exos/exos_lldp_interfaces.py \ No newline at end of file diff --git a/plugins/modules/exos_vlans.py b/plugins/modules/exos_vlans.py deleted file mode 120000 index 8d27b43e3d..0000000000 --- a/plugins/modules/exos_vlans.py +++ /dev/null @@ -1 +0,0 @@ -./network/exos/exos_vlans.py \ No newline at end of file diff --git a/plugins/modules/faz_device.py b/plugins/modules/faz_device.py deleted file mode 120000 index bcf5bb86d0..0000000000 --- a/plugins/modules/faz_device.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortianalyzer/faz_device.py \ No newline at end of file diff --git a/plugins/modules/flowadm.py b/plugins/modules/flowadm.py deleted file mode 120000 index c321926d00..0000000000 --- a/plugins/modules/flowadm.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/flowadm.py \ No newline at end of file diff --git a/plugins/modules/fmgr_device.py b/plugins/modules/fmgr_device.py deleted file mode 120000 index 99747877cf..0000000000 --- a/plugins/modules/fmgr_device.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_device.py \ No newline at end of file diff --git a/plugins/modules/fmgr_device_config.py b/plugins/modules/fmgr_device_config.py deleted file mode 120000 index 095b509314..0000000000 --- a/plugins/modules/fmgr_device_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_device_config.py \ No newline at end of file diff --git a/plugins/modules/fmgr_device_group.py b/plugins/modules/fmgr_device_group.py deleted file mode 120000 index d8399cf806..0000000000 --- a/plugins/modules/fmgr_device_group.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_device_group.py \ No newline at end of file diff --git a/plugins/modules/fmgr_device_provision_template.py b/plugins/modules/fmgr_device_provision_template.py deleted file mode 120000 index 26db1584fd..0000000000 --- a/plugins/modules/fmgr_device_provision_template.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_device_provision_template.py \ No newline at end of file diff --git a/plugins/modules/fmgr_fwobj_address.py b/plugins/modules/fmgr_fwobj_address.py deleted file mode 120000 index 6cfcfbfa69..0000000000 --- a/plugins/modules/fmgr_fwobj_address.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_fwobj_address.py \ No newline at end of file diff --git a/plugins/modules/fmgr_fwobj_ippool.py b/plugins/modules/fmgr_fwobj_ippool.py deleted file mode 120000 index fd554ea64c..0000000000 --- a/plugins/modules/fmgr_fwobj_ippool.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_fwobj_ippool.py \ No newline at end of file diff --git a/plugins/modules/fmgr_fwobj_ippool6.py b/plugins/modules/fmgr_fwobj_ippool6.py deleted file mode 120000 index 4768e64138..0000000000 --- a/plugins/modules/fmgr_fwobj_ippool6.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_fwobj_ippool6.py \ No newline at end of file diff --git a/plugins/modules/fmgr_fwobj_service.py b/plugins/modules/fmgr_fwobj_service.py deleted file mode 120000 index 574cf226e6..0000000000 --- a/plugins/modules/fmgr_fwobj_service.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_fwobj_service.py \ No newline at end of file diff --git a/plugins/modules/fmgr_fwobj_vip.py b/plugins/modules/fmgr_fwobj_vip.py deleted file mode 120000 index 65d517551d..0000000000 --- a/plugins/modules/fmgr_fwobj_vip.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_fwobj_vip.py \ No newline at end of file diff --git a/plugins/modules/fmgr_fwpol_ipv4.py b/plugins/modules/fmgr_fwpol_ipv4.py deleted file mode 120000 index 8e01ba4300..0000000000 --- a/plugins/modules/fmgr_fwpol_ipv4.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_fwpol_ipv4.py \ No newline at end of file diff --git a/plugins/modules/fmgr_fwpol_package.py b/plugins/modules/fmgr_fwpol_package.py deleted file mode 120000 index 5085bd7a6b..0000000000 --- a/plugins/modules/fmgr_fwpol_package.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_fwpol_package.py \ No newline at end of file diff --git a/plugins/modules/fmgr_ha.py b/plugins/modules/fmgr_ha.py deleted file mode 120000 index d4e2b3768f..0000000000 --- a/plugins/modules/fmgr_ha.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_ha.py \ No newline at end of file diff --git a/plugins/modules/fmgr_provisioning.py b/plugins/modules/fmgr_provisioning.py deleted file mode 120000 index 577aa80e81..0000000000 --- a/plugins/modules/fmgr_provisioning.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_provisioning.py \ No newline at end of file diff --git a/plugins/modules/fmgr_query.py b/plugins/modules/fmgr_query.py deleted file mode 120000 index b3118879fc..0000000000 --- a/plugins/modules/fmgr_query.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_query.py \ No newline at end of file diff --git a/plugins/modules/fmgr_script.py b/plugins/modules/fmgr_script.py deleted file mode 120000 index 9f97ec2d41..0000000000 --- a/plugins/modules/fmgr_script.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_script.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_appctrl.py b/plugins/modules/fmgr_secprof_appctrl.py deleted file mode 120000 index 0e811096ff..0000000000 --- a/plugins/modules/fmgr_secprof_appctrl.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_appctrl.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_av.py b/plugins/modules/fmgr_secprof_av.py deleted file mode 120000 index 1842fa6131..0000000000 --- a/plugins/modules/fmgr_secprof_av.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_av.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_dns.py b/plugins/modules/fmgr_secprof_dns.py deleted file mode 120000 index aec4d164b1..0000000000 --- a/plugins/modules/fmgr_secprof_dns.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_dns.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_ips.py b/plugins/modules/fmgr_secprof_ips.py deleted file mode 120000 index 22e3afc40e..0000000000 --- a/plugins/modules/fmgr_secprof_ips.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_ips.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_profile_group.py b/plugins/modules/fmgr_secprof_profile_group.py deleted file mode 120000 index feed2ad213..0000000000 --- a/plugins/modules/fmgr_secprof_profile_group.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_profile_group.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_proxy.py b/plugins/modules/fmgr_secprof_proxy.py deleted file mode 120000 index 20d7980772..0000000000 --- a/plugins/modules/fmgr_secprof_proxy.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_proxy.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_spam.py b/plugins/modules/fmgr_secprof_spam.py deleted file mode 120000 index d0c9b31fd6..0000000000 --- a/plugins/modules/fmgr_secprof_spam.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_spam.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_ssl_ssh.py b/plugins/modules/fmgr_secprof_ssl_ssh.py deleted file mode 120000 index 970622bb3e..0000000000 --- a/plugins/modules/fmgr_secprof_ssl_ssh.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_ssl_ssh.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_voip.py b/plugins/modules/fmgr_secprof_voip.py deleted file mode 120000 index a892763689..0000000000 --- a/plugins/modules/fmgr_secprof_voip.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_voip.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_waf.py b/plugins/modules/fmgr_secprof_waf.py deleted file mode 120000 index 5ae144ab19..0000000000 --- a/plugins/modules/fmgr_secprof_waf.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_waf.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_wanopt.py b/plugins/modules/fmgr_secprof_wanopt.py deleted file mode 120000 index 7cabea53bd..0000000000 --- a/plugins/modules/fmgr_secprof_wanopt.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_wanopt.py \ No newline at end of file diff --git a/plugins/modules/fmgr_secprof_web.py b/plugins/modules/fmgr_secprof_web.py deleted file mode 120000 index 98cc8ba334..0000000000 --- a/plugins/modules/fmgr_secprof_web.py +++ /dev/null @@ -1 +0,0 @@ -./network/fortimanager/fmgr_secprof_web.py \ No newline at end of file diff --git a/plugins/modules/ftd_configuration.py b/plugins/modules/ftd_configuration.py deleted file mode 120000 index ee1bf880ae..0000000000 --- a/plugins/modules/ftd_configuration.py +++ /dev/null @@ -1 +0,0 @@ -./network/ftd/ftd_configuration.py \ No newline at end of file diff --git a/plugins/modules/ftd_file_download.py b/plugins/modules/ftd_file_download.py deleted file mode 120000 index cb8cfbc4db..0000000000 --- a/plugins/modules/ftd_file_download.py +++ /dev/null @@ -1 +0,0 @@ -./network/ftd/ftd_file_download.py \ No newline at end of file diff --git a/plugins/modules/ftd_file_upload.py b/plugins/modules/ftd_file_upload.py deleted file mode 120000 index fcc8f9c49b..0000000000 --- a/plugins/modules/ftd_file_upload.py +++ /dev/null @@ -1 +0,0 @@ -./network/ftd/ftd_file_upload.py \ No newline at end of file diff --git a/plugins/modules/ftd_install.py b/plugins/modules/ftd_install.py deleted file mode 120000 index 16b3f65c1f..0000000000 --- a/plugins/modules/ftd_install.py +++ /dev/null @@ -1 +0,0 @@ -./network/ftd/ftd_install.py \ No newline at end of file diff --git a/plugins/modules/iap_start_workflow.py b/plugins/modules/iap_start_workflow.py deleted file mode 120000 index 0b7ce7400d..0000000000 --- a/plugins/modules/iap_start_workflow.py +++ /dev/null @@ -1 +0,0 @@ -./network/itential/iap_start_workflow.py \ No newline at end of file diff --git a/plugins/modules/iap_token.py b/plugins/modules/iap_token.py deleted file mode 120000 index f663909c8c..0000000000 --- a/plugins/modules/iap_token.py +++ /dev/null @@ -1 +0,0 @@ -./network/itential/iap_token.py \ No newline at end of file diff --git a/plugins/modules/icx_banner.py b/plugins/modules/icx_banner.py deleted file mode 120000 index d8637da47f..0000000000 --- a/plugins/modules/icx_banner.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_banner.py \ No newline at end of file diff --git a/plugins/modules/icx_command.py b/plugins/modules/icx_command.py deleted file mode 120000 index e19b5d2307..0000000000 --- a/plugins/modules/icx_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_command.py \ No newline at end of file diff --git a/plugins/modules/icx_config.py b/plugins/modules/icx_config.py deleted file mode 120000 index 061430c450..0000000000 --- a/plugins/modules/icx_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_config.py \ No newline at end of file diff --git a/plugins/modules/icx_copy.py b/plugins/modules/icx_copy.py deleted file mode 120000 index f484d082bf..0000000000 --- a/plugins/modules/icx_copy.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_copy.py \ No newline at end of file diff --git a/plugins/modules/icx_facts.py b/plugins/modules/icx_facts.py deleted file mode 120000 index 5042e91d10..0000000000 --- a/plugins/modules/icx_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_facts.py \ No newline at end of file diff --git a/plugins/modules/icx_interface.py b/plugins/modules/icx_interface.py deleted file mode 120000 index c02b08a081..0000000000 --- a/plugins/modules/icx_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_interface.py \ No newline at end of file diff --git a/plugins/modules/icx_l3_interface.py b/plugins/modules/icx_l3_interface.py deleted file mode 120000 index 764edf7115..0000000000 --- a/plugins/modules/icx_l3_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_l3_interface.py \ No newline at end of file diff --git a/plugins/modules/icx_linkagg.py b/plugins/modules/icx_linkagg.py deleted file mode 120000 index fb7b55abc5..0000000000 --- a/plugins/modules/icx_linkagg.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_linkagg.py \ No newline at end of file diff --git a/plugins/modules/icx_lldp.py b/plugins/modules/icx_lldp.py deleted file mode 120000 index 4a77ff98ec..0000000000 --- a/plugins/modules/icx_lldp.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_lldp.py \ No newline at end of file diff --git a/plugins/modules/icx_logging.py b/plugins/modules/icx_logging.py deleted file mode 120000 index 8e1ed7a40f..0000000000 --- a/plugins/modules/icx_logging.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_logging.py \ No newline at end of file diff --git a/plugins/modules/icx_ping.py b/plugins/modules/icx_ping.py deleted file mode 120000 index e1e937fe10..0000000000 --- a/plugins/modules/icx_ping.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_ping.py \ No newline at end of file diff --git a/plugins/modules/icx_static_route.py b/plugins/modules/icx_static_route.py deleted file mode 120000 index e90273faa3..0000000000 --- a/plugins/modules/icx_static_route.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_static_route.py \ No newline at end of file diff --git a/plugins/modules/icx_system.py b/plugins/modules/icx_system.py deleted file mode 120000 index db544241c3..0000000000 --- a/plugins/modules/icx_system.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_system.py \ No newline at end of file diff --git a/plugins/modules/icx_user.py b/plugins/modules/icx_user.py deleted file mode 120000 index d1d6a045df..0000000000 --- a/plugins/modules/icx_user.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_user.py \ No newline at end of file diff --git a/plugins/modules/icx_vlan.py b/plugins/modules/icx_vlan.py deleted file mode 120000 index 7203845cf7..0000000000 --- a/plugins/modules/icx_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/icx/icx_vlan.py \ No newline at end of file diff --git a/plugins/modules/ig_config.py b/plugins/modules/ig_config.py deleted file mode 120000 index b2caf88803..0000000000 --- a/plugins/modules/ig_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/ingate/ig_config.py \ No newline at end of file diff --git a/plugins/modules/ig_unit_information.py b/plugins/modules/ig_unit_information.py deleted file mode 120000 index e5262ddbba..0000000000 --- a/plugins/modules/ig_unit_information.py +++ /dev/null @@ -1 +0,0 @@ -./network/ingate/ig_unit_information.py \ No newline at end of file diff --git a/plugins/modules/ipadm_addr.py b/plugins/modules/ipadm_addr.py deleted file mode 120000 index eb0a46dfa1..0000000000 --- a/plugins/modules/ipadm_addr.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/ipadm_addr.py \ No newline at end of file diff --git a/plugins/modules/ipadm_addrprop.py b/plugins/modules/ipadm_addrprop.py deleted file mode 120000 index 5130510848..0000000000 --- a/plugins/modules/ipadm_addrprop.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/ipadm_addrprop.py \ No newline at end of file diff --git a/plugins/modules/ipadm_if.py b/plugins/modules/ipadm_if.py deleted file mode 120000 index 7a44831be0..0000000000 --- a/plugins/modules/ipadm_if.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/ipadm_if.py \ No newline at end of file diff --git a/plugins/modules/ipadm_ifprop.py b/plugins/modules/ipadm_ifprop.py deleted file mode 120000 index a9de50486a..0000000000 --- a/plugins/modules/ipadm_ifprop.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/ipadm_ifprop.py \ No newline at end of file diff --git a/plugins/modules/ipadm_prop.py b/plugins/modules/ipadm_prop.py deleted file mode 120000 index 6f0c221d96..0000000000 --- a/plugins/modules/ipadm_prop.py +++ /dev/null @@ -1 +0,0 @@ -./network/illumos/ipadm_prop.py \ No newline at end of file diff --git a/plugins/modules/ironware_command.py b/plugins/modules/ironware_command.py deleted file mode 120000 index 0cc3f7ee4d..0000000000 --- a/plugins/modules/ironware_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/ironware/ironware_command.py \ No newline at end of file diff --git a/plugins/modules/ironware_config.py b/plugins/modules/ironware_config.py deleted file mode 120000 index a23fdd2280..0000000000 --- a/plugins/modules/ironware_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/ironware/ironware_config.py \ No newline at end of file diff --git a/plugins/modules/ironware_facts.py b/plugins/modules/ironware_facts.py deleted file mode 120000 index b54c2463ac..0000000000 --- a/plugins/modules/ironware_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/ironware/ironware_facts.py \ No newline at end of file diff --git a/plugins/modules/mso_schema_template_external_epg_contract.py b/plugins/modules/mso_schema_template_external_epg_contract.py deleted file mode 120000 index 60e98e6362..0000000000 --- a/plugins/modules/mso_schema_template_external_epg_contract.py +++ /dev/null @@ -1 +0,0 @@ -./network/aci/mso_schema_template_external_epg_contract.py \ No newline at end of file diff --git a/plugins/modules/mso_schema_template_external_epg_subnet.py b/plugins/modules/mso_schema_template_external_epg_subnet.py deleted file mode 120000 index 24c75707e9..0000000000 --- a/plugins/modules/mso_schema_template_external_epg_subnet.py +++ /dev/null @@ -1 +0,0 @@ -./network/aci/mso_schema_template_external_epg_subnet.py \ No newline at end of file diff --git a/plugins/modules/nclu.py b/plugins/modules/nclu.py deleted file mode 120000 index 9fafd0fe60..0000000000 --- a/plugins/modules/nclu.py +++ /dev/null @@ -1 +0,0 @@ -./network/cumulus/nclu.py \ No newline at end of file diff --git a/plugins/modules/netact_cm_command.py b/plugins/modules/netact_cm_command.py deleted file mode 120000 index cbedd83419..0000000000 --- a/plugins/modules/netact_cm_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/netact/netact_cm_command.py \ No newline at end of file diff --git a/plugins/modules/netscaler_cs_action.py b/plugins/modules/netscaler_cs_action.py deleted file mode 120000 index 1676e45b32..0000000000 --- a/plugins/modules/netscaler_cs_action.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_cs_action.py \ No newline at end of file diff --git a/plugins/modules/netscaler_cs_policy.py b/plugins/modules/netscaler_cs_policy.py deleted file mode 120000 index 56c47a9373..0000000000 --- a/plugins/modules/netscaler_cs_policy.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_cs_policy.py \ No newline at end of file diff --git a/plugins/modules/netscaler_cs_vserver.py b/plugins/modules/netscaler_cs_vserver.py deleted file mode 120000 index 45faf87c70..0000000000 --- a/plugins/modules/netscaler_cs_vserver.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_cs_vserver.py \ No newline at end of file diff --git a/plugins/modules/netscaler_gslb_service.py b/plugins/modules/netscaler_gslb_service.py deleted file mode 120000 index c142a293f1..0000000000 --- a/plugins/modules/netscaler_gslb_service.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_gslb_service.py \ No newline at end of file diff --git a/plugins/modules/netscaler_gslb_site.py b/plugins/modules/netscaler_gslb_site.py deleted file mode 120000 index 1a94598704..0000000000 --- a/plugins/modules/netscaler_gslb_site.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_gslb_site.py \ No newline at end of file diff --git a/plugins/modules/netscaler_gslb_vserver.py b/plugins/modules/netscaler_gslb_vserver.py deleted file mode 120000 index 4860b78c39..0000000000 --- a/plugins/modules/netscaler_gslb_vserver.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_gslb_vserver.py \ No newline at end of file diff --git a/plugins/modules/netscaler_lb_monitor.py b/plugins/modules/netscaler_lb_monitor.py deleted file mode 120000 index 42eee0705a..0000000000 --- a/plugins/modules/netscaler_lb_monitor.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_lb_monitor.py \ No newline at end of file diff --git a/plugins/modules/netscaler_lb_vserver.py b/plugins/modules/netscaler_lb_vserver.py deleted file mode 120000 index 0d62bf97d0..0000000000 --- a/plugins/modules/netscaler_lb_vserver.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_lb_vserver.py \ No newline at end of file diff --git a/plugins/modules/netscaler_nitro_request.py b/plugins/modules/netscaler_nitro_request.py deleted file mode 120000 index 4090ee6a25..0000000000 --- a/plugins/modules/netscaler_nitro_request.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_nitro_request.py \ No newline at end of file diff --git a/plugins/modules/netscaler_save_config.py b/plugins/modules/netscaler_save_config.py deleted file mode 120000 index 2214b15643..0000000000 --- a/plugins/modules/netscaler_save_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_save_config.py \ No newline at end of file diff --git a/plugins/modules/netscaler_server.py b/plugins/modules/netscaler_server.py deleted file mode 120000 index eadb037852..0000000000 --- a/plugins/modules/netscaler_server.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_server.py \ No newline at end of file diff --git a/plugins/modules/netscaler_service.py b/plugins/modules/netscaler_service.py deleted file mode 120000 index 86f43d5b67..0000000000 --- a/plugins/modules/netscaler_service.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_service.py \ No newline at end of file diff --git a/plugins/modules/netscaler_servicegroup.py b/plugins/modules/netscaler_servicegroup.py deleted file mode 120000 index b1d45e4918..0000000000 --- a/plugins/modules/netscaler_servicegroup.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_servicegroup.py \ No newline at end of file diff --git a/plugins/modules/netscaler_ssl_certkey.py b/plugins/modules/netscaler_ssl_certkey.py deleted file mode 120000 index e0c5898048..0000000000 --- a/plugins/modules/netscaler_ssl_certkey.py +++ /dev/null @@ -1 +0,0 @@ -./network/netscaler/netscaler_ssl_certkey.py \ No newline at end of file diff --git a/plugins/modules/network/a10/a10_server.py b/plugins/modules/network/a10/a10_server.py deleted file mode 100644 index 3ffbadd66d..0000000000 --- a/plugins/modules/network/a10/a10_server.py +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2014, Mischa Peters , -# (c) 2016, Eric Chou -# -# 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: a10_server -short_description: Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' server object. -description: - - Manage SLB (Server Load Balancer) server objects on A10 Networks devices via aXAPIv2. -author: - - Eric Chou (@ericchou1) - - Mischa Peters (@mischapeters) -notes: - - Requires A10 Networks aXAPI 2.1. -extends_documentation_fragment: -- community.general.a10 -- url - -options: - partition: - description: - - set active-partition - server_name: - description: - - The SLB (Server Load Balancer) server name. - required: true - aliases: ['server'] - server_ip: - description: - - The SLB server IPv4 address. - aliases: ['ip', 'address'] - server_status: - description: - - The SLB virtual server status. - default: enabled - aliases: ['status'] - choices: ['enabled', 'disabled'] - server_ports: - description: - - A list of ports to create for the server. Each list item should be a - dictionary which specifies the C(port:) and C(protocol:), but can also optionally - specify the C(status:). See the examples below for details. This parameter is - required when C(state) is C(present). - aliases: ['port'] - state: - description: - - This is to specify the operation to create, update or remove SLB server. - default: present - choices: ['present', 'absent'] - validate_certs: - description: - - If C(no), SSL certificates will not be validated. This should only be used - on personally controlled devices using self-signed certificates. - type: bool - default: 'yes' - -''' - -EXAMPLES = ''' -# Create a new server -- a10_server: - host: a10.mydomain.com - username: myadmin - password: mypassword - partition: mypartition - server: test - server_ip: 1.1.1.100 - server_ports: - - port_num: 8080 - protocol: tcp - - port_num: 8443 - protocol: TCP - -''' - -RETURN = ''' -content: - description: the full info regarding the slb_server - returned: success - type: str - sample: "mynewserver" -''' -import json - -from ansible_collections.community.general.plugins.module_utils.network.a10.a10 import (axapi_call, a10_argument_spec, axapi_authenticate, axapi_failure, - axapi_get_port_protocol, axapi_enabled_disabled, AXAPI_PORT_PROTOCOLS) -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import url_argument_spec - - -VALID_PORT_FIELDS = ['port_num', 'protocol', 'status'] - - -def validate_ports(module, ports): - for item in ports: - for key in item: - if key not in VALID_PORT_FIELDS: - module.fail_json(msg="invalid port field (%s), must be one of: %s" % (key, ','.join(VALID_PORT_FIELDS))) - - # validate the port number is present and an integer - if 'port_num' in item: - try: - item['port_num'] = int(item['port_num']) - except Exception: - module.fail_json(msg="port_num entries in the port definitions must be integers") - else: - module.fail_json(msg="port definitions must define the port_num field") - - # validate the port protocol is present, and convert it to - # the internal API integer value (and validate it) - if 'protocol' in item: - protocol = axapi_get_port_protocol(item['protocol']) - if not protocol: - module.fail_json(msg="invalid port protocol, must be one of: %s" % ','.join(AXAPI_PORT_PROTOCOLS)) - else: - item['protocol'] = protocol - else: - module.fail_json(msg="port definitions must define the port protocol (%s)" % ','.join(AXAPI_PORT_PROTOCOLS)) - - # convert the status to the internal API integer value - if 'status' in item: - item['status'] = axapi_enabled_disabled(item['status']) - else: - item['status'] = 1 - - -def main(): - argument_spec = a10_argument_spec() - argument_spec.update(url_argument_spec()) - argument_spec.update( - dict( - state=dict(type='str', default='present', choices=['present', 'absent']), - server_name=dict(type='str', aliases=['server'], required=True), - server_ip=dict(type='str', aliases=['ip', 'address']), - server_status=dict(type='str', default='enabled', aliases=['status'], choices=['enabled', 'disabled']), - server_ports=dict(type='list', aliases=['port'], default=[]), - partition=dict(type='str', default=[]), - ) - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=False - ) - - host = module.params['host'] - partition = module.params['partition'] - username = module.params['username'] - password = module.params['password'] - state = module.params['state'] - write_config = module.params['write_config'] - slb_server = module.params['server_name'] - slb_server_ip = module.params['server_ip'] - slb_server_status = module.params['server_status'] - slb_server_ports = module.params['server_ports'] - - if slb_server is None: - module.fail_json(msg='server_name is required') - - axapi_base_url = 'https://%s/services/rest/V2.1/?format=json' % host - session_url = axapi_authenticate(module, axapi_base_url, username, password) - - # validate the ports data structure - validate_ports(module, slb_server_ports) - - json_post = { - 'server': { - 'name': slb_server, - } - } - - # add optional module parameters - if slb_server_ip: - json_post['server']['host'] = slb_server_ip - - if slb_server_ports: - json_post['server']['port_list'] = slb_server_ports - - if slb_server_status: - json_post['server']['status'] = axapi_enabled_disabled(slb_server_status) - - axapi_call(module, session_url + '&method=system.partition.active', json.dumps({'name': partition})) - - slb_server_data = axapi_call(module, session_url + '&method=slb.server.search', json.dumps({'name': slb_server})) - slb_server_exists = not axapi_failure(slb_server_data) - - changed = False - if state == 'present': - if not slb_server_exists: - if not slb_server_ip: - module.fail_json(msg='you must specify an IP address when creating a server') - - result = axapi_call(module, session_url + '&method=slb.server.create', json.dumps(json_post)) - if axapi_failure(result): - module.fail_json(msg="failed to create the server: %s" % result['response']['err']['msg']) - changed = True - else: - def port_needs_update(src_ports, dst_ports): - ''' - Checks to determine if the port definitions of the src_ports - array are in or different from those in dst_ports. If there is - a difference, this function returns true, otherwise false. - ''' - for src_port in src_ports: - found = False - different = False - for dst_port in dst_ports: - if src_port['port_num'] == dst_port['port_num']: - found = True - for valid_field in VALID_PORT_FIELDS: - if src_port[valid_field] != dst_port[valid_field]: - different = True - break - if found or different: - break - if not found or different: - return True - # every port from the src exists in the dst, and none of them were different - return False - - def status_needs_update(current_status, new_status): - ''' - Check to determine if we want to change the status of a server. - If there is a difference between the current status of the server and - the desired status, return true, otherwise false. - ''' - if current_status != new_status: - return True - return False - - defined_ports = slb_server_data.get('server', {}).get('port_list', []) - current_status = slb_server_data.get('server', {}).get('status') - - # we check for a needed update several ways - # - in case ports are missing from the ones specified by the user - # - in case ports are missing from those on the device - # - in case we are change the status of a server - if (port_needs_update(defined_ports, slb_server_ports) or - port_needs_update(slb_server_ports, defined_ports) or - status_needs_update(current_status, axapi_enabled_disabled(slb_server_status))): - result = axapi_call(module, session_url + '&method=slb.server.update', json.dumps(json_post)) - if axapi_failure(result): - module.fail_json(msg="failed to update the server: %s" % result['response']['err']['msg']) - changed = True - - # if we changed things, get the full info regarding - # the service group for the return data below - if changed: - result = axapi_call(module, session_url + '&method=slb.server.search', json.dumps({'name': slb_server})) - else: - result = slb_server_data - elif state == 'absent': - if slb_server_exists: - result = axapi_call(module, session_url + '&method=slb.server.delete', json.dumps({'name': slb_server})) - changed = True - else: - result = dict(msg="the server was not present") - - # if the config has changed, save the config unless otherwise requested - if changed and write_config: - write_result = axapi_call(module, session_url + '&method=system.action.write_memory') - if axapi_failure(write_result): - module.fail_json(msg="failed to save the configuration: %s" % write_result['response']['err']['msg']) - - # log out of the session nicely and exit - axapi_call(module, session_url + '&method=session.close') - module.exit_json(changed=changed, content=result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/a10/a10_server_axapi3.py b/plugins/modules/network/a10/a10_server_axapi3.py deleted file mode 100644 index cf2eae0614..0000000000 --- a/plugins/modules/network/a10/a10_server_axapi3.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2014, Mischa Peters -# Copyright: (c) 2016, Eric Chou -# 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: a10_server_axapi3 -short_description: Manage A10 Networks AX/SoftAX/Thunder/vThunder devices -description: - - Manage SLB (Server Load Balancer) server objects on A10 Networks devices via aXAPIv3. -author: - - Eric Chou (@ericchou1) -extends_documentation_fragment: -- community.general.a10 -- url - -options: - server_name: - description: - - The SLB (Server Load Balancer) server name. - required: true - aliases: ['server'] - server_ip: - description: - - The SLB (Server Load Balancer) server IPv4 address. - required: true - aliases: ['ip', 'address'] - server_status: - description: - - The SLB (Server Load Balancer) virtual server status. - default: enable - aliases: ['action'] - choices: ['enable', 'disable'] - server_ports: - description: - - A list of ports to create for the server. Each list item should be a dictionary which specifies the C(port:) - and C(protocol:). - aliases: ['port'] - operation: - description: - - Create, Update or Remove SLB server. For create and update operation, we use the IP address and server - name specified in the POST message. For delete operation, we use the server name in the request URI. - default: create - choices: ['create', 'update', 'remove'] - validate_certs: - description: - - If C(no), SSL certificates will not be validated. This should only be used - on personally controlled devices using self-signed certificates. - type: bool - default: 'yes' - -''' - -RETURN = ''' -# -''' - -EXAMPLES = ''' -# Create a new server -- a10_server: - host: a10.mydomain.com - username: myadmin - password: mypassword - server: test - server_ip: 1.1.1.100 - validate_certs: false - server_status: enable - write_config: yes - operation: create - server_ports: - - port-number: 8080 - protocol: tcp - action: enable - - port-number: 8443 - protocol: TCP - -''' -import json - -from ansible_collections.community.general.plugins.module_utils.network.a10.a10 import axapi_call_v3, a10_argument_spec, axapi_authenticate_v3, axapi_failure -from ansible_collections.community.general.plugins.module_utils.network.a10.a10 import AXAPI_PORT_PROTOCOLS -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import url_argument_spec - - -VALID_PORT_FIELDS = ['port-number', 'protocol', 'action'] - - -def validate_ports(module, ports): - for item in ports: - for key in item: - if key not in VALID_PORT_FIELDS: - module.fail_json(msg="invalid port field (%s), must be one of: %s" % (key, ','.join(VALID_PORT_FIELDS))) - - # validate the port number is present and an integer - if 'port-number' in item: - try: - item['port-number'] = int(item['port-number']) - except Exception: - module.fail_json(msg="port-number entries in the port definitions must be integers") - else: - module.fail_json(msg="port definitions must define the port-number field") - - # validate the port protocol is present, no need to convert to the internal API integer value in v3 - if 'protocol' in item: - protocol = item['protocol'] - if not protocol: - module.fail_json(msg="invalid port protocol, must be one of: %s" % ','.join(AXAPI_PORT_PROTOCOLS)) - else: - item['protocol'] = protocol - else: - module.fail_json(msg="port definitions must define the port protocol (%s)" % ','.join(AXAPI_PORT_PROTOCOLS)) - - # 'status' is 'action' in AXAPIv3 - # no need to convert the status, a.k.a action, to the internal API integer value in v3 - # action is either enabled or disabled - if 'action' in item: - action = item['action'] - if action not in ['enable', 'disable']: - module.fail_json(msg="server action must be enable or disable") - else: - item['action'] = 'enable' - - -def main(): - argument_spec = a10_argument_spec() - argument_spec.update(url_argument_spec()) - argument_spec.update( - dict( - operation=dict(type='str', default='create', choices=['create', 'update', 'delete']), - server_name=dict(type='str', aliases=['server'], required=True), - server_ip=dict(type='str', aliases=['ip', 'address'], required=True), - server_status=dict(type='str', default='enable', aliases=['action'], choices=['enable', 'disable']), - server_ports=dict(type='list', aliases=['port'], default=[]), - ) - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=False - ) - - host = module.params['host'] - username = module.params['username'] - password = module.params['password'] - operation = module.params['operation'] - write_config = module.params['write_config'] - slb_server = module.params['server_name'] - slb_server_ip = module.params['server_ip'] - slb_server_status = module.params['server_status'] - slb_server_ports = module.params['server_ports'] - - axapi_base_url = 'https://{0}/axapi/v3/'.format(host) - axapi_auth_url = axapi_base_url + 'auth/' - signature = axapi_authenticate_v3(module, axapi_auth_url, username, password) - - # validate the ports data structure - validate_ports(module, slb_server_ports) - - json_post = { - "server-list": [ - { - "name": slb_server, - "host": slb_server_ip - } - ] - } - - # add optional module parameters - if slb_server_ports: - json_post['server-list'][0]['port-list'] = slb_server_ports - - if slb_server_status: - json_post['server-list'][0]['action'] = slb_server_status - - slb_server_data = axapi_call_v3(module, axapi_base_url + 'slb/server/', method='GET', body='', signature=signature) - - # for empty slb server list - if axapi_failure(slb_server_data): - slb_server_exists = False - else: - slb_server_list = [server['name'] for server in slb_server_data['server-list']] - if slb_server in slb_server_list: - slb_server_exists = True - else: - slb_server_exists = False - - changed = False - if operation == 'create': - if slb_server_exists is False: - result = axapi_call_v3(module, axapi_base_url + 'slb/server/', method='POST', body=json.dumps(json_post), signature=signature) - if axapi_failure(result): - module.fail_json(msg="failed to create the server: %s" % result['response']['err']['msg']) - changed = True - else: - module.fail_json(msg="server already exists, use state='update' instead") - changed = False - # if we changed things, get the full info regarding result - if changed: - result = axapi_call_v3(module, axapi_base_url + 'slb/server/' + slb_server, method='GET', body='', signature=signature) - else: - result = slb_server_data - elif operation == 'delete': - if slb_server_exists: - result = axapi_call_v3(module, axapi_base_url + 'slb/server/' + slb_server, method='DELETE', body='', signature=signature) - if axapi_failure(result): - module.fail_json(msg="failed to delete server: %s" % result['response']['err']['msg']) - changed = True - else: - result = dict(msg="the server was not present") - elif operation == 'update': - if slb_server_exists: - result = axapi_call_v3(module, axapi_base_url + 'slb/server/', method='PUT', body=json.dumps(json_post), signature=signature) - if axapi_failure(result): - module.fail_json(msg="failed to update server: %s" % result['response']['err']['msg']) - changed = True - else: - result = dict(msg="the server was not present") - - # if the config has changed, save the config unless otherwise requested - if changed and write_config: - write_result = axapi_call_v3(module, axapi_base_url + 'write/memory/', method='POST', body='', signature=signature) - if axapi_failure(write_result): - module.fail_json(msg="failed to save the configuration: %s" % write_result['response']['err']['msg']) - - # log out gracefully and exit - axapi_call_v3(module, axapi_base_url + 'logoff/', method='POST', body='', signature=signature) - module.exit_json(changed=changed, content=result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/a10/a10_service_group.py b/plugins/modules/network/a10/a10_service_group.py deleted file mode 100644 index cc05e03093..0000000000 --- a/plugins/modules/network/a10/a10_service_group.py +++ /dev/null @@ -1,338 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2014, Mischa Peters , -# Eric Chou -# -# 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: a10_service_group -short_description: Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' service groups. -description: - - Manage SLB (Server Load Balancing) service-group objects on A10 Networks devices via aXAPIv2. -author: - - Eric Chou (@ericchou1) - - Mischa Peters (@mischapeters) -notes: - - Requires A10 Networks aXAPI 2.1. - - When a server doesn't exist and is added to the service-group the server will be created. -extends_documentation_fragment: -- community.general.a10 -- url - -options: - state: - description: - - If the specified service group should exists. - default: present - choices: ['present', 'absent'] - partition: - description: - - set active-partition - service_group: - description: - - The SLB (Server Load Balancing) service-group name - required: true - aliases: ['service', 'pool', 'group'] - service_group_protocol: - description: - - The SLB service-group protocol of TCP or UDP. - default: tcp - aliases: ['proto', 'protocol'] - choices: ['tcp', 'udp'] - service_group_method: - description: - - The SLB service-group load balancing method, such as round-robin or weighted-rr. - default: round-robin - aliases: ['method'] - choices: - - 'round-robin' - - 'weighted-rr' - - 'least-connection' - - 'weighted-least-connection' - - 'service-least-connection' - - 'service-weighted-least-connection' - - 'fastest-response' - - 'least-request' - - 'round-robin-strict' - - 'src-ip-only-hash' - - 'src-ip-hash' - servers: - description: - - A list of servers to add to the service group. Each list item should be a - dictionary which specifies the C(server:) and C(port:), but can also optionally - specify the C(status:). See the examples below for details. - aliases: ['server', 'member'] - validate_certs: - description: - - If C(no), SSL certificates will not be validated. This should only be used - on personally controlled devices using self-signed certificates. - type: bool - default: 'yes' - -''' - -EXAMPLES = ''' -# Create a new service-group -- a10_service_group: - host: a10.mydomain.com - username: myadmin - password: mypassword - partition: mypartition - service_group: sg-80-tcp - servers: - - server: foo1.mydomain.com - port: 8080 - - server: foo2.mydomain.com - port: 8080 - - server: foo3.mydomain.com - port: 8080 - - server: foo4.mydomain.com - port: 8080 - status: disabled - -''' - -RETURN = ''' -content: - description: the full info regarding the slb_service_group - returned: success - type: str - sample: "mynewservicegroup" -''' -import json - -from ansible_collections.community.general.plugins.module_utils.network.a10.a10 import (axapi_call, a10_argument_spec, axapi_authenticate, - axapi_failure, axapi_enabled_disabled) -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import url_argument_spec - - -VALID_SERVICE_GROUP_FIELDS = ['name', 'protocol', 'lb_method'] -VALID_SERVER_FIELDS = ['server', 'port', 'status'] - - -def validate_servers(module, servers): - for item in servers: - for key in item: - if key not in VALID_SERVER_FIELDS: - module.fail_json(msg="invalid server field (%s), must be one of: %s" % (key, ','.join(VALID_SERVER_FIELDS))) - - # validate the server name is present - if 'server' not in item: - module.fail_json(msg="server definitions must define the server field") - - # validate the port number is present and an integer - if 'port' in item: - try: - item['port'] = int(item['port']) - except Exception: - module.fail_json(msg="server port definitions must be integers") - else: - module.fail_json(msg="server definitions must define the port field") - - # convert the status to the internal API integer value - if 'status' in item: - item['status'] = axapi_enabled_disabled(item['status']) - else: - item['status'] = 1 - - -def main(): - argument_spec = a10_argument_spec() - argument_spec.update(url_argument_spec()) - argument_spec.update( - dict( - state=dict(type='str', default='present', choices=['present', 'absent']), - service_group=dict(type='str', aliases=['service', 'pool', 'group'], required=True), - service_group_protocol=dict(type='str', default='tcp', aliases=['proto', 'protocol'], choices=['tcp', 'udp']), - service_group_method=dict(type='str', default='round-robin', - aliases=['method'], - choices=['round-robin', - 'weighted-rr', - 'least-connection', - 'weighted-least-connection', - 'service-least-connection', - 'service-weighted-least-connection', - 'fastest-response', - 'least-request', - 'round-robin-strict', - 'src-ip-only-hash', - 'src-ip-hash']), - servers=dict(type='list', aliases=['server', 'member'], default=[]), - partition=dict(type='str', default=[]), - ) - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=False - ) - - host = module.params['host'] - username = module.params['username'] - password = module.params['password'] - partition = module.params['partition'] - state = module.params['state'] - write_config = module.params['write_config'] - slb_service_group = module.params['service_group'] - slb_service_group_proto = module.params['service_group_protocol'] - slb_service_group_method = module.params['service_group_method'] - slb_servers = module.params['servers'] - - if slb_service_group is None: - module.fail_json(msg='service_group is required') - - axapi_base_url = 'https://' + host + '/services/rest/V2.1/?format=json' - load_balancing_methods = {'round-robin': 0, - 'weighted-rr': 1, - 'least-connection': 2, - 'weighted-least-connection': 3, - 'service-least-connection': 4, - 'service-weighted-least-connection': 5, - 'fastest-response': 6, - 'least-request': 7, - 'round-robin-strict': 8, - 'src-ip-only-hash': 14, - 'src-ip-hash': 15} - - if not slb_service_group_proto or slb_service_group_proto.lower() == 'tcp': - protocol = 2 - else: - protocol = 3 - - # validate the server data list structure - validate_servers(module, slb_servers) - - json_post = { - 'service_group': { - 'name': slb_service_group, - 'protocol': protocol, - 'lb_method': load_balancing_methods[slb_service_group_method], - } - } - - # first we authenticate to get a session id - session_url = axapi_authenticate(module, axapi_base_url, username, password) - # then we select the active-partition - axapi_call(module, session_url + '&method=system.partition.active', json.dumps({'name': partition})) - # then we check to see if the specified group exists - slb_result = axapi_call(module, session_url + '&method=slb.service_group.search', json.dumps({'name': slb_service_group})) - slb_service_group_exist = not axapi_failure(slb_result) - - changed = False - if state == 'present': - # before creating/updating we need to validate that servers - # defined in the servers list exist to prevent errors - checked_servers = [] - for server in slb_servers: - result = axapi_call(module, session_url + '&method=slb.server.search', json.dumps({'name': server['server']})) - if axapi_failure(result): - module.fail_json(msg="the server %s specified in the servers list does not exist" % server['server']) - checked_servers.append(server['server']) - - if not slb_service_group_exist: - result = axapi_call(module, session_url + '&method=slb.service_group.create', json.dumps(json_post)) - if axapi_failure(result): - module.fail_json(msg=result['response']['err']['msg']) - changed = True - else: - # check to see if the service group definition without the - # server members is different, and update that individually - # if it needs it - do_update = False - for field in VALID_SERVICE_GROUP_FIELDS: - if json_post['service_group'][field] != slb_result['service_group'][field]: - do_update = True - break - - if do_update: - result = axapi_call(module, session_url + '&method=slb.service_group.update', json.dumps(json_post)) - if axapi_failure(result): - module.fail_json(msg=result['response']['err']['msg']) - changed = True - - # next we pull the defined list of servers out of the returned - # results to make it a bit easier to iterate over - defined_servers = slb_result.get('service_group', {}).get('member_list', []) - - # next we add/update new member servers from the user-specified - # list if they're different or not on the target device - for server in slb_servers: - found = False - different = False - for def_server in defined_servers: - if server['server'] == def_server['server']: - found = True - for valid_field in VALID_SERVER_FIELDS: - if server[valid_field] != def_server[valid_field]: - different = True - break - if found or different: - break - # add or update as required - server_data = { - "name": slb_service_group, - "member": server, - } - if not found: - result = axapi_call(module, session_url + '&method=slb.service_group.member.create', json.dumps(server_data)) - changed = True - elif different: - result = axapi_call(module, session_url + '&method=slb.service_group.member.update', json.dumps(server_data)) - changed = True - - # finally, remove any servers that are on the target - # device but were not specified in the list given - for server in defined_servers: - found = False - for slb_server in slb_servers: - if server['server'] == slb_server['server']: - found = True - break - # remove if not found - server_data = { - "name": slb_service_group, - "member": server, - } - if not found: - result = axapi_call(module, session_url + '&method=slb.service_group.member.delete', json.dumps(server_data)) - changed = True - - # if we changed things, get the full info regarding - # the service group for the return data below - if changed: - result = axapi_call(module, session_url + '&method=slb.service_group.search', json.dumps({'name': slb_service_group})) - else: - result = slb_result - elif state == 'absent': - if slb_service_group_exist: - result = axapi_call(module, session_url + '&method=slb.service_group.delete', json.dumps({'name': slb_service_group})) - changed = True - else: - result = dict(msg="the service group was not present") - - # if the config has changed, save the config unless otherwise requested - if changed and write_config: - write_result = axapi_call(module, session_url + '&method=system.action.write_memory') - if axapi_failure(write_result): - module.fail_json(msg="failed to save the configuration: %s" % write_result['response']['err']['msg']) - - # log out of the session nicely and exit - axapi_call(module, session_url + '&method=session.close') - module.exit_json(changed=changed, content=result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/a10/a10_virtual_server.py b/plugins/modules/network/a10/a10_virtual_server.py deleted file mode 100644 index 8f61788f04..0000000000 --- a/plugins/modules/network/a10/a10_virtual_server.py +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2014, Mischa Peters , -# Eric Chou -# -# 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: a10_virtual_server -short_description: Manage A10 Networks AX/SoftAX/Thunder/vThunder devices' virtual servers. -description: - - Manage SLB (Server Load Balancing) virtual server objects on A10 Networks devices via aXAPIv2. -author: - - Eric Chou (@ericchou1) - - Mischa Peters (@mischapeters) -notes: - - Requires A10 Networks aXAPI 2.1. -extends_documentation_fragment: -- community.general.a10 -- url - -options: - state: - description: - - If the specified virtual server should exist. - choices: ['present', 'absent'] - default: present - partition: - description: - - set active-partition - virtual_server: - description: - - The SLB (Server Load Balancing) virtual server name. - required: true - aliases: ['vip', 'virtual'] - virtual_server_ip: - description: - - The SLB virtual server IPv4 address. - aliases: ['ip', 'address'] - virtual_server_status: - description: - - The SLB virtual server status, such as enabled or disabled. - default: enable - aliases: ['status'] - choices: ['enabled', 'disabled'] - virtual_server_ports: - description: - - A list of ports to create for the virtual server. Each list item should be a - dictionary which specifies the C(port:) and C(type:), but can also optionally - specify the C(service_group:) as well as the C(status:). See the examples - below for details. This parameter is required when C(state) is C(present). - validate_certs: - description: - - If C(no), SSL certificates will not be validated. This should only be used - on personally controlled devices using self-signed certificates. - type: bool - default: 'yes' - -''' - - -EXAMPLES = ''' -# Create a new virtual server -- a10_virtual_server: - host: a10.mydomain.com - username: myadmin - password: mypassword - partition: mypartition - virtual_server: vserver1 - virtual_server_ip: 1.1.1.1 - virtual_server_ports: - - port: 80 - protocol: TCP - service_group: sg-80-tcp - - port: 443 - protocol: HTTPS - service_group: sg-443-https - - port: 8080 - protocol: http - status: disabled - -''' - -RETURN = ''' -content: - description: the full info regarding the slb_virtual - returned: success - type: str - sample: "mynewvirtualserver" -''' -import json - -from ansible_collections.community.general.plugins.module_utils.network.a10.a10 import (axapi_call, a10_argument_spec, axapi_authenticate, axapi_failure, - axapi_enabled_disabled, axapi_get_vport_protocol, - AXAPI_VPORT_PROTOCOLS) -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import url_argument_spec - - -VALID_PORT_FIELDS = ['port', 'protocol', 'service_group', 'status'] - - -def validate_ports(module, ports): - for item in ports: - for key in item: - if key not in VALID_PORT_FIELDS: - module.fail_json(msg="invalid port field (%s), must be one of: %s" % (key, ','.join(VALID_PORT_FIELDS))) - - # validate the port number is present and an integer - if 'port' in item: - try: - item['port'] = int(item['port']) - except Exception: - module.fail_json(msg="port definitions must be integers") - else: - module.fail_json(msg="port definitions must define the port field") - - # validate the port protocol is present, and convert it to - # the internal API integer value (and validate it) - if 'protocol' in item: - protocol = axapi_get_vport_protocol(item['protocol']) - if not protocol: - module.fail_json(msg="invalid port protocol, must be one of: %s" % ','.join(AXAPI_VPORT_PROTOCOLS)) - else: - item['protocol'] = protocol - else: - module.fail_json(msg="port definitions must define the port protocol (%s)" % ','.join(AXAPI_VPORT_PROTOCOLS)) - - # convert the status to the internal API integer value - if 'status' in item: - item['status'] = axapi_enabled_disabled(item['status']) - else: - item['status'] = 1 - - # ensure the service_group field is at least present - if 'service_group' not in item: - item['service_group'] = '' - - -def main(): - argument_spec = a10_argument_spec() - argument_spec.update(url_argument_spec()) - argument_spec.update( - dict( - state=dict(type='str', default='present', choices=['present', 'absent']), - virtual_server=dict(type='str', aliases=['vip', 'virtual'], required=True), - virtual_server_ip=dict(type='str', aliases=['ip', 'address'], required=True), - virtual_server_status=dict(type='str', default='enabled', aliases=['status'], choices=['enabled', 'disabled']), - virtual_server_ports=dict(type='list', required=True), - partition=dict(type='str', default=[]), - ) - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=False - ) - - host = module.params['host'] - username = module.params['username'] - password = module.params['password'] - partition = module.params['partition'] - state = module.params['state'] - write_config = module.params['write_config'] - slb_virtual = module.params['virtual_server'] - slb_virtual_ip = module.params['virtual_server_ip'] - slb_virtual_status = module.params['virtual_server_status'] - slb_virtual_ports = module.params['virtual_server_ports'] - - if slb_virtual is None: - module.fail_json(msg='virtual_server is required') - - validate_ports(module, slb_virtual_ports) - - axapi_base_url = 'https://%s/services/rest/V2.1/?format=json' % host - session_url = axapi_authenticate(module, axapi_base_url, username, password) - - axapi_call(module, session_url + '&method=system.partition.active', json.dumps({'name': partition})) - slb_virtual_data = axapi_call(module, session_url + '&method=slb.virtual_server.search', json.dumps({'name': slb_virtual})) - slb_virtual_exists = not axapi_failure(slb_virtual_data) - - changed = False - if state == 'present': - json_post = { - 'virtual_server': { - 'name': slb_virtual, - 'address': slb_virtual_ip, - 'status': axapi_enabled_disabled(slb_virtual_status), - 'vport_list': slb_virtual_ports, - } - } - - # before creating/updating we need to validate that any - # service groups defined in the ports list exist since - # since the API will still create port definitions for - # them while indicating a failure occurred - checked_service_groups = [] - for port in slb_virtual_ports: - if 'service_group' in port and port['service_group'] not in checked_service_groups: - # skip blank service group entries - if port['service_group'] == '': - continue - result = axapi_call(module, session_url + '&method=slb.service_group.search', json.dumps({'name': port['service_group']})) - if axapi_failure(result): - module.fail_json(msg="the service group %s specified in the ports list does not exist" % port['service_group']) - checked_service_groups.append(port['service_group']) - - if not slb_virtual_exists: - result = axapi_call(module, session_url + '&method=slb.virtual_server.create', json.dumps(json_post)) - if axapi_failure(result): - module.fail_json(msg="failed to create the virtual server: %s" % result['response']['err']['msg']) - changed = True - else: - def needs_update(src_ports, dst_ports): - ''' - Checks to determine if the port definitions of the src_ports - array are in or different from those in dst_ports. If there is - a difference, this function returns true, otherwise false. - ''' - for src_port in src_ports: - found = False - different = False - for dst_port in dst_ports: - if src_port['port'] == dst_port['port']: - found = True - for valid_field in VALID_PORT_FIELDS: - if src_port[valid_field] != dst_port[valid_field]: - different = True - break - if found or different: - break - if not found or different: - return True - # every port from the src exists in the dst, and none of them were different - return False - - defined_ports = slb_virtual_data.get('virtual_server', {}).get('vport_list', []) - - # we check for a needed update both ways, in case ports - # are missing from either the ones specified by the user - # or from those on the device - if needs_update(defined_ports, slb_virtual_ports) or needs_update(slb_virtual_ports, defined_ports): - result = axapi_call(module, session_url + '&method=slb.virtual_server.update', json.dumps(json_post)) - if axapi_failure(result): - module.fail_json(msg="failed to create the virtual server: %s" % result['response']['err']['msg']) - changed = True - - # if we changed things, get the full info regarding - # the service group for the return data below - if changed: - result = axapi_call(module, session_url + '&method=slb.virtual_server.search', json.dumps({'name': slb_virtual})) - else: - result = slb_virtual_data - elif state == 'absent': - if slb_virtual_exists: - result = axapi_call(module, session_url + '&method=slb.virtual_server.delete', json.dumps({'name': slb_virtual})) - changed = True - else: - result = dict(msg="the virtual server was not present") - - # if the config has changed, save the config unless otherwise requested - if changed and write_config: - write_result = axapi_call(module, session_url + '&method=system.action.write_memory') - if axapi_failure(write_result): - module.fail_json(msg="failed to save the configuration: %s" % write_result['response']['err']['msg']) - - # log out of the session nicely and exit - axapi_call(module, session_url + '&method=session.close') - module.exit_json(changed=changed, content=result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/aci/aci_interface_policy_fc.py b/plugins/modules/network/aci/aci_interface_policy_fc.py deleted file mode 100644 index 7297b72479..0000000000 --- a/plugins/modules/network/aci/aci_interface_policy_fc.py +++ /dev/null @@ -1,239 +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 -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: aci_interface_policy_fc -short_description: Manage Fibre Channel interface policies (fc:IfPol) -description: -- Manage ACI Fiber Channel interface policies on Cisco ACI fabrics. -options: - fc_policy: - description: - - The name of the Fiber Channel interface policy. - type: str - required: yes - aliases: [ name ] - description: - description: - - The description of the Fiber Channel interface policy. - type: str - aliases: [ descr ] - port_mode: - description: - - The Port Mode to use. - - The APIC defaults to C(f) when unset during creation. - type: str - choices: [ f, np ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present - name_alias: - description: - - The alias for the current object. This relates to the nameAlias field in ACI. - type: str -extends_documentation_fragment: -- cisco.aci.modules - -seealso: -- name: APIC Management Information Model reference - description: More information about the internal APIC class B(fc:IfPol). - link: https://developer.cisco.com/docs/apic-mim-ref/ -author: -- Dag Wieers (@dagwieers) -''' - -EXAMPLES = r''' -- aci_interface_policy_fc: - host: '{{ hostname }}' - username: '{{ username }}' - password: '{{ password }}' - fc_policy: '{{ fc_policy }}' - port_mode: '{{ port_mode }}' - description: '{{ description }}' - state: present - delegate_to: localhost -''' - -RETURN = r''' -current: - description: The existing configuration from the APIC after the module has finished - returned: success - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -error: - description: The error information as returned from the APIC - returned: failure - type: dict - sample: - { - "code": "122", - "text": "unknown managed object class foo" - } -raw: - description: The raw output returned by the APIC REST API (xml or json) - returned: parse error - type: str - sample: '' -sent: - description: The actual/minimal configuration pushed to the APIC - returned: info - type: list - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment" - } - } - } -previous: - description: The original configuration from the APIC before the module has started - returned: info - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -proposed: - description: The assembled configuration from the user-provided parameters - returned: info - type: dict - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "name": "production" - } - } - } -filter_string: - description: The filter string used for the request - returned: failure or debug - type: str - sample: ?rsp-prop-include=config-only -method: - description: The HTTP method used for the request to the APIC - returned: failure or debug - type: str - sample: POST -response: - description: The HTTP response from the APIC - returned: failure or debug - type: str - sample: OK (30 bytes) -status: - description: The HTTP status from the APIC - returned: failure or debug - type: int - sample: 200 -url: - description: The HTTP url used for the request to the APIC - returned: failure or debug - type: str - sample: https://10.11.12.13/api/mo/uni/tn-production.json -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec - - -def main(): - argument_spec = aci_argument_spec() - argument_spec.update( - fc_policy=dict(type='str', aliases=['name']), # Not required for querying all objects - description=dict(type='str', aliases=['descr']), - port_mode=dict(type='str', choices=['f', 'np']), # No default provided on purpose - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - name_alias=dict(type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['fc_policy']], - ['state', 'present', ['fc_policy']], - ], - ) - - fc_policy = module.params.get('fc_policy') - port_mode = module.params.get('port_mode') - description = module.params.get('description') - state = module.params.get('state') - name_alias = module.params.get('name_alias') - - aci = ACIModule(module) - aci.construct_url( - root_class=dict( - aci_class='fcIfPol', - aci_rn='infra/fcIfPol-{0}'.format(fc_policy), - module_object=fc_policy, - target_filter={'name': fc_policy}, - ), - ) - - aci.get_existing() - - if state == 'present': - aci.payload( - aci_class='fcIfPol', - class_config=dict( - name=fc_policy, - descr=description, - portMode=port_mode, - nameAlias=name_alias, - ), - ) - - aci.get_diff(aci_class='fcIfPol') - - aci.post_config() - - elif state == 'absent': - aci.delete_config() - - aci.exit_json() - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/aci/aci_interface_policy_l2.py b/plugins/modules/network/aci/aci_interface_policy_l2.py deleted file mode 100644 index d2ac475f27..0000000000 --- a/plugins/modules/network/aci/aci_interface_policy_l2.py +++ /dev/null @@ -1,264 +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 -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: aci_interface_policy_l2 -short_description: Manage Layer 2 interface policies (l2:IfPol) -description: -- Manage Layer 2 interface policies on Cisco ACI fabrics. -options: - l2_policy: - description: - - The name of the Layer 2 interface policy. - type: str - required: yes - aliases: [ name ] - description: - description: - - The description of the Layer 2 interface policy. - type: str - aliases: [ descr ] - qinq: - description: - - Determines if QinQ is disabled or if the port should be considered a core or edge port. - - The APIC defaults to C(disabled) when unset during creation. - type: str - choices: [ core, disabled, edge ] - vepa: - description: - - Determines if Virtual Ethernet Port Aggregator is disabled or enabled. - - The APIC defaults to C(no) when unset during creation. - type: bool - vlan_scope: - description: - - The scope of the VLAN. - - The APIC defaults to C(global) when unset during creation. - type: str - choices: [ global, portlocal ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present - name_alias: - description: - - The alias for the current object. This relates to the nameAlias field in ACI. - type: str -extends_documentation_fragment: -- cisco.aci.modules - -seealso: -- name: APIC Management Information Model reference - description: More information about the internal APIC class B(l2:IfPol). - link: https://developer.cisco.com/docs/apic-mim-ref/ -author: -- Dag Wieers (@dagwieers) -''' - -EXAMPLES = r''' -- aci_interface_policy_l2: - host: '{{ hostname }}' - username: '{{ username }}' - password: '{{ password }}' - l2_policy: '{{ l2_policy }}' - vlan_scope: '{{ vlan_policy }}' - description: '{{ description }}' - delegate_to: localhost -''' - -RETURN = r''' -current: - description: The existing configuration from the APIC after the module has finished - returned: success - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -error: - description: The error information as returned from the APIC - returned: failure - type: dict - sample: - { - "code": "122", - "text": "unknown managed object class foo" - } -raw: - description: The raw output returned by the APIC REST API (xml or json) - returned: parse error - type: str - sample: '' -sent: - description: The actual/minimal configuration pushed to the APIC - returned: info - type: list - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment" - } - } - } -previous: - description: The original configuration from the APIC before the module has started - returned: info - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -proposed: - description: The assembled configuration from the user-provided parameters - returned: info - type: dict - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "name": "production" - } - } - } -filter_string: - description: The filter string used for the request - returned: failure or debug - type: str - sample: ?rsp-prop-include=config-only -method: - description: The HTTP method used for the request to the APIC - returned: failure or debug - type: str - sample: POST -response: - description: The HTTP response from the APIC - returned: failure or debug - type: str - sample: OK (30 bytes) -status: - description: The HTTP status from the APIC - returned: failure or debug - type: int - sample: 200 -url: - description: The HTTP url used for the request to the APIC - returned: failure or debug - type: str - sample: https://10.11.12.13/api/mo/uni/tn-production.json -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec - -# Mapping dicts are used to normalize the proposed data to what the APIC expects, which will keep diffs accurate -QINQ_MAPPING = dict( - core='corePort', - disabled='disabled', - edge='edgePort', -) - - -def main(): - argument_spec = aci_argument_spec() - argument_spec.update( - l2_policy=dict(type='str', aliases=['name']), # Not required for querying all policies - description=dict(type='str', aliases=['descr']), - vlan_scope=dict(type='str', choices=['global', 'portlocal']), # No default provided on purpose - qinq=dict(type='str', choices=['core', 'disabled', 'edge']), - vepa=dict(type='bool'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - name_alias=dict(type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['l2_policy']], - ['state', 'present', ['l2_policy']], - ], - ) - - aci = ACIModule(module) - - l2_policy = module.params.get('l2_policy') - vlan_scope = module.params.get('vlan_scope') - qinq = module.params.get('qinq') - if qinq is not None: - qinq = QINQ_MAPPING.get(qinq) - vepa = aci.boolean(module.params.get('vepa'), 'enabled', 'disabled') - description = module.params.get('description') - state = module.params.get('state') - name_alias = module.params.get('name_alias') - - aci.construct_url( - root_class=dict( - aci_class='l2IfPol', - aci_rn='infra/l2IfP-{0}'.format(l2_policy), - module_object=l2_policy, - target_filter={'name': l2_policy}, - ), - ) - - aci.get_existing() - - if state == 'present': - aci.payload( - aci_class='l2IfPol', - class_config=dict( - name=l2_policy, - descr=description, - vlanScope=vlan_scope, - qinq=qinq, vepa=vepa, - nameAlias=name_alias, - ), - ) - - aci.get_diff(aci_class='l2IfPol') - - aci.post_config() - - elif state == 'absent': - aci.delete_config() - - aci.exit_json() - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/aci/aci_interface_policy_lldp.py b/plugins/modules/network/aci/aci_interface_policy_lldp.py deleted file mode 100644 index baec7e29e3..0000000000 --- a/plugins/modules/network/aci/aci_interface_policy_lldp.py +++ /dev/null @@ -1,248 +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 -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: aci_interface_policy_lldp -short_description: Manage LLDP interface policies (lldp:IfPol) -description: -- Manage LLDP interface policies on Cisco ACI fabrics. -options: - lldp_policy: - description: - - The LLDP interface policy name. - type: str - required: yes - aliases: [ name ] - description: - description: - - The description for the LLDP interface policy name. - type: str - aliases: [ descr ] - receive_state: - description: - - Enable or disable Receive state. - - The APIC defaults to C(yes) when unset during creation. - type: bool - transmit_state: - description: - - Enable or Disable Transmit state. - - The APIC defaults to C(yes) when unset during creation. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present - name_alias: - description: - - The alias for the current object. This relates to the nameAlias field in ACI. - type: str -extends_documentation_fragment: -- cisco.aci.modules - -seealso: -- name: APIC Management Information Model reference - description: More information about the internal APIC class B(lldp:IfPol). - link: https://developer.cisco.com/docs/apic-mim-ref/ -author: -- Dag Wieers (@dagwieers) -''' - -# FIXME: Add more, better examples -EXAMPLES = r''' -- aci_interface_policy_lldp: - host: '{{ hostname }}' - username: '{{ username }}' - password: '{{ password }}' - lldp_policy: '{{ lldp_policy }}' - description: '{{ description }}' - receive_state: '{{ receive_state }}' - transmit_state: '{{ transmit_state }}' - delegate_to: localhost -''' - -RETURN = r''' -current: - description: The existing configuration from the APIC after the module has finished - returned: success - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -error: - description: The error information as returned from the APIC - returned: failure - type: dict - sample: - { - "code": "122", - "text": "unknown managed object class foo" - } -raw: - description: The raw output returned by the APIC REST API (xml or json) - returned: parse error - type: str - sample: '' -sent: - description: The actual/minimal configuration pushed to the APIC - returned: info - type: list - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment" - } - } - } -previous: - description: The original configuration from the APIC before the module has started - returned: info - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -proposed: - description: The assembled configuration from the user-provided parameters - returned: info - type: dict - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "name": "production" - } - } - } -filter_string: - description: The filter string used for the request - returned: failure or debug - type: str - sample: ?rsp-prop-include=config-only -method: - description: The HTTP method used for the request to the APIC - returned: failure or debug - type: str - sample: POST -response: - description: The HTTP response from the APIC - returned: failure or debug - type: str - sample: OK (30 bytes) -status: - description: The HTTP status from the APIC - returned: failure or debug - type: int - sample: 200 -url: - description: The HTTP url used for the request to the APIC - returned: failure or debug - type: str - sample: https://10.11.12.13/api/mo/uni/tn-production.json -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec - - -def main(): - argument_spec = aci_argument_spec() - argument_spec.update( - lldp_policy=dict(type='str', aliases=['name']), # Not required for querying all objects - description=dict(type='str', aliases=['descr']), - receive_state=dict(type='bool'), - transmit_state=dict(type='bool'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - name_alias=dict(type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['lldp_policy']], - ['state', 'present', ['lldp_policy']], - ], - ) - - aci = ACIModule(module) - - lldp_policy = module.params.get('lldp_policy') - description = module.params.get('description') - receive_state = aci.boolean(module.params.get('receive_state'), 'enabled', 'disabled') - transmit_state = aci.boolean(module.params.get('transmit_state'), 'enabled', 'disabled') - state = module.params.get('state') - name_alias = module.params.get('name_alias') - - aci.construct_url( - root_class=dict( - aci_class='lldpIfPol', - aci_rn='infra/lldpIfP-{0}'.format(lldp_policy), - module_object=lldp_policy, - target_filter={'name': lldp_policy}, - ), - ) - - aci.get_existing() - - if state == 'present': - aci.payload( - aci_class='lldpIfPol', - class_config=dict( - name=lldp_policy, - descr=description, - adminRxSt=receive_state, - adminTxSt=transmit_state, - nameAlias=name_alias, - ), - ) - - aci.get_diff(aci_class='lldpIfPol') - - aci.post_config() - - elif state == 'absent': - aci.delete_config() - - aci.exit_json() - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/aci/aci_interface_policy_mcp.py b/plugins/modules/network/aci/aci_interface_policy_mcp.py deleted file mode 100644 index 054b7d811e..0000000000 --- a/plugins/modules/network/aci/aci_interface_policy_mcp.py +++ /dev/null @@ -1,239 +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 -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: aci_interface_policy_mcp -short_description: Manage MCP interface policies (mcp:IfPol) -description: -- Manage MCP interface policies on Cisco ACI fabrics. -options: - mcp: - description: - - The name of the MCP interface. - type: str - required: yes - aliases: [ mcp_interface, name ] - description: - description: - - The description for the MCP interface. - type: str - aliases: [ descr ] - admin_state: - description: - - Enable or disable admin state. - - The APIC defaults to C(yes) when unset during creation. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present - name_alias: - description: - - The alias for the current object. This relates to the nameAlias field in ACI. - type: str -extends_documentation_fragment: -- cisco.aci.modules - -seealso: -- name: APIC Management Information Model reference - description: More information about the internal APIC class B(mcp:IfPol). - link: https://developer.cisco.com/docs/apic-mim-ref/ -author: -- Dag Wieers (@dagwieers) -''' - -# FIXME: Add more, better examples -EXAMPLES = r''' -- aci_interface_policy_mcp: - host: '{{ hostname }}' - username: '{{ username }}' - password: '{{ password }}' - mcp: '{{ mcp }}' - description: '{{ descr }}' - admin_state: '{{ admin_state }}' - delegate_to: localhost -''' - -RETURN = r''' -current: - description: The existing configuration from the APIC after the module has finished - returned: success - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -error: - description: The error information as returned from the APIC - returned: failure - type: dict - sample: - { - "code": "122", - "text": "unknown managed object class foo" - } -raw: - description: The raw output returned by the APIC REST API (xml or json) - returned: parse error - type: str - sample: '' -sent: - description: The actual/minimal configuration pushed to the APIC - returned: info - type: list - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment" - } - } - } -previous: - description: The original configuration from the APIC before the module has started - returned: info - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -proposed: - description: The assembled configuration from the user-provided parameters - returned: info - type: dict - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "name": "production" - } - } - } -filter_string: - description: The filter string used for the request - returned: failure or debug - type: str - sample: ?rsp-prop-include=config-only -method: - description: The HTTP method used for the request to the APIC - returned: failure or debug - type: str - sample: POST -response: - description: The HTTP response from the APIC - returned: failure or debug - type: str - sample: OK (30 bytes) -status: - description: The HTTP status from the APIC - returned: failure or debug - type: int - sample: 200 -url: - description: The HTTP url used for the request to the APIC - returned: failure or debug - type: str - sample: https://10.11.12.13/api/mo/uni/tn-production.json -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec - - -def main(): - argument_spec = aci_argument_spec() - argument_spec.update( - mcp=dict(type='str', aliases=['mcp_interface', 'name']), # Not required for querying all objects - description=dict(type='str', aliases=['descr']), - admin_state=dict(type='bool'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - name_alias=dict(type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['mcp']], - ['state', 'present', ['mcp']], - ], - ) - - aci = ACIModule(module) - - mcp = module.params.get('mcp') - description = module.params.get('description') - admin_state = aci.boolean(module.params.get('admin_state'), 'enabled', 'disabled') - state = module.params.get('state') - name_alias = module.params.get('name_alias') - - aci.construct_url( - root_class=dict( - aci_class='mcpIfPol', - aci_rn='infra/mcpIfP-{0}'.format(mcp), - module_object=mcp, - target_filter={'name': mcp}, - ), - ) - - aci.get_existing() - - if state == 'present': - aci.payload( - aci_class='mcpIfPol', - class_config=dict( - name=mcp, - descr=description, - adminSt=admin_state, - nameAlias=name_alias, - ), - ) - - aci.get_diff(aci_class='mcpIfPol') - - aci.post_config() - - elif state == 'absent': - aci.delete_config() - - aci.exit_json() - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/aci/aci_interface_policy_port_channel.py b/plugins/modules/network/aci/aci_interface_policy_port_channel.py deleted file mode 100644 index 6700dd3fdf..0000000000 --- a/plugins/modules/network/aci/aci_interface_policy_port_channel.py +++ /dev/null @@ -1,321 +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 -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: aci_interface_policy_port_channel -short_description: Manage port channel interface policies (lacp:LagPol) -description: -- Manage port channel interface policies on Cisco ACI fabrics. -options: - port_channel: - description: - - Name of the port channel. - type: str - required: yes - aliases: [ name ] - description: - description: - - The description for the port channel. - type: str - aliases: [ descr ] - max_links: - description: - - Maximum links. - - Accepted values range between 1 and 16. - - The APIC defaults to C(16) when unset during creation. - type: int - min_links: - description: - - Minimum links. - - Accepted values range between 1 and 16. - - The APIC defaults to C(1) when unset during creation. - type: int - mode: - description: - - Port channel interface policy mode. - - Determines the LACP method to use for forming port-channels. - - The APIC defaults to C(off) when unset during creation. - type: str - choices: [ active, mac-pin, mac-pin-nicload, 'off', passive ] - fast_select: - description: - - Determines if Fast Select is enabled for Hot Standby Ports. - - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties - left undefined or set to false will not exist after the task is ran. - - The APIC defaults to C(yes) when unset during creation. - type: bool - graceful_convergence: - description: - - Determines if Graceful Convergence is enabled. - - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties - left undefined or set to false will not exist after the task is ran. - - The APIC defaults to C(yes) when unset during creation. - type: bool - load_defer: - description: - - Determines if Load Defer is enabled. - - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties - left undefined or set to false will not exist after the task is ran. - - The APIC defaults to C(no) when unset during creation. - type: bool - suspend_individual: - description: - - Determines if Suspend Individual is enabled. - - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties - left undefined or set to false will not exist after the task is ran. - - The APIC defaults to C(yes) when unset during creation. - type: bool - symmetric_hash: - description: - - Determines if Symmetric Hashing is enabled. - - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties - left undefined or set to false will not exist after the task is ran. - - The APIC defaults to C(no) when unset during creation. - type: bool - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present - name_alias: - description: - - The alias for the current object. This relates to the nameAlias field in ACI. - type: str -extends_documentation_fragment: -- cisco.aci.modules - -seealso: -- name: APIC Management Information Model reference - description: More information about the internal APIC class B(lacp:LagPol). - link: https://developer.cisco.com/docs/apic-mim-ref/ -author: -- Dag Wieers (@dagwieers) -''' - -EXAMPLES = r''' -- aci_interface_policy_port_channel: - host: '{{ inventory_hostname }}' - username: '{{ username }}' - password: '{{ password }}' - port_channel: '{{ port_channel }}' - description: '{{ description }}' - min_links: '{{ min_links }}' - max_links: '{{ max_links }}' - mode: '{{ mode }}' - delegate_to: localhost -''' - -RETURN = r''' -current: - description: The existing configuration from the APIC after the module has finished - returned: success - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -error: - description: The error information as returned from the APIC - returned: failure - type: dict - sample: - { - "code": "122", - "text": "unknown managed object class foo" - } -raw: - description: The raw output returned by the APIC REST API (xml or json) - returned: parse error - type: str - sample: '' -sent: - description: The actual/minimal configuration pushed to the APIC - returned: info - type: list - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment" - } - } - } -previous: - description: The original configuration from the APIC before the module has started - returned: info - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -proposed: - description: The assembled configuration from the user-provided parameters - returned: info - type: dict - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "name": "production" - } - } - } -filter_string: - description: The filter string used for the request - returned: failure or debug - type: str - sample: ?rsp-prop-include=config-only -method: - description: The HTTP method used for the request to the APIC - returned: failure or debug - type: str - sample: POST -response: - description: The HTTP response from the APIC - returned: failure or debug - type: str - sample: OK (30 bytes) -status: - description: The HTTP status from the APIC - returned: failure or debug - type: int - sample: 200 -url: - description: The HTTP url used for the request to the APIC - returned: failure or debug - type: str - sample: https://10.11.12.13/api/mo/uni/tn-production.json -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec - - -def main(): - argument_spec = aci_argument_spec() - argument_spec.update( - port_channel=dict(type='str', aliases=['name']), # Not required for querying all objects - description=dict(type='str', aliases=['descr']), - min_links=dict(type='int'), - max_links=dict(type='int'), - mode=dict(type='str', choices=['active', 'mac-pin', 'mac-pin-nicload', 'off', 'passive']), - fast_select=dict(type='bool'), - graceful_convergence=dict(type='bool'), - load_defer=dict(type='bool'), - suspend_individual=dict(type='bool'), - symmetric_hash=dict(type='bool'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - name_alias=dict(type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['port_channel']], - ['state', 'present', ['port_channel']], - ], - ) - - port_channel = module.params.get('port_channel') - description = module.params.get('description') - min_links = module.params.get('min_links') - if min_links is not None and min_links not in range(1, 17): - module.fail_json(msg='The "min_links" must be a value between 1 and 16') - max_links = module.params.get('max_links') - if max_links is not None and max_links not in range(1, 17): - module.fail_json(msg='The "max_links" must be a value between 1 and 16') - mode = module.params.get('mode') - state = module.params.get('state') - name_alias = module.params.get('name_alias') - - # Build ctrl value for request - ctrl = [] - if module.params.get('fast_select') is True: - ctrl.append('fast-sel-hot-stdby') - if module.params.get('graceful_convergence') is True: - ctrl.append('graceful-conv') - if module.params.get('load_defer') is True: - ctrl.append('load-defer') - if module.params.get('suspend_individual') is True: - ctrl.append('susp-individual') - if module.params.get('symmetric_hash') is True: - ctrl.append('symmetric-hash') - if not ctrl: - ctrl = None - else: - ctrl = ",".join(ctrl) - - aci = ACIModule(module) - aci.construct_url( - root_class=dict( - aci_class='lacpLagPol', - aci_rn='infra/lacplagp-{0}'.format(port_channel), - module_object=port_channel, - target_filter={'name': port_channel}, - ), - ) - - aci.get_existing() - - if state == 'present': - aci.payload( - aci_class='lacpLagPol', - class_config=dict( - name=port_channel, - ctrl=ctrl, - descr=description, - minLinks=min_links, - maxLinks=max_links, - mode=mode, - nameAlias=name_alias, - ), - ) - - aci.get_diff(aci_class='lacpLagPol') - - aci.post_config() - - elif state == 'absent': - aci.delete_config() - - aci.exit_json() - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/aci/aci_interface_policy_port_security.py b/plugins/modules/network/aci/aci_interface_policy_port_security.py deleted file mode 100644 index 8434e7d1d6..0000000000 --- a/plugins/modules/network/aci/aci_interface_policy_port_security.py +++ /dev/null @@ -1,252 +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 -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: aci_interface_policy_port_security -short_description: Manage port security (l2:PortSecurityPol) -description: -- Manage port security on Cisco ACI fabrics. -options: - port_security: - description: - - The name of the port security. - type: str - required: yes - aliases: [ name ] - description: - description: - - The description for the contract. - type: str - aliases: [ descr ] - max_end_points: - description: - - Maximum number of end points. - - Accepted values range between C(0) and C(12000). - - The APIC defaults to C(0) when unset during creation. - type: int - port_security_timeout: - description: - - The delay time in seconds before MAC learning is re-enabled - - Accepted values range between C(60) and C(3600) - - The APIC defaults to C(60) when unset during creation - type: int - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present - name_alias: - description: - - The alias for the current object. This relates to the nameAlias field in ACI. - type: str -extends_documentation_fragment: -- cisco.aci.modules - -seealso: -- name: APIC Management Information Model reference - description: More information about the internal APIC class B(l2:PortSecurityPol). - link: https://developer.cisco.com/docs/apic-mim-ref/ -author: -- Dag Wieers (@dagwieers) -''' - -# FIXME: Add more, better examples -EXAMPLES = r''' -- aci_interface_policy_port_security: - host: '{{ inventory_hostname }}' - username: '{{ username }}' - password: '{{ password }}' - port_security: '{{ port_security }}' - description: '{{ descr }}' - max_end_points: '{{ max_end_points }}' - port_security_timeout: '{{ port_security_timeout }}' - delegate_to: localhost -''' - -RETURN = r''' -current: - description: The existing configuration from the APIC after the module has finished - returned: success - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -error: - description: The error information as returned from the APIC - returned: failure - type: dict - sample: - { - "code": "122", - "text": "unknown managed object class foo" - } -raw: - description: The raw output returned by the APIC REST API (xml or json) - returned: parse error - type: str - sample: '' -sent: - description: The actual/minimal configuration pushed to the APIC - returned: info - type: list - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment" - } - } - } -previous: - description: The original configuration from the APIC before the module has started - returned: info - type: list - sample: - [ - { - "fvTenant": { - "attributes": { - "descr": "Production", - "dn": "uni/tn-production", - "name": "production", - "nameAlias": "", - "ownerKey": "", - "ownerTag": "" - } - } - } - ] -proposed: - description: The assembled configuration from the user-provided parameters - returned: info - type: dict - sample: - { - "fvTenant": { - "attributes": { - "descr": "Production environment", - "name": "production" - } - } - } -filter_string: - description: The filter string used for the request - returned: failure or debug - type: str - sample: ?rsp-prop-include=config-only -method: - description: The HTTP method used for the request to the APIC - returned: failure or debug - type: str - sample: POST -response: - description: The HTTP response from the APIC - returned: failure or debug - type: str - sample: OK (30 bytes) -status: - description: The HTTP status from the APIC - returned: failure or debug - type: int - sample: 200 -url: - description: The HTTP url used for the request to the APIC - returned: failure or debug - type: str - sample: https://10.11.12.13/api/mo/uni/tn-production.json -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec - - -def main(): - argument_spec = aci_argument_spec() - argument_spec.update( - port_security=dict(type='str', aliases=['name']), # Not required for querying all objects - description=dict(type='str', aliases=['descr']), - max_end_points=dict(type='int'), - port_security_timeout=dict(type='int'), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - name_alias=dict(type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['port_security']], - ['state', 'present', ['port_security']], - ], - ) - - port_security = module.params.get('port_security') - description = module.params.get('description') - max_end_points = module.params.get('max_end_points') - port_security_timeout = module.params.get('port_security_timeout') - name_alias = module.params.get('name_alias') - if max_end_points is not None and max_end_points not in range(12001): - module.fail_json(msg='The "max_end_points" must be between 0 and 12000') - if port_security_timeout is not None and port_security_timeout not in range(60, 3601): - module.fail_json(msg='The "port_security_timeout" must be between 60 and 3600') - state = module.params.get('state') - - aci = ACIModule(module) - aci.construct_url( - root_class=dict( - aci_class='l2PortSecurityPol', - aci_rn='infra/portsecurityP-{0}'.format(port_security), - module_object=port_security, - target_filter={'name': port_security}, - ), - ) - - aci.get_existing() - - if state == 'present': - aci.payload( - aci_class='l2PortSecurityPol', - class_config=dict( - name=port_security, - descr=description, - maximum=max_end_points, - nameAlias=name_alias, - ), - ) - - aci.get_diff(aci_class='l2PortSecurityPol') - - aci.post_config() - - elif state == 'absent': - aci.delete_config() - - aci.exit_json() - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/aci/aci_intf_policy_fc.py b/plugins/modules/network/aci/aci_intf_policy_fc.py deleted file mode 120000 index 4f1118aa6d..0000000000 --- a/plugins/modules/network/aci/aci_intf_policy_fc.py +++ /dev/null @@ -1 +0,0 @@ -aci_interface_policy_fc.py \ No newline at end of file diff --git a/plugins/modules/network/aci/aci_intf_policy_l2.py b/plugins/modules/network/aci/aci_intf_policy_l2.py deleted file mode 120000 index 924390568c..0000000000 --- a/plugins/modules/network/aci/aci_intf_policy_l2.py +++ /dev/null @@ -1 +0,0 @@ -aci_interface_policy_l2.py \ No newline at end of file diff --git a/plugins/modules/network/aci/aci_intf_policy_lldp.py b/plugins/modules/network/aci/aci_intf_policy_lldp.py deleted file mode 120000 index 5d751deab3..0000000000 --- a/plugins/modules/network/aci/aci_intf_policy_lldp.py +++ /dev/null @@ -1 +0,0 @@ -aci_interface_policy_lldp.py \ No newline at end of file diff --git a/plugins/modules/network/aci/aci_intf_policy_mcp.py b/plugins/modules/network/aci/aci_intf_policy_mcp.py deleted file mode 120000 index 89b6309246..0000000000 --- a/plugins/modules/network/aci/aci_intf_policy_mcp.py +++ /dev/null @@ -1 +0,0 @@ -aci_interface_policy_mcp.py \ No newline at end of file diff --git a/plugins/modules/network/aci/aci_intf_policy_port_channel.py b/plugins/modules/network/aci/aci_intf_policy_port_channel.py deleted file mode 120000 index bed0d32886..0000000000 --- a/plugins/modules/network/aci/aci_intf_policy_port_channel.py +++ /dev/null @@ -1 +0,0 @@ -aci_interface_policy_port_channel.py \ No newline at end of file diff --git a/plugins/modules/network/aci/aci_intf_policy_port_security.py b/plugins/modules/network/aci/aci_intf_policy_port_security.py deleted file mode 120000 index ff1bc0fdc7..0000000000 --- a/plugins/modules/network/aci/aci_intf_policy_port_security.py +++ /dev/null @@ -1 +0,0 @@ -aci_interface_policy_port_security.py \ No newline at end of file diff --git a/plugins/modules/network/aci/mso_schema_template_external_epg_contract.py b/plugins/modules/network/aci/mso_schema_template_external_epg_contract.py deleted file mode 100644 index 679202b150..0000000000 --- a/plugins/modules/network/aci/mso_schema_template_external_epg_contract.py +++ /dev/null @@ -1,245 +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 -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: mso_schema_template_external_epg_contract -short_description: Manage Extrnal EPG contracts in schema templates -description: -- Manage External EPG contracts in schema templates on Cisco ACI Multi-Site. -author: -- Devarshi Shah (@devarshishah3) -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template to change. - type: str - required: yes - external_epg: - description: - - The name of the EPG to manage. - type: str - required: yes - contract: - description: - - A contract associated to this EPG. - type: dict - suboptions: - name: - description: - - The name of the Contract to associate with. - required: true - type: str - schema: - description: - - The schema that defines the referenced BD. - - If this parameter is unspecified, it defaults to the current schema. - type: str - template: - description: - - The template that defines the referenced BD. - type: str - type: - description: - - The type of contract. - type: str - required: true - choices: [ consumer, provider ] - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -seealso: -- module: cisco.mso.mso_schema_template_externalepg -- module: cisco.mso.mso_schema_template_contract_filter -extends_documentation_fragment: -- cisco.mso.modules - -''' - -EXAMPLES = r''' -- name: Add a contract to an EPG - mso_schema_template_external_epg_contract: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - epg: EPG 1 - contract: - name: Contract 1 - type: consumer - state: present - delegate_to: localhost - -- name: Remove a Contract - mso_schema_template_external_epg_contract: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - epg: EPG 1 - contract: - name: Contract 1 - state: absent - delegate_to: localhost - -- name: Query a specific Contract - mso_schema_template_external_epg_contract: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - epg: EPG 1 - contract: - name: Contract 1 - state: query - delegate_to: localhost - register: query_result - -- name: Query all Contracts - mso_schema_template_external_epg_contract: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec, mso_contractref_spec, issubset - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - external_epg=dict(type='str', required=True), - contract=dict(type='dict', options=mso_contractref_spec()), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['contract']], - ['state', 'present', ['contract']], - ], - ) - - schema = module.params['schema'] - template = module.params['template'] - external_epg = module.params['external_epg'] - contract = module.params['contract'] - state = module.params['state'] - - mso = MSOModule(module) - - if contract: - if contract.get('schema') is None: - contract['schema'] = schema - contract['schema_id'] = mso.lookup_schema(contract['schema']) - if contract.get('template') is None: - contract['template'] = template - - # Get schema_id - schema_obj = mso.get_obj('schemas', displayName=schema) - if schema_obj: - schema_id = schema_obj['id'] - else: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t['name'] for t in schema_obj['templates']] - if template not in templates: - mso.fail_json(msg="Provided template '{0}' does not exist. Existing templates: {1}".format(template, ', '.join(templates))) - template_idx = templates.index(template) - - # Get EPG - epgs = [e['name'] for e in schema_obj['templates'][template_idx]['externalEpgs']] - if external_epg not in epgs: - mso.fail_json(msg="Provided epg '{epg}' does not exist. Existing epgs: {epgs}".format(epg=external_epg, epgs=', '.join(epgs))) - epg_idx = epgs.index(external_epg) - - # Get Contract - if contract: - contracts = [(c['contractRef'], - c['relationshipType']) for c in schema_obj['templates'][template_idx]['externalEpgs'][epg_idx]['contractRelationships']] - contract_ref = mso.contract_ref(**contract) - if (contract_ref, contract['type']) in contracts: - contract_idx = contracts.index((contract_ref, contract['type'])) - contract_path = '/templates/{0}/externalEpgs/{1}/contractRelationships/{2}'.format(template, external_epg, contract) - mso.existing = schema_obj['templates'][template_idx]['externalEpgs'][epg_idx]['contractRelationships'][contract_idx] - - if state == 'query': - if not contract: - mso.existing = schema_obj['templates'][template_idx]['externalEpgs'][epg_idx]['contractRelationships'] - elif not mso.existing: - mso.fail_json(msg="Contract '{0}' not found".format(contract_ref)) - mso.exit_json() - - contracts_path = '/templates/{0}/externalEpgs/{1}/contractRelationships'.format(template, external_epg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.sent = mso.existing = {} - ops.append(dict(op='remove', path=contract_path)) - - elif state == 'present': - payload = dict( - relationshipType=contract['type'], - contractRef=dict( - contractName=contract['name'], - templateName=contract['template'], - schemaId=contract['schema_id'], - ), - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=contract_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=contracts_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/aci/mso_schema_template_external_epg_subnet.py b/plugins/modules/network/aci/mso_schema_template_external_epg_subnet.py deleted file mode 100644 index 1a736b96f5..0000000000 --- a/plugins/modules/network/aci/mso_schema_template_external_epg_subnet.py +++ /dev/null @@ -1,219 +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 -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: mso_schema_template_external_epg_subnet -short_description: Manage External EPG subnets in schema templates -description: -- Manage External EPG subnets in schema templates on Cisco ACI Multi-Site. -author: -- Devarshi Shah (@devarshishah3) -options: - schema: - description: - - The name of the schema. - type: str - required: yes - template: - description: - - The name of the template to change. - type: str - required: yes - external_epg: - description: - - The name of the External EPG to manage. - type: str - required: yes - subnet: - description: - - The IP range in CIDR notation. - type: str - required: true - scope: - description: - - The scope of the subnet. - type: list - aggregate: - description: - - The aggregate option for the subnet. - type: list - state: - description: - - Use C(present) or C(absent) for adding or removing. - - Use C(query) for listing an object or multiple objects. - type: str - choices: [ absent, present, query ] - default: present -notes: -- Due to restrictions of the MSO REST API concurrent modifications to EPG subnets can be dangerous and corrupt data. -extends_documentation_fragment: -- cisco.mso.modules - -''' - -EXAMPLES = r''' -- name: Add a new subnet to an External EPG - mso_schema_template_external_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - external_epg: EPG 1 - subnet: 10.0.0.0/24 - state: present - delegate_to: localhost - -- name: Remove a subnet from an External EPG - mso_schema_template_external_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - external_epg: EPG 1 - subnet: 10.0.0.0/24 - state: absent - delegate_to: localhost - -- name: Query a specific External EPG subnet - mso_schema_template_external_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - external_epg: EPG 1 - subnet: 10.0.0.0/24 - state: query - delegate_to: localhost - register: query_result - -- name: Query all External EPGs subnets - mso_schema_template_external_epg_subnet: - host: mso_host - username: admin - password: SomeSecretPassword - schema: Schema 1 - template: Template 1 - state: query - delegate_to: localhost - register: query_result -''' - -RETURN = r''' -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec, mso_reference_spec, mso_subnet_spec - - -def main(): - argument_spec = mso_argument_spec() - argument_spec.update( - schema=dict(type='str', required=True), - template=dict(type='str', required=True), - external_epg=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['absent', 'present', 'query']), - subnet=dict(type='str', required=True), - scope=dict(type='list', default=[]), - aggregate=dict(type='list', default=[]), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_if=[ - ['state', 'absent', ['subnet']], - ['state', 'present', ['subnet']], - ], - ) - - schema = module.params['schema'] - template = module.params['template'] - external_epg = module.params['external_epg'] - subnet = module.params['subnet'] - scope = module.params['scope'] - aggregate = module.params['aggregate'] - state = module.params['state'] - - mso = MSOModule(module) - - # Get schema - schema_obj = mso.get_obj('schemas', displayName=schema) - if not schema_obj: - mso.fail_json(msg="Provided schema '{0}' does not exist".format(schema)) - - schema_path = 'schemas/{id}'.format(**schema_obj) - - # Get template - templates = [t['name'] for t in schema_obj['templates']] - if template not in templates: - mso.fail_json(msg="Provided template '{template}' does not exist. Existing templates: {templates}".format(template=template, - templates=', '.join(templates))) - template_idx = templates.index(template) - - # Get EPG - external_epgs = [e['name'] for e in schema_obj['templates'][template_idx]['externalEpgs']] - if external_epg not in external_epgs: - mso.fail_json(msg="Provided External EPG '{epg}' does not exist. Existing epgs: {epgs}".format(epg=external_epg, epgs=', '.join(external_epgs))) - epg_idx = external_epgs.index(external_epg) - - # Get Subnet - subnets = [s['ip'] for s in schema_obj['templates'][template_idx]['externalEpgs'][epg_idx]['subnets']] - if subnet in subnets: - subnet_idx = subnets.index(subnet) - # FIXME: Changes based on index are DANGEROUS - subnet_path = '/templates/{0}/externalEpgs/{1}/subnets/{2}'.format(template, external_epg, subnet_idx) - mso.existing = schema_obj['templates'][template_idx]['externalEpgs'][epg_idx]['subnets'][subnet_idx] - - if state == 'query': - if subnet is None: - mso.existing = schema_obj['templates'][template_idx]['externalEpgs'][epg_idx]['subnets'] - elif not mso.existing: - mso.fail_json(msg="Subnet '{subnet}' not found".format(subnet=subnet)) - mso.exit_json() - - subnets_path = '/templates/{0}/externalEpgs/{1}/subnets'.format(template, external_epg) - ops = [] - - mso.previous = mso.existing - if state == 'absent': - if mso.existing: - mso.existing = {} - ops.append(dict(op='remove', path=subnet_path)) - - elif state == 'present': - payload = dict( - ip=subnet, - scope=scope, - aggregate=aggregate, - ) - - mso.sanitize(payload, collate=True) - - if mso.existing: - ops.append(dict(op='replace', path=subnet_path, value=mso.sent)) - else: - ops.append(dict(op='add', path=subnets_path + '/-', value=mso.sent)) - - mso.existing = mso.proposed - - if not module.check_mode: - mso.request(schema_path, method='PATCH', data=ops) - - mso.exit_json() - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/aireos/aireos_command.py b/plugins/modules/network/aireos/aireos_command.py deleted file mode 100644 index da5da5155e..0000000000 --- a/plugins/modules/network/aireos/aireos_command.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Team -# 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: aireos_command -author: "James Mighion (@jmighion)" -short_description: Run commands on remote devices running Cisco WLC -description: - - Sends arbitrary commands to an aireos node and returns the results - read from the device. This module includes an - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. - - Commands run in configuration mode with this module are not - idempotent. Please use M(aireos_config) to configure WLC devices. -extends_documentation_fragment: -- community.general.aireos - -options: - commands: - description: - - List of commands to send to the remote aireos 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. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - aliases: ['waitfor'] - match: - description: - - The I(match) argument is used in conjunction with the - I(wait_for) argument to specify the match policy. Valid - values are C(all) or C(any). If the value is set to C(all) - then all conditionals in the wait_for must be satisfied. If - the value is set to C(any) then only one of the values must be - satisfied. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -tasks: - - name: run show sysinfo on remote devices - aireos_command: - commands: show sysinfo - - - name: run show sysinfo and check to see if output contains Cisco Controller - aireos_command: - commands: show sysinfo - wait_for: result[0] contains 'Cisco Controller' - - - name: run multiple commands on remote nodes - aireos_command: - commands: - - show sysinfo - - show interface summary - - - name: run multiple commands and evaluate the output - aireos_command: - commands: - - show sysinfo - - show interface summary - wait_for: - - result[0] contains Cisco Controller - - result[1] contains Loopback0 -""" - -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: ['...', '...'] -""" -import time - -from ansible_collections.community.general.plugins.module_utils.network.aireos.aireos import run_commands -from ansible_collections.community.general.plugins.module_utils.network.aireos.aireos import aireos_argument_spec, check_args -from ansible.module_utils.basic import AnsibleModule -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 -from ansible.module_utils._text import to_text - - -def to_lines(stdout): - for item in stdout: - if isinstance(item, string_types): - item = to_text(item, errors='surrogate_then_replace').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'): - warnings.append( - 'commands run in config mode with aireos_command are not ' - 'idempotent. Please use aireos_config instead' - ) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list', aliases=['waitfor']), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - argument_spec.update(aireos_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/plugins/modules/network/aireos/aireos_config.py b/plugins/modules/network/aireos/aireos_config.py deleted file mode 100644 index c2244db774..0000000000 --- a/plugins/modules/network/aireos/aireos_config.py +++ /dev/null @@ -1,357 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Team -# 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: aireos_config -author: "James Mighion (@jmighion)" -short_description: Manage Cisco WLC configurations -description: - - AireOS does not use a block indent file syntax, so there are no sections or parents. - This module provides an implementation for working with AireOS configurations in - a deterministic way. -extends_documentation_fragment: -- community.general.aireos - -options: - lines: - description: - - The ordered set of commands that should be configured. - The commands must be the exact same commands as found - in the device run-config. Be sure to note the configuration - command syntax as some commands are automatically modified by the - device config parser. - aliases: ['commands'] - 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). - 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. - 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. - 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(none), the module will not attempt to - compare the source configuration with the running - configuration on the remote device. - default: line - choices: ['line', 'none'] - 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' - running_config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(running_config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - aliases: ['config'] - save: - description: - - The C(save) argument instructs the module to save the - running-config to startup-config. This operation is performed - after any changes are made to the current running config. If - no changes are made, the configuration is still saved to the - startup config. This option will always cause the module to - return changed. This argument is mutually exclusive with I(save_when). - - This option is deprecated as of Ansible 2.7, use C(save_when) - type: bool - default: 'no' - save_when: - description: - - When changes are made to the device running-configuration, the - changes are not copied to non-volatile storage by default. Using - this argument will change that. If the argument is set to - I(always), then the running-config will always be copied to the - startup-config and the module will always return as changed. - If the argument is set to I(never), the running-config will never - be copied to the startup-config. If the argument is set to I(changed), - then the running-config will only be copied to the startup-config if - the task has made a change. - default: never - choices: ['always', 'never', 'changed'] - diff_against: - description: - - When using the C(ansible-playbook --diff) command line argument - the module can generate diffs against different sources. - - When this option is configured as I(intended), the module will - return the diff of the running-config against the configuration - provided in the C(intended_config) argument. - - When this option is configured as I(running), the module will - return the before and after diff of the running-config with respect - to any changes made to the device configuration. - choices: ['intended', 'running'] - diff_ignore_lines: - description: - - Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - intended_config: - description: - - The C(intended_config) provides the master configuration that - the node should conform to and is used to check the final - running-config against. This argument will not modify any settings - on the remote device and is strictly used to check the compliance - of the current device's configuration against. When specifying this - argument, the task should also modify the C(diff_against) value and - set it to I(intended). - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- name: configure configuration - aireos_config: - lines: sysname testDevice - -- name: diff the running-config against a provided config - aireos_config: - diff_against: intended - intended: "{{ lookup('file', 'master.cfg') }}" - -- name: load new acl into device - aireos_config: - lines: - - acl create testACL - - acl rule protocol testACL 1 any - - acl rule direction testACL 3 in - before: acl delete testACL - -- name: configurable backup path - aireos_config: - backup: yes - lines: sysname testDevice - backup_options: - filename: backup.cfg - dir_path: /home/user -""" - -RETURN = """ -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'vlan 1', 'name default'] -updates: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'vlan 1', 'name default'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/aireos_config.2016-07-16@22:28:34 -""" -from ansible_collections.community.general.plugins.module_utils.network.aireos.aireos import run_commands, get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.aireos.aireos import aireos_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.aireos.aireos import check_args as aireos_check_args -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - - -def get_running_config(module, config=None): - contents = module.params['running_config'] - if not contents: - if config: - contents = config - else: - contents = get_config(module) - return NetworkConfig(indent=1, contents=contents) - - -def get_candidate(module): - candidate = NetworkConfig(indent=1) - - if module.params['src']: - candidate.load(module.params['src']) - elif module.params['lines']: - candidate.add(module.params['lines']) - return candidate - - -def save_config(module, result): - result['changed'] = True - if not module.check_mode: - command = {"command": "save config", "prompt": "Are you sure you want to save", "answer": "y"} - run_commands(module, command) - else: - module.warn('Skipping command `save config` due to check_mode. Configuration not copied to ' - 'non-volatile storage') - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'none']), - - running_config=dict(aliases=['config']), - intended_config=dict(), - - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - - # save is deprecated as of 2.7, use save_when instead - save=dict(type='bool', default=False, removed_in_version='2.11'), - save_when=dict(choices=['always', 'never', 'changed'], default='never'), - - diff_against=dict(choices=['running', 'intended']), - diff_ignore_lines=dict(type='list') - ) - - argument_spec.update(aireos_argument_spec) - - mutually_exclusive = [('lines', 'src'), - ('save', 'save_when')] - - required_if = [('diff_against', 'intended', ['intended_config'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - warnings = list() - aireos_check_args(module, warnings) - result = {'changed': False, 'warnings': warnings} - - config = None - - if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'): - contents = get_config(module) - config = NetworkConfig(indent=1, contents=contents) - if module.params['backup']: - result['__backup__'] = contents - - if any((module.params['src'], module.params['lines'])): - match = module.params['match'] - - candidate = get_candidate(module) - - if match != 'none': - config = get_running_config(module, config) - configobjs = candidate.difference(config, match=match) - else: - configobjs = candidate.items - - if configobjs: - commands = dumps(configobjs, 'commands').split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - result['updates'] = commands - - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - diff_ignore_lines = module.params['diff_ignore_lines'] - - if module.params['save_when'] == 'always' or module.params['save']: - save_config(module, result) - elif module.params['save_when'] == 'changed' and result['changed']: - save_config(module, result) - - if module._diff: - output = run_commands(module, 'show run-config commands') - contents = output[0] - - # recreate the object in order to process diff_ignore_lines - running_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if module.params['diff_against'] == 'running': - if module.check_mode: - module.warn("unable to perform diff against running-config due to check mode") - contents = None - else: - contents = config.config_text - elif module.params['diff_against'] == 'intended': - contents = module.params['intended_config'] - - if contents is not None: - base_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if running_config.sha1 != base_config.sha1: - result.update({ - 'changed': True, - 'diff': {'before': str(base_config), 'after': str(running_config)} - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/apconos/apconos_command.py b/plugins/modules/network/apconos/apconos_command.py deleted file mode 100644 index dc14fa0eaf..0000000000 --- a/plugins/modules/network/apconos/apconos_command.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2019 APCON. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Module to execute apconos Commands on Apcon Switches. -# Apcon Networking - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: apconos_command -author: "David Lee (@davidlee-ap)" -short_description: Run arbitrary commands on APCON devices -description: - - Sends arbitrary commands to an apcon device and returns the results - read from the device. The module includes an argument that will - cause the module to wait for a specific condition before returning - or timing out if the condition is not met. -notes: - - Tested against apcon iis+ii -options: - commands: - description: - - List of commands to send to the remote 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 retires as expired. - required: true - type: list - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - type: list - 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. - default: all - choices: ['any', 'all'] - type: str - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - default: 10 - type: int - 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. - default: 1 - type: int -''' - -EXAMPLES = """ -- name: Basic Configuration - apconos_command: - commands: - - show version - - enable ssh - register: result - -- name: Get output from single command - apconos_command: - commands: ['show version'] - register: result -""" - -RETURN = """ -""" - -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_lines -from ansible_collections.community.general.plugins.module_utils.network.apconos.apconos import run_commands -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional - - -def parse_commands(module, warnings): - - commands = module.params['commands'] - - if module.check_mode: - for item in list(commands): - if not item.startswith('show'): - warnings.append( - 'Only show commands are supported when using check mode, not ' - 'executing %s' % item - ) - commands.remove(item) - - return commands - - -def main(): - spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=spec, supports_check_mode=False) - warnings = list() - result = {'changed': False, 'warnings': warnings} - - wait_for = module.params['wait_for'] or list() - conditionals = [Conditional(c) for c in wait_for] - - commands = parse_commands(module, warnings) - commands = module.params['commands'] - 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) - - for item in responses: - if len(item) == 0: - if module.check_mode: - result.update({ - 'changed': False, - 'stdout': responses, - 'stdout_lines': list(to_lines(responses)) - }) - else: - result.update({ - 'changed': True, - 'stdout': responses, - 'stdout_lines': list(to_lines(responses)) - }) - elif 'ERROR' in item: - result.update({ - 'failed': True, - 'stdout': responses, - 'stdout_lines': list(to_lines(responses)) - }) - else: - result.update({ - 'stdout': item, - 'stdout_lines': list(to_lines(responses)) - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/aruba/aruba_command.py b/plugins/modules/network/aruba/aruba_command.py deleted file mode 100644 index d59756e2e9..0000000000 --- a/plugins/modules/network/aruba/aruba_command.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Team -# 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: aruba_command -author: "James Mighion (@jmighion)" -short_description: Run commands on remote devices running Aruba Mobility Controller -description: - - Sends arbitrary commands to an aruba node and returns the results - read from the device. This module includes an - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. - - This module does not support running commands in configuration mode. - Please use M(aruba_config) to configure Aruba devices. -extends_documentation_fragment: -- community.general.aruba - -options: - commands: - description: - - List of commands to send to the remote aruba 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. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - aliases: ['waitfor'] - match: - description: - - The I(match) argument is used in conjunction with the - I(wait_for) argument to specify the match policy. Valid - values are C(all) or C(any). If the value is set to C(all) - then all conditionals in the wait_for must be satisfied. If - the value is set to C(any) then only one of the values must be - satisfied. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -tasks: - - name: run show version on remote devices - aruba_command: - commands: show version - - - name: run show version and check to see if output contains Aruba - aruba_command: - commands: show version - wait_for: result[0] contains Aruba - - - name: run multiple commands on remote nodes - aruba_command: - commands: - - show version - - show interfaces - - - name: run multiple commands and evaluate the output - aruba_command: - commands: - - show version - - show interfaces - wait_for: - - result[0] contains Aruba - - result[1] contains Loopback0 -""" - -RETURN = """ -stdout: - description: The set of responses from the commands - returned: always - type: list - sample: ['...', '...'] -stdout_lines: - description: The value of stdout split into a list - returned: always - type: list - sample: [['...', '...'], ['...'], ['...']] -failed_conditions: - description: The list of conditionals that have failed - returned: failed - type: list - sample: ['...', '...'] -""" -import time - -from ansible_collections.community.general.plugins.module_utils.network.aruba.aruba import run_commands -from ansible_collections.community.general.plugins.module_utils.network.aruba.aruba import aruba_argument_spec, check_args -from ansible.module_utils.basic import AnsibleModule -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='aruba_command does not support running config mode ' - 'commands. Please use aruba_config instead' - ) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list', aliases=['waitfor']), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - argument_spec.update(aruba_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/plugins/modules/network/aruba/aruba_config.py b/plugins/modules/network/aruba/aruba_config.py deleted file mode 100644 index 4511ccd3dc..0000000000 --- a/plugins/modules/network/aruba/aruba_config.py +++ /dev/null @@ -1,424 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Team -# 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: aruba_config -author: "James Mighion (@jmighion)" -short_description: Manage Aruba configuration sections -description: - - Aruba configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with Aruba configuration sections in - a deterministic way. -extends_documentation_fragment: -- community.general.aruba - -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. - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - 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. - 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. - 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. - 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. - default: line - choices: ['line', 'block'] - 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' - running_config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(running_config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - aliases: ['config'] - save_when: - description: - - When changes are made to the device running-configuration, the - changes are not copied to non-volatile storage by default. Using - this argument will change that before. If the argument is set to - I(always), then the running-config will always be copied to the - startup configuration and the I(modified) flag will always be set to - True. If the argument is set to I(modified), then the running-config - will only be copied to the startup configuration if it has changed since - the last save to startup configuration. If the argument is set to - I(never), the running-config will never be copied to the - startup configuration. If the argument is set to I(changed), then the running-config - will only be copied to the startup configuration if the task has made a change. - default: never - choices: ['always', 'never', 'modified', 'changed'] - diff_against: - description: - - When using the C(ansible-playbook --diff) command line argument - the module can generate diffs against different sources. - - When this option is configure as I(startup), the module will return - the diff of the running-config against the startup configuration. - - When this option is configured as I(intended), the module will - return the diff of the running-config against the configuration - provided in the C(intended_config) argument. - - When this option is configured as I(running), the module will - return the before and after diff of the running-config with respect - to any changes made to the device configuration. - choices: ['startup', 'intended', 'running'] - diff_ignore_lines: - description: - - Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - intended_config: - description: - - The C(intended_config) provides the master configuration that - the node should conform to and is used to check the final - running-config against. This argument will not modify any settings - on the remote device and is strictly used to check the compliance - of the current device's configuration against. When specifying this - argument, the task should also modify the C(diff_against) value and - set it to I(intended). - encrypt: - description: - - This allows an Aruba controller's passwords and keys to be displayed in plain - text when set to I(false) or encrypted when set to I(true). - If set to I(false), the setting will re-encrypt at the end of the module run. - Backups are still encrypted even when set to I(false). - type: bool - default: 'yes' - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- name: configure top level configuration - aruba_config: - lines: hostname {{ inventory_hostname }} - -- name: diff the running-config against a provided config - aruba_config: - diff_against: intended - intended_config: "{{ lookup('file', 'master.cfg') }}" - -- name: configure interface settings - aruba_config: - lines: - - description test interface - - ip access-group 1 in - parents: interface gigabitethernet 0/0/0 - -- name: load new acl into device - aruba_config: - lines: - - permit host 10.10.10.10 - - ipv6 permit host fda9:97d6:32a3:3e59::3333 - parents: ip access-list standard 1 - before: no ip access-list standard 1 - match: exact - -- name: configurable backup path - aruba_config: - backup: yes - lines: hostname {{ inventory_hostname }} - backup_options: - filename: backup.cfg - dir_path: /home/user -""" - -RETURN = """ -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'vlan 1', 'name default'] -updates: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'vlan 1', 'name default'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/aruba_config.2016-07-16@22:28:34 -""" - - -from ansible_collections.community.general.plugins.module_utils.network.aruba.aruba import run_commands, get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.aruba.aruba import aruba_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.aruba.aruba import check_args as aruba_check_args -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - - -def get_running_config(module, config=None): - contents = module.params['running_config'] - if not contents: - if config: - contents = config - else: - contents = get_config(module) - return NetworkConfig(contents=contents) - - -def get_candidate(module): - candidate = NetworkConfig() - - if module.params['src']: - candidate.load(module.params['src']) - elif module.params['lines']: - parents = module.params['parents'] or list() - candidate.add(module.params['lines'], parents=parents) - return candidate - - -def save_config(module, result): - result['changed'] = True - if not module.check_mode: - run_commands(module, 'write memory') - else: - module.warn('Skipping command `write memory` ' - 'due to check_mode. Configuration not copied to ' - 'non-volatile storage') - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - - running_config=dict(aliases=['config']), - intended_config=dict(), - - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - - save_when=dict(choices=['always', 'never', 'modified', 'changed'], default='never'), - - diff_against=dict(choices=['running', 'startup', 'intended']), - diff_ignore_lines=dict(type='list'), - - encrypt=dict(type='bool', default=True), - ) - - argument_spec.update(aruba_argument_spec) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('diff_against', 'intended', ['intended_config'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - warnings = list() - aruba_check_args(module, warnings) - result = {'changed': False, 'warnings': warnings} - - config = None - - if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'): - contents = get_config(module) - config = NetworkConfig(contents=contents) - if module.params['backup']: - result['__backup__'] = contents - - if not module.params['encrypt']: - run_commands(module, 'encrypt disable') - - if any((module.params['src'], module.params['lines'])): - match = module.params['match'] - replace = module.params['replace'] - - candidate = get_candidate(module) - - if match != 'none': - config = get_running_config(module, config) - path = module.params['parents'] - configobjs = candidate.difference(config, match=match, replace=replace, path=path) - else: - configobjs = candidate.items - - if configobjs: - commands = dumps(configobjs, 'commands').split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - result['updates'] = commands - - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - running_config = None - startup_config = None - - diff_ignore_lines = module.params['diff_ignore_lines'] - - if module.params['save_when'] == 'always': - save_config(module, result) - elif module.params['save_when'] == 'modified': - output = run_commands(module, ['show running-config', 'show configuration']) - - running_config = NetworkConfig(contents=output[0], ignore_lines=diff_ignore_lines) - startup_config = NetworkConfig(contents=output[1], ignore_lines=diff_ignore_lines) - - if running_config.sha1 != startup_config.sha1: - save_config(module, result) - elif module.params['save_when'] == 'changed': - if result['changed']: - save_config(module, result) - - if module._diff: - if not running_config: - output = run_commands(module, 'show running-config') - contents = output[0] - else: - contents = running_config.config_text - - # recreate the object in order to process diff_ignore_lines - running_config = NetworkConfig(contents=contents, ignore_lines=diff_ignore_lines) - - if module.params['diff_against'] == 'running': - if module.check_mode: - module.warn("unable to perform diff against running-config due to check mode") - contents = None - else: - contents = config.config_text - - elif module.params['diff_against'] == 'startup': - if not startup_config: - output = run_commands(module, 'show configuration') - contents = output[0] - else: - contents = startup_config.config_text - - elif module.params['diff_against'] == 'intended': - contents = module.params['intended_config'] - - if contents is not None: - base_config = NetworkConfig(contents=contents, ignore_lines=diff_ignore_lines) - - if running_config.sha1 != base_config.sha1: - result.update({ - 'changed': True, - 'diff': {'before': str(base_config), 'after': str(running_config)} - }) - - # make sure 'encrypt enable' is applied if it was ever disabled - if not module.params['encrypt']: - run_commands(module, 'encrypt enable') - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_actiongroupconfig.py b/plugins/modules/network/avi/avi_actiongroupconfig.py deleted file mode 100644 index 9374845364..0000000000 --- a/plugins/modules/network/avi/avi_actiongroupconfig.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_actiongroupconfig -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ActionGroupConfig Avi RESTful Object -description: - - This module is used to configure ActionGroupConfig object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - action_script_config_ref: - description: - - Reference of the action script configuration to be used. - - It is a reference to an object of type alertscriptconfig. - autoscale_trigger_notification: - description: - - Trigger notification to autoscale manager. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - description: - description: - - User defined description for the object. - email_config_ref: - description: - - Select the email notification configuration to use when sending alerts via email. - - It is a reference to an object of type alertemailconfig. - external_only: - description: - - Generate alert only to external destinations. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - required: true - type: bool - level: - description: - - When an alert is generated, mark its priority via the alert level. - - Enum options - ALERT_LOW, ALERT_MEDIUM, ALERT_HIGH. - - Default value when not specified in API or module is interpreted by Avi Controller as ALERT_LOW. - required: true - name: - description: - - Name of the object. - required: true - snmp_trap_profile_ref: - description: - - Select the snmp trap notification to use when sending alerts via snmp trap. - - It is a reference to an object of type snmptrapprofile. - syslog_config_ref: - description: - - Select the syslog notification configuration to use when sending alerts via syslog. - - It is a reference to an object of type alertsyslogconfig. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create ActionGroupConfig object - avi_actiongroupconfig: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_actiongroupconfig -""" - -RETURN = ''' -obj: - description: ActionGroupConfig (api/actiongroupconfig) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - action_script_config_ref=dict(type='str',), - autoscale_trigger_notification=dict(type='bool',), - description=dict(type='str',), - email_config_ref=dict(type='str',), - external_only=dict(type='bool', required=True), - level=dict(type='str', required=True), - name=dict(type='str', required=True), - snmp_trap_profile_ref=dict(type='str',), - syslog_config_ref=dict(type='str',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'actiongroupconfig', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_alertconfig.py b/plugins/modules/network/avi/avi_alertconfig.py deleted file mode 100644 index b512195ba9..0000000000 --- a/plugins/modules/network/avi/avi_alertconfig.py +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_alertconfig -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of AlertConfig Avi RESTful Object -description: - - This module is used to configure AlertConfig object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - action_group_ref: - description: - - The alert config will trigger the selected alert action, which can send notifications and execute a controlscript. - - It is a reference to an object of type actiongroupconfig. - alert_rule: - description: - - List of filters matching on events or client logs used for triggering alerts. - required: true - autoscale_alert: - description: - - This alert config applies to auto scale alerts. - type: bool - category: - description: - - Determines whether an alert is raised immediately when event occurs (realtime) or after specified number of events occurs within rolling time - - window. - - Enum options - REALTIME, ROLLINGWINDOW, WATERMARK. - - Default value when not specified in API or module is interpreted by Avi Controller as REALTIME. - required: true - description: - description: - - A custom description field. - enabled: - description: - - Enable or disable this alert config from generating new alerts. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - expiry_time: - description: - - An alert is expired and deleted after the expiry time has elapsed. - - The original event triggering the alert remains in the event's log. - - Allowed values are 1-31536000. - - Default value when not specified in API or module is interpreted by Avi Controller as 86400. - name: - description: - - Name of the alert configuration. - required: true - obj_uuid: - description: - - Uuid of the resource for which alert was raised. - object_type: - description: - - The object type to which the alert config is associated with. - - Valid object types are - virtual service, pool, service engine. - - Enum options - VIRTUALSERVICE, POOL, HEALTHMONITOR, NETWORKPROFILE, APPLICATIONPROFILE, HTTPPOLICYSET, DNSPOLICY, SECURITYPOLICY, IPADDRGROUP, - - STRINGGROUP, SSLPROFILE, SSLKEYANDCERTIFICATE, NETWORKSECURITYPOLICY, APPLICATIONPERSISTENCEPROFILE, ANALYTICSPROFILE, VSDATASCRIPTSET, TENANT, - - PKIPROFILE, AUTHPROFILE, CLOUD, SERVERAUTOSCALEPOLICY, AUTOSCALELAUNCHCONFIG, MICROSERVICEGROUP, IPAMPROFILE, HARDWARESECURITYMODULEGROUP, - - POOLGROUP, PRIORITYLABELS, POOLGROUPDEPLOYMENTPOLICY, GSLBSERVICE, GSLBSERVICERUNTIME, SCHEDULER, GSLBGEODBPROFILE, - - GSLBAPPLICATIONPERSISTENCEPROFILE, TRAFFICCLONEPROFILE, VSVIP, WAFPOLICY, WAFPROFILE, ERRORPAGEPROFILE, ERRORPAGEBODY, L4POLICYSET, - - GSLBSERVICERUNTIMEBATCH, WAFPOLICYPSMGROUP, PINGACCESSAGENT, SERVICEENGINEPOLICY, NATPOLICY, SSOPOLICY, PROTOCOLPARSER, SERVICEENGINE, - - DEBUGSERVICEENGINE, DEBUGCONTROLLER, DEBUGVIRTUALSERVICE, SERVICEENGINEGROUP, SEPROPERTIES, NETWORK, CONTROLLERNODE, CONTROLLERPROPERTIES, - - SYSTEMCONFIGURATION, VRFCONTEXT, USER, ALERTCONFIG, ALERTSYSLOGCONFIG, ALERTEMAILCONFIG, ALERTTYPECONFIG, APPLICATION, ROLE, CLOUDPROPERTIES, - - SNMPTRAPPROFILE, ACTIONGROUPPROFILE, MICROSERVICE, ALERTPARAMS, ACTIONGROUPCONFIG, CLOUDCONNECTORUSER, GSLB, GSLBDNSUPDATE, GSLBSITEOPS, - - GLBMGRWARMSTART, IPAMDNSRECORD, GSLBDNSGSSTATUS, GSLBDNSGEOFILEOPS, GSLBDNSGEOUPDATE, GSLBDNSGEOCLUSTEROPS, GSLBDNSCLEANUP, GSLBSITEOPSRESYNC, - - IPAMDNSPROVIDERPROFILE, TCPSTATRUNTIME, UDPSTATRUNTIME, IPSTATRUNTIME, ARPSTATRUNTIME, MBSTATRUNTIME, IPSTKQSTATSRUNTIME, MALLOCSTATRUNTIME, - - SHMALLOCSTATRUNTIME, CPUUSAGERUNTIME, L7GLOBALSTATSRUNTIME, L7VIRTUALSERVICESTATSRUNTIME, SEAGENTVNICDBRUNTIME, SEAGENTGRAPHDBRUNTIME, - - SEAGENTSTATERUNTIME, INTERFACERUNTIME, ARPTABLERUNTIME, DISPATCHERSTATRUNTIME, DISPATCHERSTATCLEARRUNTIME, DISPATCHERTABLEDUMPRUNTIME, - - DISPATCHERREMOTETIMERLISTDUMPRUNTIME, METRICSAGENTMESSAGE, HEALTHMONITORSTATRUNTIME, METRICSENTITYRUNTIME, PERSISTENCEINTERNAL, - - HTTPPOLICYSETINTERNAL, DNSPOLICYINTERNAL, CONNECTIONDUMPRUNTIME, SHAREDDBSTATS, SHAREDDBSTATSCLEAR, ICMPSTATRUNTIME, ROUTETABLERUNTIME, - - VIRTUALMACHINE, POOLSERVER, SEVSLIST, MEMINFORUNTIME, RTERINGSTATRUNTIME, ALGOSTATRUNTIME, HEALTHMONITORRUNTIME, CPUSTATRUNTIME, SEVM, HOST, - - PORTGROUP, CLUSTER, DATACENTER, VCENTER, HTTPPOLICYSETSTATS, DNSPOLICYSTATS, METRICSSESTATS, RATELIMITERSTATRUNTIME, NETWORKSECURITYPOLICYSTATS, - - TCPCONNRUNTIME, POOLSTATS, CONNPOOLINTERNAL, CONNPOOLSTATS, VSHASHSHOWRUNTIME, SELOGSTATSRUNTIME, NETWORKSECURITYPOLICYDETAIL, LICENSERUNTIME, - - SERVERRUNTIME, METRICSRUNTIMESUMMARY, METRICSRUNTIMEDETAIL, DISPATCHERSEHMPROBETEMPDISABLERUNTIME, POOLDEBUG, VSLOGMGRMAP, SERUMINSERTIONSTATS, - - HTTPCACHE, HTTPCACHESTATS, SEDOSSTATRUNTIME, VSDOSSTATRUNTIME, SERVERUPDATEREQ, VSSCALEOUTLIST, SEMEMDISTRUNTIME, TCPCONNRUNTIMEDETAIL, - - SEUPGRADESTATUS, SEUPGRADEPREVIEW, SEFAULTINJECTEXHAUSTM, SEFAULTINJECTEXHAUSTMCL, SEFAULTINJECTEXHAUSTMCLSMALL, SEFAULTINJECTEXHAUSTCONN, - - SEHEADLESSONLINEREQ, SEUPGRADE, SEUPGRADESTATUSDETAIL, SERESERVEDVS, SERESERVEDVSCLEAR, VSCANDIDATESEHOSTLIST, SEGROUPUPGRADE, REBALANCE, - - SEGROUPREBALANCE, SEAUTHSTATSRUNTIME, AUTOSCALESTATE, VIRTUALSERVICEAUTHSTATS, NETWORKSECURITYPOLICYDOS, KEYVALINTERNAL, KEYVALSUMMARYINTERNAL, - - SERVERSTATEUPDATEINFO, CLTRACKINTERNAL, CLTRACKSUMMARYINTERNAL, MICROSERVICERUNTIME, SEMICROSERVICE, VIRTUALSERVICEANALYSIS, CLIENTINTERNAL, - - CLIENTSUMMARYINTERNAL, MICROSERVICEGROUPRUNTIME, BGPRUNTIME, REQUESTQUEUERUNTIME, MIGRATEALL, MIGRATEALLSTATUSSUMMARY, MIGRATEALLSTATUSDETAIL, - - INTERFACESUMMARYRUNTIME, INTERFACELACPRUNTIME, DNSTABLE, GSLBSERVICEDETAIL, GSLBSERVICEINTERNAL, GSLBSERVICEHMONSTAT, SETROLESREQUEST, - - TRAFFICCLONERUNTIME, GEOLOCATIONINFO, SEVSHBSTATRUNTIME, GEODBINTERNAL, GSLBSITEINTERNAL, WAFSTATS, USERDEFINEDDATASCRIPTCOUNTERS, LLDPRUNTIME, - - VSESSHARINGPOOL, NDTABLERUNTIME, IP6STATRUNTIME, ICMP6STATRUNTIME, SEVSSPLACEMENT, L4POLICYSETSTATS, L4POLICYSETINTERNAL, BGPDEBUGINFO, SHARD, - - CPUSTATRUNTIMEDETAIL, SEASSERTSTATRUNTIME, SEFAULTINJECTINFRA, SEAGENTASSERTSTATRUNTIME, SEDATASTORESTATUS, DIFFQUEUESTATUS, IP6ROUTETABLERUNTIME, - - SECURITYMGRSTATE, VIRTUALSERVICESESCALEOUTSTATUS, SHARDSERVERSTATUS, SEAGENTSHARDCLIENTRESOURCEMAP, SEAGENTCONSISTENTHASH, SEAGENTVNICDBHISTORY, - - SEAGENTSHARDCLIENTAPPMAP, SEAGENTSHARDCLIENTEVENTHISTORY, SENATSTATRUNTIME, SENATFLOWRUNTIME, SERESOURCEPROTO, SECONSUMERPROTO, - - SECREATEPENDINGPROTO, PLACEMENTSTATS, SEVIPPROTO, RMVRFPROTO, VCENTERMAP, VIMGRVCENTERRUNTIME, INTERESTEDVMS, INTERESTEDHOSTS, - - VCENTERSUPPORTEDCOUNTERS, ENTITYCOUNTERS, TRANSACTIONSTATS, SEVMCREATEPROGRESS, PLACEMENTSTATUS, VISUBFOLDERS, VIDATASTORE, VIHOSTRESOURCES, - - CLOUDCONNECTOR, VINETWORKSUBNETVMS, VIDATASTORECONTENTS, VIMGRVCENTERCLOUDRUNTIME, VIVCENTERPORTGROUPS, VIVCENTERDATACENTERS, VIMGRHOSTRUNTIME, - - PLACEMENTGLOBALS, APICCONFIGURATION, CIFTABLE, APICTRANSACTION, VIRTUALSERVICESTATEDBCACHESUMMARY, POOLSTATEDBCACHESUMMARY, - - SERVERSTATEDBCACHESUMMARY, APICAGENTINTERNAL, APICTRANSACTIONFLAP, APICGRAPHINSTANCES, APICEPGS, APICEPGEPS, APICDEVICEPKGVER, APICTENANTS, - - APICVMMDOMAINS, NSXCONFIGURATION, NSXSGTABLE, NSXAGENTINTERNAL, NSXSGINFO, NSXSGIPS, NSXAGENTINTERNALCLI, MAXOBJECTS. - recommendation: - description: - - Recommendation of alertconfig. - rolling_window: - description: - - Only if the number of events is reached or exceeded within the time window will an alert be generated. - - Allowed values are 1-31536000. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - source: - description: - - Signifies system events or the type of client logsused in this alert configuration. - - Enum options - CONN_LOGS, APP_LOGS, EVENT_LOGS, METRICS. - required: true - summary: - description: - - Summary of reason why alert is generated. - tenant_ref: - description: - - It is a reference to an object of type tenant. - threshold: - description: - - An alert is created only when the number of events meets or exceeds this number within the chosen time frame. - - Allowed values are 1-65536. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - throttle: - description: - - Alerts are suppressed (throttled) for this duration of time since the last alert was raised for this alert config. - - Allowed values are 0-31536000. - - Default value when not specified in API or module is interpreted by Avi Controller as 600. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create AlertConfig object - avi_alertconfig: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_alertconfig -""" - -RETURN = ''' -obj: - description: AlertConfig (api/alertconfig) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - action_group_ref=dict(type='str',), - alert_rule=dict(type='dict', required=True), - autoscale_alert=dict(type='bool',), - category=dict(type='str', required=True), - description=dict(type='str',), - enabled=dict(type='bool',), - expiry_time=dict(type='int',), - name=dict(type='str', required=True), - obj_uuid=dict(type='str',), - object_type=dict(type='str',), - recommendation=dict(type='str',), - rolling_window=dict(type='int',), - source=dict(type='str', required=True), - summary=dict(type='str',), - tenant_ref=dict(type='str',), - threshold=dict(type='int',), - throttle=dict(type='int',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'alertconfig', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_alertemailconfig.py b/plugins/modules/network/avi/avi_alertemailconfig.py deleted file mode 100644 index 8ed5db1ad6..0000000000 --- a/plugins/modules/network/avi/avi_alertemailconfig.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_alertemailconfig -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of AlertEmailConfig Avi RESTful Object -description: - - This module is used to configure AlertEmailConfig object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cc_emails: - description: - - Alerts are copied to the comma separated list of email recipients. - description: - description: - - User defined description for the object. - name: - description: - - A user-friendly name of the email notification service. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - to_emails: - description: - - Alerts are sent to the comma separated list of email recipients. - required: true - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create AlertEmailConfig object - avi_alertemailconfig: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_alertemailconfig -""" - -RETURN = ''' -obj: - description: AlertEmailConfig (api/alertemailconfig) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cc_emails=dict(type='str',), - description=dict(type='str',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - to_emails=dict(type='str', required=True), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'alertemailconfig', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_alertscriptconfig.py b/plugins/modules/network/avi/avi_alertscriptconfig.py deleted file mode 100644 index f616be8d6b..0000000000 --- a/plugins/modules/network/avi/avi_alertscriptconfig.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_alertscriptconfig -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of AlertScriptConfig Avi RESTful Object -description: - - This module is used to configure AlertScriptConfig object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - action_script: - description: - - User defined alert action script. - - Please refer to kb.avinetworks.com for more information. - name: - description: - - A user-friendly name of the script. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create Alert Script to perform AWS server autoscaling - avi_alertscriptconfig: - username: '{{ username }}' - controller: '{{ controller }}' - password: '{{ password }}' - action_script: "echo Hello" - name: AWS-Launch-Script - tenant_ref: Demo -""" - -RETURN = ''' -obj: - description: AlertScriptConfig (api/alertscriptconfig) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - action_script=dict(type='str',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'alertscriptconfig', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_alertsyslogconfig.py b/plugins/modules/network/avi/avi_alertsyslogconfig.py deleted file mode 100644 index 1a71f3d587..0000000000 --- a/plugins/modules/network/avi/avi_alertsyslogconfig.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_alertsyslogconfig -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of AlertSyslogConfig Avi RESTful Object -description: - - This module is used to configure AlertSyslogConfig object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - description: - description: - - User defined description for alert syslog config. - name: - description: - - A user-friendly name of the syslog notification. - required: true - syslog_servers: - description: - - The list of syslog servers. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create Alert Syslog object to forward all events to external syslog server - avi_alertsyslogconfig: - controller: '{{ controller }}' - name: Roberts-syslog - password: '{{ password }}' - syslog_servers: - - syslog_server: 10.10.0.100 - syslog_server_port: 514 - udp: true - tenant_ref: admin - username: '{{ username }}' -""" - -RETURN = ''' -obj: - description: AlertSyslogConfig (api/alertsyslogconfig) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - description=dict(type='str',), - name=dict(type='str', required=True), - syslog_servers=dict(type='list',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'alertsyslogconfig', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_analyticsprofile.py b/plugins/modules/network/avi/avi_analyticsprofile.py deleted file mode 100644 index 866ee9c306..0000000000 --- a/plugins/modules/network/avi/avi_analyticsprofile.py +++ /dev/null @@ -1,611 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_analyticsprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of AnalyticsProfile Avi RESTful Object -description: - - This module is used to configure AnalyticsProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - apdex_response_threshold: - description: - - If a client receives an http response in less than the satisfactory latency threshold, the request is considered satisfied. - - It is considered tolerated if it is not satisfied and less than tolerated latency factor multiplied by the satisfactory latency threshold. - - Greater than this number and the client's request is considered frustrated. - - Allowed values are 1-30000. - - Default value when not specified in API or module is interpreted by Avi Controller as 500. - apdex_response_tolerated_factor: - description: - - Client tolerated response latency factor. - - Client must receive a response within this factor times the satisfactory threshold (apdex_response_threshold) to be considered tolerated. - - Allowed values are 1-1000. - - Default value when not specified in API or module is interpreted by Avi Controller as 4.0. - apdex_rtt_threshold: - description: - - Satisfactory client to avi round trip time(rtt). - - Allowed values are 1-2000. - - Default value when not specified in API or module is interpreted by Avi Controller as 250. - apdex_rtt_tolerated_factor: - description: - - Tolerated client to avi round trip time(rtt) factor. - - It is a multiple of apdex_rtt_tolerated_factor. - - Allowed values are 1-1000. - - Default value when not specified in API or module is interpreted by Avi Controller as 4.0. - apdex_rum_threshold: - description: - - If a client is able to load a page in less than the satisfactory latency threshold, the pageload is considered satisfied. - - It is considered tolerated if it is greater than satisfied but less than the tolerated latency multiplied by satisfied latency. - - Greater than this number and the client's request is considered frustrated. - - A pageload includes the time for dns lookup, download of all http objects, and page render time. - - Allowed values are 1-30000. - - Default value when not specified in API or module is interpreted by Avi Controller as 5000. - apdex_rum_tolerated_factor: - description: - - Virtual service threshold factor for tolerated page load time (plt) as multiple of apdex_rum_threshold. - - Allowed values are 1-1000. - - Default value when not specified in API or module is interpreted by Avi Controller as 4.0. - apdex_server_response_threshold: - description: - - A server http response is considered satisfied if latency is less than the satisfactory latency threshold. - - The response is considered tolerated when it is greater than satisfied but less than the tolerated latency factor * s_latency. - - Greater than this number and the server response is considered frustrated. - - Allowed values are 1-30000. - - Default value when not specified in API or module is interpreted by Avi Controller as 400. - apdex_server_response_tolerated_factor: - description: - - Server tolerated response latency factor. - - Servermust response within this factor times the satisfactory threshold (apdex_server_response_threshold) to be considered tolerated. - - Allowed values are 1-1000. - - Default value when not specified in API or module is interpreted by Avi Controller as 4.0. - apdex_server_rtt_threshold: - description: - - Satisfactory client to avi round trip time(rtt). - - Allowed values are 1-2000. - - Default value when not specified in API or module is interpreted by Avi Controller as 125. - apdex_server_rtt_tolerated_factor: - description: - - Tolerated client to avi round trip time(rtt) factor. - - It is a multiple of apdex_rtt_tolerated_factor. - - Allowed values are 1-1000. - - Default value when not specified in API or module is interpreted by Avi Controller as 4.0. - client_log_config: - description: - - Configure which logs are sent to the avi controller from ses and how they are processed. - client_log_streaming_config: - description: - - Configure to stream logs to an external server. - - Field introduced in 17.1.1. - conn_lossy_ooo_threshold: - description: - - A connection between client and avi is considered lossy when more than this percentage of out of order packets are received. - - Allowed values are 1-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 50. - conn_lossy_timeo_rexmt_threshold: - description: - - A connection between client and avi is considered lossy when more than this percentage of packets are retransmitted due to timeout. - - Allowed values are 1-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 20. - conn_lossy_total_rexmt_threshold: - description: - - A connection between client and avi is considered lossy when more than this percentage of packets are retransmitted. - - Allowed values are 1-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 50. - conn_lossy_zero_win_size_event_threshold: - description: - - A client connection is considered lossy when percentage of times a packet could not be transmitted due to tcp zero window is above this threshold. - - Allowed values are 0-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 2. - conn_server_lossy_ooo_threshold: - description: - - A connection between avi and server is considered lossy when more than this percentage of out of order packets are received. - - Allowed values are 1-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 50. - conn_server_lossy_timeo_rexmt_threshold: - description: - - A connection between avi and server is considered lossy when more than this percentage of packets are retransmitted due to timeout. - - Allowed values are 1-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 20. - conn_server_lossy_total_rexmt_threshold: - description: - - A connection between avi and server is considered lossy when more than this percentage of packets are retransmitted. - - Allowed values are 1-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 50. - conn_server_lossy_zero_win_size_event_threshold: - description: - - A server connection is considered lossy when percentage of times a packet could not be transmitted due to tcp zero window is above this threshold. - - Allowed values are 0-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 2. - description: - description: - - User defined description for the object. - disable_ondemand_metrics: - description: - - Virtual service (vs) metrics are processed only when there is live data traffic on the vs. - - In case, vs is idle for a period of time as specified by ondemand_metrics_idle_timeout then metrics processing is suspended for that vs. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - disable_se_analytics: - description: - - Disable node (service engine) level analytics forvs metrics. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - disable_server_analytics: - description: - - Disable analytics on backend servers. - - This may be desired in container environment when there are large number of ephemeral servers. - - Additionally, no healthscore of servers is computed when server analytics is disabled. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - disable_vs_analytics: - description: - - Disable virtualservice (frontend) analytics. - - This flag disables metrics and healthscore for virtualservice. - - Field introduced in 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - enable_advanced_analytics: - description: - - Enables advanced analytics features like anomaly detection. - - If set to false, anomaly computation (and associated rules/events) for vs, pool and server metrics will be disabled. - - However, setting it to false reduces cpu and memory requirements for analytics subsystem. - - Field introduced in 17.2.13, 18.1.5, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - exclude_client_close_before_request_as_error: - description: - - Exclude client closed connection before an http request could be completed from being classified as an error. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_dns_policy_drop_as_significant: - description: - - Exclude dns policy drops from the list of errors. - - Field introduced in 17.2.2. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_gs_down_as_error: - description: - - Exclude queries to gslb services that are operationally down from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_http_error_codes: - description: - - List of http status codes to be excluded from being classified as an error. - - Error connections or responses impacts health score, are included as significant logs, and may be classified as part of a dos attack. - exclude_invalid_dns_domain_as_error: - description: - - Exclude dns queries to domains outside the domains configured in the dns application profile from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_invalid_dns_query_as_error: - description: - - Exclude invalid dns queries from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_no_dns_record_as_error: - description: - - Exclude queries to domains that did not have configured services/records from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_no_valid_gs_member_as_error: - description: - - Exclude queries to gslb services that have no available members from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_persistence_change_as_error: - description: - - Exclude persistence server changed while load balancing' from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_server_dns_error_as_error: - description: - - Exclude server dns error response from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_server_tcp_reset_as_error: - description: - - Exclude server tcp reset from errors. - - It is common for applications like ms exchange. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_sip_error_codes: - description: - - List of sip status codes to be excluded from being classified as an error. - - Field introduced in 17.2.13, 18.1.5, 18.2.1. - exclude_syn_retransmit_as_error: - description: - - Exclude 'server unanswered syns' from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_tcp_reset_as_error: - description: - - Exclude tcp resets by client from the list of potential errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - exclude_unsupported_dns_query_as_error: - description: - - Exclude unsupported dns queries from the list of errors. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - healthscore_max_server_limit: - description: - - Skips health score computation of pool servers when number of servers in a pool is more than this setting. - - Allowed values are 0-5000. - - Special values are 0- 'server health score is disabled'. - - Field introduced in 17.2.13, 18.1.4. - - Default value when not specified in API or module is interpreted by Avi Controller as 20. - hs_event_throttle_window: - description: - - Time window (in secs) within which only unique health change events should occur. - - Default value when not specified in API or module is interpreted by Avi Controller as 1209600. - hs_max_anomaly_penalty: - description: - - Maximum penalty that may be deducted from health score for anomalies. - - Allowed values are 0-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - hs_max_resources_penalty: - description: - - Maximum penalty that may be deducted from health score for high resource utilization. - - Allowed values are 0-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 25. - hs_max_security_penalty: - description: - - Maximum penalty that may be deducted from health score based on security assessment. - - Allowed values are 0-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 100. - hs_min_dos_rate: - description: - - Dos connection rate below which the dos security assessment will not kick in. - - Default value when not specified in API or module is interpreted by Avi Controller as 1000. - hs_performance_boost: - description: - - Adds free performance score credits to health score. - - It can be used for compensating health score for known slow applications. - - Allowed values are 0-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - hs_pscore_traffic_threshold_l4_client: - description: - - Threshold number of connections in 5min, below which apdexr, apdexc, rum_apdex, and other network quality metrics are not computed. - - Default value when not specified in API or module is interpreted by Avi Controller as 10.0. - hs_pscore_traffic_threshold_l4_server: - description: - - Threshold number of connections in 5min, below which apdexr, apdexc, rum_apdex, and other network quality metrics are not computed. - - Default value when not specified in API or module is interpreted by Avi Controller as 10.0. - hs_security_certscore_expired: - description: - - Score assigned when the certificate has expired. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 0.0. - hs_security_certscore_gt30d: - description: - - Score assigned when the certificate expires in more than 30 days. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 5.0. - hs_security_certscore_le07d: - description: - - Score assigned when the certificate expires in less than or equal to 7 days. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 2.0. - hs_security_certscore_le30d: - description: - - Score assigned when the certificate expires in less than or equal to 30 days. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 4.0. - hs_security_chain_invalidity_penalty: - description: - - Penalty for allowing certificates with invalid chain. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 1.0. - hs_security_cipherscore_eq000b: - description: - - Score assigned when the minimum cipher strength is 0 bits. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 0.0. - hs_security_cipherscore_ge128b: - description: - - Score assigned when the minimum cipher strength is greater than equal to 128 bits. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 5.0. - hs_security_cipherscore_lt128b: - description: - - Score assigned when the minimum cipher strength is less than 128 bits. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 3.5. - hs_security_encalgo_score_none: - description: - - Score assigned when no algorithm is used for encryption. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 0.0. - hs_security_encalgo_score_rc4: - description: - - Score assigned when rc4 algorithm is used for encryption. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 2.5. - hs_security_hsts_penalty: - description: - - Penalty for not enabling hsts. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 1.0. - hs_security_nonpfs_penalty: - description: - - Penalty for allowing non-pfs handshakes. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 1.0. - hs_security_selfsignedcert_penalty: - description: - - Deprecated. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 1.0. - hs_security_ssl30_score: - description: - - Score assigned when supporting ssl3.0 encryption protocol. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 3.5. - hs_security_tls10_score: - description: - - Score assigned when supporting tls1.0 encryption protocol. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 5.0. - hs_security_tls11_score: - description: - - Score assigned when supporting tls1.1 encryption protocol. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 5.0. - hs_security_tls12_score: - description: - - Score assigned when supporting tls1.2 encryption protocol. - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 5.0. - hs_security_weak_signature_algo_penalty: - description: - - Penalty for allowing weak signature algorithm(s). - - Allowed values are 0-5. - - Default value when not specified in API or module is interpreted by Avi Controller as 1.0. - name: - description: - - The name of the analytics profile. - required: true - ondemand_metrics_idle_timeout: - description: - - This flag sets the time duration of no live data traffic after which virtual service metrics processing is suspended. - - It is applicable only when disable_ondemand_metrics is set to false. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 1800. - ranges: - description: - - List of http status code ranges to be excluded from being classified as an error. - resp_code_block: - description: - - Block of http response codes to be excluded from being classified as an error. - - Enum options - AP_HTTP_RSP_4XX, AP_HTTP_RSP_5XX. - sensitive_log_profile: - description: - - Rules applied to the http application log for filtering sensitive information. - - Field introduced in 17.2.10, 18.1.2. - sip_log_depth: - description: - - Maximum number of sip messages added in logs for a sip transaction. - - By default, this value is 20. - - Allowed values are 1-1000. - - Field introduced in 17.2.13, 18.1.5, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 20. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the analytics profile. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create a custom Analytics profile object - avi_analyticsprofile: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - apdex_response_threshold: 500 - apdex_response_tolerated_factor: 4.0 - apdex_rtt_threshold: 250 - apdex_rtt_tolerated_factor: 4.0 - apdex_rum_threshold: 5000 - apdex_rum_tolerated_factor: 4.0 - apdex_server_response_threshold: 400 - apdex_server_response_tolerated_factor: 4.0 - apdex_server_rtt_threshold: 125 - apdex_server_rtt_tolerated_factor: 4.0 - conn_lossy_ooo_threshold: 50 - conn_lossy_timeo_rexmt_threshold: 20 - conn_lossy_total_rexmt_threshold: 50 - conn_lossy_zero_win_size_event_threshold: 2 - conn_server_lossy_ooo_threshold: 50 - conn_server_lossy_timeo_rexmt_threshold: 20 - conn_server_lossy_total_rexmt_threshold: 50 - conn_server_lossy_zero_win_size_event_threshold: 2 - disable_se_analytics: false - disable_server_analytics: false - exclude_client_close_before_request_as_error: false - exclude_persistence_change_as_error: false - exclude_server_tcp_reset_as_error: false - exclude_syn_retransmit_as_error: false - exclude_tcp_reset_as_error: false - hs_event_throttle_window: 1209600 - hs_max_anomaly_penalty: 10 - hs_max_resources_penalty: 25 - hs_max_security_penalty: 100 - hs_min_dos_rate: 1000 - hs_performance_boost: 20 - hs_pscore_traffic_threshold_l4_client: 10.0 - hs_pscore_traffic_threshold_l4_server: 10.0 - hs_security_certscore_expired: 0.0 - hs_security_certscore_gt30d: 5.0 - hs_security_certscore_le07d: 2.0 - hs_security_certscore_le30d: 4.0 - hs_security_chain_invalidity_penalty: 1.0 - hs_security_cipherscore_eq000b: 0.0 - hs_security_cipherscore_ge128b: 5.0 - hs_security_cipherscore_lt128b: 3.5 - hs_security_encalgo_score_none: 0.0 - hs_security_encalgo_score_rc4: 2.5 - hs_security_hsts_penalty: 0.0 - hs_security_nonpfs_penalty: 1.0 - hs_security_selfsignedcert_penalty: 1.0 - hs_security_ssl30_score: 3.5 - hs_security_tls10_score: 5.0 - hs_security_tls11_score: 5.0 - hs_security_tls12_score: 5.0 - hs_security_weak_signature_algo_penalty: 1.0 - name: jason-analytics-profile - tenant_ref: Demo -""" - -RETURN = ''' -obj: - description: AnalyticsProfile (api/analyticsprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - apdex_response_threshold=dict(type='int',), - apdex_response_tolerated_factor=dict(type='float',), - apdex_rtt_threshold=dict(type='int',), - apdex_rtt_tolerated_factor=dict(type='float',), - apdex_rum_threshold=dict(type='int',), - apdex_rum_tolerated_factor=dict(type='float',), - apdex_server_response_threshold=dict(type='int',), - apdex_server_response_tolerated_factor=dict(type='float',), - apdex_server_rtt_threshold=dict(type='int',), - apdex_server_rtt_tolerated_factor=dict(type='float',), - client_log_config=dict(type='dict',), - client_log_streaming_config=dict(type='dict',), - conn_lossy_ooo_threshold=dict(type='int',), - conn_lossy_timeo_rexmt_threshold=dict(type='int',), - conn_lossy_total_rexmt_threshold=dict(type='int',), - conn_lossy_zero_win_size_event_threshold=dict(type='int',), - conn_server_lossy_ooo_threshold=dict(type='int',), - conn_server_lossy_timeo_rexmt_threshold=dict(type='int',), - conn_server_lossy_total_rexmt_threshold=dict(type='int',), - conn_server_lossy_zero_win_size_event_threshold=dict(type='int',), - description=dict(type='str',), - disable_ondemand_metrics=dict(type='bool',), - disable_se_analytics=dict(type='bool',), - disable_server_analytics=dict(type='bool',), - disable_vs_analytics=dict(type='bool',), - enable_advanced_analytics=dict(type='bool',), - exclude_client_close_before_request_as_error=dict(type='bool',), - exclude_dns_policy_drop_as_significant=dict(type='bool',), - exclude_gs_down_as_error=dict(type='bool',), - exclude_http_error_codes=dict(type='list',), - exclude_invalid_dns_domain_as_error=dict(type='bool',), - exclude_invalid_dns_query_as_error=dict(type='bool',), - exclude_no_dns_record_as_error=dict(type='bool',), - exclude_no_valid_gs_member_as_error=dict(type='bool',), - exclude_persistence_change_as_error=dict(type='bool',), - exclude_server_dns_error_as_error=dict(type='bool',), - exclude_server_tcp_reset_as_error=dict(type='bool',), - exclude_sip_error_codes=dict(type='list',), - exclude_syn_retransmit_as_error=dict(type='bool',), - exclude_tcp_reset_as_error=dict(type='bool',), - exclude_unsupported_dns_query_as_error=dict(type='bool',), - healthscore_max_server_limit=dict(type='int',), - hs_event_throttle_window=dict(type='int',), - hs_max_anomaly_penalty=dict(type='int',), - hs_max_resources_penalty=dict(type='int',), - hs_max_security_penalty=dict(type='int',), - hs_min_dos_rate=dict(type='int',), - hs_performance_boost=dict(type='int',), - hs_pscore_traffic_threshold_l4_client=dict(type='float',), - hs_pscore_traffic_threshold_l4_server=dict(type='float',), - hs_security_certscore_expired=dict(type='float',), - hs_security_certscore_gt30d=dict(type='float',), - hs_security_certscore_le07d=dict(type='float',), - hs_security_certscore_le30d=dict(type='float',), - hs_security_chain_invalidity_penalty=dict(type='float',), - hs_security_cipherscore_eq000b=dict(type='float',), - hs_security_cipherscore_ge128b=dict(type='float',), - hs_security_cipherscore_lt128b=dict(type='float',), - hs_security_encalgo_score_none=dict(type='float',), - hs_security_encalgo_score_rc4=dict(type='float',), - hs_security_hsts_penalty=dict(type='float',), - hs_security_nonpfs_penalty=dict(type='float',), - hs_security_selfsignedcert_penalty=dict(type='float',), - hs_security_ssl30_score=dict(type='float',), - hs_security_tls10_score=dict(type='float',), - hs_security_tls11_score=dict(type='float',), - hs_security_tls12_score=dict(type='float',), - hs_security_weak_signature_algo_penalty=dict(type='float',), - name=dict(type='str', required=True), - ondemand_metrics_idle_timeout=dict(type='int',), - ranges=dict(type='list',), - resp_code_block=dict(type='list',), - sensitive_log_profile=dict(type='dict',), - sip_log_depth=dict(type='int',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'analyticsprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_api_session.py b/plugins/modules/network/avi/avi_api_session.py deleted file mode 100644 index b46bb06fd4..0000000000 --- a/plugins/modules/network/avi/avi_api_session.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/python -""" -# Created on Aug 12, 2016 -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) GitHub ID: grastogi23 -# -# module_check: not supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -""" - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: avi_api_session -author: Gaurav Rastogi (@grastogi23) - -short_description: Avi API Module -description: - - This module can be used for calling any resources defined in Avi REST API. U(https://avinetworks.com/) - - This module is useful for invoking HTTP Patch methods and accessing resources that do not have an REST object associated with them. -requirements: [ avisdk ] -options: - http_method: - description: - - Allowed HTTP methods for RESTful services and are supported by Avi Controller. - choices: ["get", "put", "post", "patch", "delete"] - required: true - data: - description: - - HTTP body in YAML or JSON format. - params: - description: - - Query parameters passed to the HTTP API. - path: - description: - - 'Path for Avi API resource. For example, C(path: virtualservice) will translate to C(api/virtualserivce).' - timeout: - description: - - Timeout (in seconds) for Avi API calls. - default: 60 -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = ''' - - - name: Get Pool Information using avi_api_session - avi_api_session: - controller: "{{ controller }}" - username: "{{ username }}" - password: "{{ password }}" - http_method: get - path: pool - params: - name: "{{ pool_name }}" - api_version: 16.4 - register: pool_results - - - name: Patch Pool with list of servers - avi_api_session: - controller: "{{ controller }}" - username: "{{ username }}" - password: "{{ password }}" - http_method: patch - path: "{{ pool_path }}" - api_version: 16.4 - data: - add: - servers: - - ip: - addr: 10.10.10.10 - type: V4 - - ip: - addr: 20.20.20.20 - type: V4 - register: updated_pool - - - name: Fetch Pool metrics bandwidth and connections rate - avi_api_session: - controller: "{{ controller }}" - username: "{{ username }}" - password: "{{ password }}" - http_method: get - path: analytics/metrics/pool - api_version: 16.4 - params: - name: "{{ pool_name }}" - metric_id: l4_server.avg_bandwidth,l4_server.avg_complete_conns - step: 300 - limit: 10 - register: pool_metrics - -''' - - -RETURN = ''' -obj: - description: Avi REST resource - returned: success, changed - type: dict -''' - - -import json -import time -from ansible.module_utils.basic import AnsibleModule -from copy import deepcopy - -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, ansible_return, avi_obj_cmp, - cleanup_absent_fields, HAS_AVI) - from ansible_collections.community.general.plugins.module_utils.network.avi.avi_api import ( - ApiSession, AviCredentials) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - http_method=dict(required=True, - choices=['get', 'put', 'post', 'patch', - 'delete']), - path=dict(type='str', required=True), - params=dict(type='dict'), - data=dict(type='jsonarg'), - timeout=dict(type='int', default=60) - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule(argument_spec=argument_specs) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - api_creds = AviCredentials() - api_creds.update_from_ansible_module(module) - api = ApiSession.get_session( - api_creds.controller, api_creds.username, password=api_creds.password, - timeout=api_creds.timeout, tenant=api_creds.tenant, - tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, - port=api_creds.port) - - tenant_uuid = api_creds.tenant_uuid - tenant = api_creds.tenant - timeout = int(module.params.get('timeout')) - # path is a required argument - path = module.params.get('path', '') - params = module.params.get('params', None) - data = module.params.get('data', None) - # Get the api_version from module. - api_version = api_creds.api_version - if data is not None: - data = json.loads(data) - method = module.params['http_method'] - - existing_obj = None - changed = method != 'get' - gparams = deepcopy(params) if params else {} - gparams.update({'include_refs': '', 'include_name': ''}) - - # API methods not allowed - api_get_not_allowed = ["cluster", "gslbsiteops"] - api_post_not_allowed = ["alert", "fileservice"] - api_put_not_allowed = ["backup"] - - if method == 'post' and not any(path.startswith(uri) for uri in api_post_not_allowed): - # TODO: Above condition should be updated after AV-38981 is fixed - # need to check if object already exists. In that case - # change the method to be put - try: - using_collection = False - if not any(path.startswith(uri) for uri in api_get_not_allowed): - if 'name' in data: - gparams['name'] = data['name'] - using_collection = True - if not any(path.startswith(uri) for uri in api_get_not_allowed): - rsp = api.get(path, tenant=tenant, tenant_uuid=tenant_uuid, - params=gparams, api_version=api_version) - existing_obj = rsp.json() - if using_collection: - existing_obj = existing_obj['results'][0] - except (IndexError, KeyError): - # object is not found - pass - else: - if not any(path.startswith(uri) for uri in api_get_not_allowed): - # object is present - method = 'put' - path += '/' + existing_obj['uuid'] - - if method == 'put' and not any(path.startswith(uri) for uri in api_put_not_allowed): - # put can happen with when full path is specified or it is put + post - if existing_obj is None: - using_collection = False - if ((len(path.split('/')) == 1) and ('name' in data) and - (not any(path.startswith(uri) for uri in api_get_not_allowed))): - gparams['name'] = data['name'] - using_collection = True - rsp = api.get(path, tenant=tenant, tenant_uuid=tenant_uuid, - params=gparams, api_version=api_version) - rsp_data = rsp.json() - if using_collection: - if rsp_data['results']: - existing_obj = rsp_data['results'][0] - path += '/' + existing_obj['uuid'] - else: - method = 'post' - else: - if rsp.status_code == 404: - method = 'post' - else: - existing_obj = rsp_data - if existing_obj: - changed = not avi_obj_cmp(data, existing_obj) - cleanup_absent_fields(data) - if method == 'patch': - rsp = api.get(path, tenant=tenant, tenant_uuid=tenant_uuid, - params=gparams, api_version=api_version) - existing_obj = rsp.json() - - if (method == 'put' and changed) or (method != 'put'): - fn = getattr(api, method) - rsp = fn(path, tenant=tenant, tenant_uuid=tenant, timeout=timeout, - params=params, data=data, api_version=api_version) - else: - rsp = None - if method == 'delete' and rsp.status_code == 404: - changed = False - rsp.status_code = 200 - if method == 'patch' and existing_obj and rsp.status_code < 299: - # Ideally the comparison should happen with the return values - # from the patch API call. However, currently Avi API are - # returning different hostname when GET is used vs Patch. - # tracked as AV-12561 - if path.startswith('pool'): - time.sleep(1) - gparams = deepcopy(params) if params else {} - gparams.update({'include_refs': '', 'include_name': ''}) - rsp = api.get(path, tenant=tenant, tenant_uuid=tenant_uuid, - params=gparams, api_version=api_version) - new_obj = rsp.json() - changed = not avi_obj_cmp(new_obj, existing_obj) - if rsp is None: - return module.exit_json(changed=changed, obj=existing_obj) - return ansible_return(module, rsp, changed, req=data) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_api_version.py b/plugins/modules/network/avi/avi_api_version.py deleted file mode 100644 index b509483cc2..0000000000 --- a/plugins/modules/network/avi/avi_api_version.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/python -""" -# Created on July 24, 2017 -# -# @author: Vilian Atmadzhov (vilian.atmadzhov@paddypowerbetfair.com) GitHub ID: vivobg -# -# module_check: not supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# Vilian Atmadzhov, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -""" - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: avi_api_version -author: Vilian Atmadzhov (@vivobg) - -short_description: Avi API Version Module -description: - - This module can be used to obtain the version of the Avi REST API. U(https://avinetworks.com/) -requirements: [ avisdk ] -options: {} -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = ''' - - name: Get AVI API version - avi_api_version: - controller: "" - username: "" - password: "" - tenant: "" - register: avi_controller_version -''' - - -RETURN = ''' -obj: - description: Avi REST resource - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule - -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, ansible_return, HAS_AVI) - from ansible_collections.community.general.plugins.module_utils.network.avi.avi_api import ( - ApiSession, AviCredentials) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict() - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule(argument_spec=argument_specs) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - try: - api_creds = AviCredentials() - api_creds.update_from_ansible_module(module) - api = ApiSession.get_session( - api_creds.controller, api_creds.username, - password=api_creds.password, - timeout=api_creds.timeout, tenant=api_creds.tenant, - tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, - port=api_creds.port) - - remote_api_version = api.remote_api_version - remote = {} - for key in remote_api_version.keys(): - remote[key.lower()] = remote_api_version[key] - api.close() - module.exit_json(changed=False, obj=remote) - except Exception as e: - module.fail_json(msg=("Unable to get an AVI session. %s" % e)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_applicationpersistenceprofile.py b/plugins/modules/network/avi/avi_applicationpersistenceprofile.py deleted file mode 100644 index 24072d0f4a..0000000000 --- a/plugins/modules/network/avi/avi_applicationpersistenceprofile.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_applicationpersistenceprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ApplicationPersistenceProfile Avi RESTful Object -description: - - This module is used to configure ApplicationPersistenceProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - app_cookie_persistence_profile: - description: - - Specifies the application cookie persistence profile parameters. - description: - description: - - User defined description for the object. - hdr_persistence_profile: - description: - - Specifies the custom http header persistence profile parameters. - http_cookie_persistence_profile: - description: - - Specifies the http cookie persistence profile parameters. - ip_persistence_profile: - description: - - Specifies the client ip persistence profile parameters. - is_federated: - description: - - This field describes the object's replication scope. - - If the field is set to false, then the object is visible within the controller-cluster and its associated service-engines. - - If the field is set to true, then the object is replicated across the federation. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - name: - description: - - A user-friendly name for the persistence profile. - required: true - persistence_type: - description: - - Method used to persist clients to the same server for a duration of time or a session. - - Enum options - PERSISTENCE_TYPE_CLIENT_IP_ADDRESS, PERSISTENCE_TYPE_HTTP_COOKIE, PERSISTENCE_TYPE_TLS, PERSISTENCE_TYPE_CLIENT_IPV6_ADDRESS, - - PERSISTENCE_TYPE_CUSTOM_HTTP_HEADER, PERSISTENCE_TYPE_APP_COOKIE, PERSISTENCE_TYPE_GSLB_SITE. - - Default value when not specified in API or module is interpreted by Avi Controller as PERSISTENCE_TYPE_CLIENT_IP_ADDRESS. - required: true - server_hm_down_recovery: - description: - - Specifies behavior when a persistent server has been marked down by a health monitor. - - Enum options - HM_DOWN_PICK_NEW_SERVER, HM_DOWN_ABORT_CONNECTION, HM_DOWN_CONTINUE_PERSISTENT_SERVER. - - Default value when not specified in API or module is interpreted by Avi Controller as HM_DOWN_PICK_NEW_SERVER. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the persistence profile. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create an Application Persistence setting using http cookie. - avi_applicationpersistenceprofile: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - http_cookie_persistence_profile: - always_send_cookie: false - cookie_name: My-HTTP - key: - - aes_key: ShYGZdMks8j6Bpvm2sCvaXWzvXms2Z9ob+TTjRy46lQ= - name: c1276819-550c-4adf-912d-59efa5fd7269 - - aes_key: OGsyVk84VCtyMENFOW0rMnRXVnNrb0RzdG5mT29oamJRb0dlbHZVSjR1az0= - name: a080de57-77c3-4580-a3ea-e7a6493c14fd - - aes_key: UVN0cU9HWmFUM2xOUzBVcmVXaHFXbnBLVUUxMU1VSktSVU5HWjJOWmVFMTBUMUV4UmxsNk4xQmFZejA9 - name: 60478846-33c6-484d-868d-bbc324fce4a5 - timeout: 15 - name: My-HTTP-Cookie - persistence_type: PERSISTENCE_TYPE_HTTP_COOKIE - server_hm_down_recovery: HM_DOWN_PICK_NEW_SERVER - tenant_ref: Demo -""" - -RETURN = ''' -obj: - description: ApplicationPersistenceProfile (api/applicationpersistenceprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - app_cookie_persistence_profile=dict(type='dict',), - description=dict(type='str',), - hdr_persistence_profile=dict(type='dict',), - http_cookie_persistence_profile=dict(type='dict',), - ip_persistence_profile=dict(type='dict',), - is_federated=dict(type='bool',), - name=dict(type='str', required=True), - persistence_type=dict(type='str', required=True), - server_hm_down_recovery=dict(type='str',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'applicationpersistenceprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_applicationprofile.py b/plugins/modules/network/avi/avi_applicationprofile.py deleted file mode 100644 index 10f8b0038f..0000000000 --- a/plugins/modules/network/avi/avi_applicationprofile.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_applicationprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ApplicationProfile Avi RESTful Object -description: - - This module is used to configure ApplicationProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cloud_config_cksum: - description: - - Checksum of application profiles. - - Internally set by cloud connector. - - Field introduced in 17.2.14, 18.1.5, 18.2.1. - created_by: - description: - - Name of the application profile creator. - - Field introduced in 17.2.14, 18.1.5, 18.2.1. - description: - description: - - User defined description for the object. - dns_service_profile: - description: - - Specifies various dns service related controls for virtual service. - dos_rl_profile: - description: - - Specifies various security related controls for virtual service. - http_profile: - description: - - Specifies the http application proxy profile parameters. - name: - description: - - The name of the application profile. - required: true - preserve_client_ip: - description: - - Specifies if client ip needs to be preserved for backend connection. - - Not compatible with connection multiplexing. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - preserve_client_port: - description: - - Specifies if we need to preserve client port while preserving client ip for backend connections. - - Field introduced in 17.2.7. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - sip_service_profile: - description: - - Specifies various sip service related controls for virtual service. - - Field introduced in 17.2.8, 18.1.3, 18.2.1. - tcp_app_profile: - description: - - Specifies the tcp application proxy profile parameters. - tenant_ref: - description: - - It is a reference to an object of type tenant. - type: - description: - - Specifies which application layer proxy is enabled for the virtual service. - - Enum options - APPLICATION_PROFILE_TYPE_L4, APPLICATION_PROFILE_TYPE_HTTP, APPLICATION_PROFILE_TYPE_SYSLOG, APPLICATION_PROFILE_TYPE_DNS, - - APPLICATION_PROFILE_TYPE_SSL, APPLICATION_PROFILE_TYPE_SIP. - required: true - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the application profile. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create an Application Profile for HTTP application enabled for SSL traffic - avi_applicationprofile: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - http_profile: - cache_config: - age_header: true - aggressive: false - date_header: true - default_expire: 600 - enabled: false - heuristic_expire: false - max_cache_size: 0 - max_object_size: 4194304 - mime_types_group_refs: - - admin:System-Cacheable-Resource-Types - min_object_size: 100 - query_cacheable: false - xcache_header: true - client_body_timeout: 0 - client_header_timeout: 10000 - client_max_body_size: 0 - client_max_header_size: 12 - client_max_request_size: 48 - compression_profile: - compressible_content_ref: admin:System-Compressible-Content-Types - compression: false - remove_accept_encoding_header: true - type: AUTO_COMPRESSION - connection_multiplexing_enabled: true - hsts_enabled: false - hsts_max_age: 365 - http_to_https: false - httponly_enabled: false - keepalive_header: false - keepalive_timeout: 30000 - max_bad_rps_cip: 0 - max_bad_rps_cip_uri: 0 - max_bad_rps_uri: 0 - max_rps_cip: 0 - max_rps_cip_uri: 0 - max_rps_unknown_cip: 0 - max_rps_unknown_uri: 0 - max_rps_uri: 0 - post_accept_timeout: 30000 - secure_cookie_enabled: false - server_side_redirect_to_https: false - spdy_enabled: false - spdy_fwd_proxy_mode: false - ssl_client_certificate_mode: SSL_CLIENT_CERTIFICATE_NONE - ssl_everywhere_enabled: false - websockets_enabled: true - x_forwarded_proto_enabled: false - xff_alternate_name: X-Forwarded-For - xff_enabled: true - name: System-HTTP - tenant_ref: admin - type: APPLICATION_PROFILE_TYPE_HTTP -""" - -RETURN = ''' -obj: - description: ApplicationProfile (api/applicationprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cloud_config_cksum=dict(type='str',), - created_by=dict(type='str',), - description=dict(type='str',), - dns_service_profile=dict(type='dict',), - dos_rl_profile=dict(type='dict',), - http_profile=dict(type='dict',), - name=dict(type='str', required=True), - preserve_client_ip=dict(type='bool',), - preserve_client_port=dict(type='bool',), - sip_service_profile=dict(type='dict',), - tcp_app_profile=dict(type='dict',), - tenant_ref=dict(type='str',), - type=dict(type='str', required=True), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'applicationprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_authprofile.py b/plugins/modules/network/avi/avi_authprofile.py deleted file mode 100644 index dd5b2f3a0d..0000000000 --- a/plugins/modules/network/avi/avi_authprofile.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_authprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of AuthProfile Avi RESTful Object -description: - - This module is used to configure AuthProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - description: - description: - - User defined description for the object. - http: - description: - - Http user authentication params. - ldap: - description: - - Ldap server and directory settings. - name: - description: - - Name of the auth profile. - required: true - pa_agent_ref: - description: - - Pingaccessagent uuid. - - It is a reference to an object of type pingaccessagent. - - Field introduced in 18.2.3. - saml: - description: - - Saml settings. - - Field introduced in 17.2.3. - tacacs_plus: - description: - - Tacacs+ settings. - tenant_ref: - description: - - It is a reference to an object of type tenant. - type: - description: - - Type of the auth profile. - - Enum options - AUTH_PROFILE_LDAP, AUTH_PROFILE_TACACS_PLUS, AUTH_PROFILE_SAML, AUTH_PROFILE_PINGACCESS. - required: true - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the auth profile. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create user authorization profile based on the LDAP - avi_authprofile: - controller: '{{ controller }}' - password: '{{ password }}' - username: '{{ username }}' - http: - cache_expiration_time: 5 - group_member_is_full_dn: false - ldap: - base_dn: dc=avi,dc=local - bind_as_administrator: true - port: 389 - security_mode: AUTH_LDAP_SECURE_NONE - server: - - 10.10.0.100 - settings: - admin_bind_dn: user@avi.local - group_filter: (objectClass=*) - group_member_attribute: member - group_member_is_full_dn: true - group_search_dn: dc=avi,dc=local - group_search_scope: AUTH_LDAP_SCOPE_SUBTREE - ignore_referrals: true - password: password - user_id_attribute: samAccountname - user_search_dn: dc=avi,dc=local - user_search_scope: AUTH_LDAP_SCOPE_ONE - name: ProdAuth - tenant_ref: admin - type: AUTH_PROFILE_LDAP -""" - -RETURN = ''' -obj: - description: AuthProfile (api/authprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - description=dict(type='str',), - http=dict(type='dict',), - ldap=dict(type='dict',), - name=dict(type='str', required=True), - pa_agent_ref=dict(type='str',), - saml=dict(type='dict',), - tacacs_plus=dict(type='dict',), - tenant_ref=dict(type='str',), - type=dict(type='str', required=True), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'authprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_autoscalelaunchconfig.py b/plugins/modules/network/avi/avi_autoscalelaunchconfig.py deleted file mode 100644 index a1655f617d..0000000000 --- a/plugins/modules/network/avi/avi_autoscalelaunchconfig.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_autoscalelaunchconfig -author: Chaitanya Deshpande (@chaitanyaavi) - -short_description: Module for setup of AutoScaleLaunchConfig Avi RESTful Object -description: - - This module is used to configure AutoScaleLaunchConfig object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - description: - description: - - User defined description for the object. - image_id: - description: - - Unique id of the amazon machine image (ami) or openstack vm id. - mesos: - description: - - Autoscalemesossettings settings for autoscalelaunchconfig. - name: - description: - - Name of the object. - required: true - openstack: - description: - - Autoscaleopenstacksettings settings for autoscalelaunchconfig. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - use_external_asg: - description: - - If set to true, serverautoscalepolicy will use the autoscaling group (external_autoscaling_groups) from pool to perform scale up and scale down. - - Pool should have single autoscaling group configured. - - Field introduced in 17.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create an Autoscale Launch configuration. - avi_autoscalelaunchconfig: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - image_id: default - name: default-autoscalelaunchconfig - tenant_ref: admin -""" - -RETURN = ''' -obj: - description: AutoScaleLaunchConfig (api/autoscalelaunchconfig) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - description=dict(type='str',), - image_id=dict(type='str',), - mesos=dict(type='dict',), - name=dict(type='str', required=True), - openstack=dict(type='dict',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - use_external_asg=dict(type='bool',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'autoscalelaunchconfig', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_backup.py b/plugins/modules/network/avi/avi_backup.py deleted file mode 100644 index 8935563b3a..0000000000 --- a/plugins/modules/network/avi/avi_backup.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_backup -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Backup Avi RESTful Object -description: - - This module is used to configure Backup object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - backup_config_ref: - description: - - Backupconfiguration information. - - It is a reference to an object of type backupconfiguration. - file_name: - description: - - The file name of backup. - required: true - local_file_url: - description: - - Url to download the backup file. - remote_file_url: - description: - - Url to download the backup file. - scheduler_ref: - description: - - Scheduler information. - - It is a reference to an object of type scheduler. - tenant_ref: - description: - - It is a reference to an object of type tenant. - timestamp: - description: - - Unix timestamp of when the backup file is created. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create Backup object - avi_backup: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_backup -""" - -RETURN = ''' -obj: - description: Backup (api/backup) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - backup_config_ref=dict(type='str',), - file_name=dict(type='str', required=True), - local_file_url=dict(type='str',), - remote_file_url=dict(type='str',), - scheduler_ref=dict(type='str',), - tenant_ref=dict(type='str',), - timestamp=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'backup', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_backupconfiguration.py b/plugins/modules/network/avi/avi_backupconfiguration.py deleted file mode 100644 index 17f7a755fb..0000000000 --- a/plugins/modules/network/avi/avi_backupconfiguration.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_backupconfiguration -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of BackupConfiguration Avi RESTful Object -description: - - This module is used to configure BackupConfiguration object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - aws_access_key: - description: - - Aws access key id. - - Field introduced in 18.2.3. - aws_bucket_id: - description: - - Aws bucket. - - Field introduced in 18.2.3. - aws_secret_access: - description: - - Aws secret access key. - - Field introduced in 18.2.3. - backup_file_prefix: - description: - - Prefix of the exported configuration file. - - Field introduced in 17.1.1. - backup_passphrase: - description: - - Passphrase of backup configuration. - maximum_backups_stored: - description: - - Rotate the backup files based on this count. - - Allowed values are 1-20. - - Default value when not specified in API or module is interpreted by Avi Controller as 4. - name: - description: - - Name of backup configuration. - required: true - remote_directory: - description: - - Directory at remote destination with write permission for ssh user. - remote_hostname: - description: - - Remote destination. - save_local: - description: - - Local backup. - type: bool - ssh_user_ref: - description: - - Access credentials for remote destination. - - It is a reference to an object of type cloudconnectoruser. - tenant_ref: - description: - - It is a reference to an object of type tenant. - upload_to_remote_host: - description: - - Remote backup. - type: bool - upload_to_s3: - description: - - Cloud backup. - - Field introduced in 18.2.3. - type: bool - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create BackupConfiguration object - avi_backupconfiguration: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_backupconfiguration -""" - -RETURN = ''' -obj: - description: BackupConfiguration (api/backupconfiguration) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - aws_access_key=dict(type='str', no_log=True,), - aws_bucket_id=dict(type='str',), - aws_secret_access=dict(type='str', no_log=True,), - backup_file_prefix=dict(type='str',), - backup_passphrase=dict(type='str', no_log=True,), - maximum_backups_stored=dict(type='int',), - name=dict(type='str', required=True), - remote_directory=dict(type='str',), - remote_hostname=dict(type='str',), - save_local=dict(type='bool',), - ssh_user_ref=dict(type='str',), - tenant_ref=dict(type='str',), - upload_to_remote_host=dict(type='bool',), - upload_to_s3=dict(type='bool',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'backupconfiguration', - set(['backup_passphrase', 'aws_access_key', 'aws_secret_access'])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_certificatemanagementprofile.py b/plugins/modules/network/avi/avi_certificatemanagementprofile.py deleted file mode 100644 index b704c410cf..0000000000 --- a/plugins/modules/network/avi/avi_certificatemanagementprofile.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_certificatemanagementprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of CertificateManagementProfile Avi RESTful Object -description: - - This module is used to configure CertificateManagementProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - name: - description: - - Name of the pki profile. - required: true - script_params: - description: - - List of customparams. - script_path: - description: - - Script_path of certificatemanagementprofile. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create CertificateManagementProfile object - avi_certificatemanagementprofile: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_certificatemanagementprofile -""" - -RETURN = ''' -obj: - description: CertificateManagementProfile (api/certificatemanagementprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - name=dict(type='str', required=True), - script_params=dict(type='list',), - script_path=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'certificatemanagementprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_cloud.py b/plugins/modules/network/avi/avi_cloud.py deleted file mode 100644 index a46b7e775b..0000000000 --- a/plugins/modules/network/avi/avi_cloud.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_cloud -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Cloud Avi RESTful Object -description: - - This module is used to configure Cloud object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - apic_configuration: - description: - - Apicconfiguration settings for cloud. - apic_mode: - description: - - Boolean flag to set apic_mode. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - autoscale_polling_interval: - description: - - Cloudconnector polling interval for external autoscale groups. - - Field introduced in 18.2.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - aws_configuration: - description: - - Awsconfiguration settings for cloud. - azure_configuration: - description: - - Field introduced in 17.2.1. - cloudstack_configuration: - description: - - Cloudstackconfiguration settings for cloud. - custom_tags: - description: - - Custom tags for all avi created resources in the cloud infrastructure. - - Field introduced in 17.1.5. - dhcp_enabled: - description: - - Select the ip address management scheme. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - dns_provider_ref: - description: - - Dns profile for the cloud. - - It is a reference to an object of type ipamdnsproviderprofile. - docker_configuration: - description: - - Dockerconfiguration settings for cloud. - east_west_dns_provider_ref: - description: - - Dns profile for east-west services. - - It is a reference to an object of type ipamdnsproviderprofile. - east_west_ipam_provider_ref: - description: - - Ipam profile for east-west services. - - Warning - please use virtual subnets in this ipam profile that do not conflict with the underlay networks or any overlay networks in the cluster. - - For example in aws and gcp, 169.254.0.0/16 is used for storing instance metadata. - - Hence, it should not be used in this profile. - - It is a reference to an object of type ipamdnsproviderprofile. - enable_vip_static_routes: - description: - - Use static routes for vip side network resolution during virtualservice placement. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - gcp_configuration: - description: - - Google cloud platform configuration. - - Field introduced in 18.2.1. - ip6_autocfg_enabled: - description: - - Enable ipv6 auto configuration. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - ipam_provider_ref: - description: - - Ipam profile for the cloud. - - It is a reference to an object of type ipamdnsproviderprofile. - license_tier: - description: - - Specifies the default license tier which would be used by new se groups. - - This field by default inherits the value from system configuration. - - Enum options - ENTERPRISE_16, ENTERPRISE_18. - - Field introduced in 17.2.5. - license_type: - description: - - If no license type is specified then default license enforcement for the cloud type is chosen. - - The default mappings are container cloud is max ses, openstack and vmware is cores and linux it is sockets. - - Enum options - LIC_BACKEND_SERVERS, LIC_SOCKETS, LIC_CORES, LIC_HOSTS, LIC_SE_BANDWIDTH, LIC_METERED_SE_BANDWIDTH. - linuxserver_configuration: - description: - - Linuxserverconfiguration settings for cloud. - mesos_configuration: - description: - - Field deprecated in 18.2.2. - mtu: - description: - - Mtu setting for the cloud. - - Default value when not specified in API or module is interpreted by Avi Controller as 1500. - name: - description: - - Name of the object. - required: true - nsx_configuration: - description: - - Configuration parameters for nsx manager. - - Field introduced in 17.1.1. - obj_name_prefix: - description: - - Default prefix for all automatically created objects in this cloud. - - This prefix can be overridden by the se-group template. - openstack_configuration: - description: - - Openstackconfiguration settings for cloud. - oshiftk8s_configuration: - description: - - Oshiftk8sconfiguration settings for cloud. - prefer_static_routes: - description: - - Prefer static routes over interface routes during virtualservice placement. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - proxy_configuration: - description: - - Proxyconfiguration settings for cloud. - rancher_configuration: - description: - - Rancherconfiguration settings for cloud. - state_based_dns_registration: - description: - - Dns records for vips are added/deleted based on the operational state of the vips. - - Field introduced in 17.1.12. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. - vca_configuration: - description: - - Vcloudairconfiguration settings for cloud. - vcenter_configuration: - description: - - Vcenterconfiguration settings for cloud. - vtype: - description: - - Cloud type. - - Enum options - CLOUD_NONE, CLOUD_VCENTER, CLOUD_OPENSTACK, CLOUD_AWS, CLOUD_VCA, CLOUD_APIC, CLOUD_MESOS, CLOUD_LINUXSERVER, CLOUD_DOCKER_UCP, - - CLOUD_RANCHER, CLOUD_OSHIFT_K8S, CLOUD_AZURE, CLOUD_GCP. - - Default value when not specified in API or module is interpreted by Avi Controller as CLOUD_NONE. - required: true -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create a VMware cloud with write access mode - avi_cloud: - username: '{{ username }}' - controller: '{{ controller }}' - password: '{{ password }}' - apic_mode: false - dhcp_enabled: true - enable_vip_static_routes: false - license_type: LIC_CORES - mtu: 1500 - name: vCenter Cloud - prefer_static_routes: false - tenant_ref: admin - vcenter_configuration: - datacenter_ref: /api/vimgrdcruntime/datacenter-2-10.10.20.100 - management_network: /api/vimgrnwruntime/dvportgroup-103-10.10.20.100 - password: password - privilege: WRITE_ACCESS - username: user - vcenter_url: 10.10.20.100 - vtype: CLOUD_VCENTER -""" - -RETURN = ''' -obj: - description: Cloud (api/cloud) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - apic_configuration=dict(type='dict',), - apic_mode=dict(type='bool',), - autoscale_polling_interval=dict(type='int',), - aws_configuration=dict(type='dict',), - azure_configuration=dict(type='dict',), - cloudstack_configuration=dict(type='dict',), - custom_tags=dict(type='list',), - dhcp_enabled=dict(type='bool',), - dns_provider_ref=dict(type='str',), - docker_configuration=dict(type='dict',), - east_west_dns_provider_ref=dict(type='str',), - east_west_ipam_provider_ref=dict(type='str',), - enable_vip_static_routes=dict(type='bool',), - gcp_configuration=dict(type='dict',), - ip6_autocfg_enabled=dict(type='bool',), - ipam_provider_ref=dict(type='str',), - license_tier=dict(type='str',), - license_type=dict(type='str',), - linuxserver_configuration=dict(type='dict',), - mesos_configuration=dict(type='dict',), - mtu=dict(type='int',), - name=dict(type='str', required=True), - nsx_configuration=dict(type='dict',), - obj_name_prefix=dict(type='str',), - openstack_configuration=dict(type='dict',), - oshiftk8s_configuration=dict(type='dict',), - prefer_static_routes=dict(type='bool',), - proxy_configuration=dict(type='dict',), - rancher_configuration=dict(type='dict',), - state_based_dns_registration=dict(type='bool',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - vca_configuration=dict(type='dict',), - vcenter_configuration=dict(type='dict',), - vtype=dict(type='str', required=True), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'cloud', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_cloudconnectoruser.py b/plugins/modules/network/avi/avi_cloudconnectoruser.py deleted file mode 100644 index 21082ecf96..0000000000 --- a/plugins/modules/network/avi/avi_cloudconnectoruser.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_cloudconnectoruser -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of CloudConnectorUser Avi RESTful Object -description: - - This module is used to configure CloudConnectorUser object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - azure_serviceprincipal: - description: - - Field introduced in 17.2.1. - azure_userpass: - description: - - Field introduced in 17.2.1. - gcp_credentials: - description: - - Credentials for google cloud platform. - - Field introduced in 18.2.1. - name: - description: - - Name of the object. - required: true - oci_credentials: - description: - - Credentials for oracle cloud infrastructure. - - Field introduced in 18.2.1,18.1.3. - private_key: - description: - - Private_key of cloudconnectoruser. - public_key: - description: - - Public_key of cloudconnectoruser. - tenant_ref: - description: - - It is a reference to an object of type tenant. - tencent_credentials: - description: - - Credentials for tencent cloud. - - Field introduced in 18.2.3. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create a Cloud connector user that is used for integration into cloud platforms - avi_cloudconnectoruser: - controller: '{{ controller }}' - name: root - password: '{{ password }}' - private_key: | - -----BEGIN RSA PRIVATE KEY----- - -----END RSA PRIVATE KEY-----' - public_key: 'ssh-rsa ...' - tenant_ref: admin - username: '{{ username }}' -""" - -RETURN = ''' -obj: - description: CloudConnectorUser (api/cloudconnectoruser) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - azure_serviceprincipal=dict(type='dict',), - azure_userpass=dict(type='dict',), - gcp_credentials=dict(type='dict',), - name=dict(type='str', required=True), - oci_credentials=dict(type='dict',), - private_key=dict(type='str', no_log=True,), - public_key=dict(type='str',), - tenant_ref=dict(type='str',), - tencent_credentials=dict(type='dict',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'cloudconnectoruser', - set(['private_key'])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_cloudproperties.py b/plugins/modules/network/avi/avi_cloudproperties.py deleted file mode 100644 index 636aa23306..0000000000 --- a/plugins/modules/network/avi/avi_cloudproperties.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_cloudproperties -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of CloudProperties Avi RESTful Object -description: - - This module is used to configure CloudProperties object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cc_props: - description: - - Cloudconnector properties. - cc_vtypes: - description: - - Cloud types supported by cloudconnector. - - Enum options - CLOUD_NONE, CLOUD_VCENTER, CLOUD_OPENSTACK, CLOUD_AWS, CLOUD_VCA, CLOUD_APIC, CLOUD_MESOS, CLOUD_LINUXSERVER, CLOUD_DOCKER_UCP, - - CLOUD_RANCHER, CLOUD_OSHIFT_K8S, CLOUD_AZURE, CLOUD_GCP. - hyp_props: - description: - - Hypervisor properties. - info: - description: - - Properties specific to a cloud type. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create CloudProperties object - avi_cloudproperties: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_cloudproperties -""" - -RETURN = ''' -obj: - description: CloudProperties (api/cloudproperties) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cc_props=dict(type='dict',), - cc_vtypes=dict(type='list',), - hyp_props=dict(type='list',), - info=dict(type='list',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'cloudproperties', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_cluster.py b/plugins/modules/network/avi/avi_cluster.py deleted file mode 100644 index fe983d77ea..0000000000 --- a/plugins/modules/network/avi/avi_cluster.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_cluster -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Cluster Avi RESTful Object -description: - - This module is used to configure Cluster object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - name: - description: - - Name of the object. - required: true - nodes: - description: - - List of clusternode. - rejoin_nodes_automatically: - description: - - Re-join cluster nodes automatically in the event one of the node is reset to factory. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. - virtual_ip: - description: - - A virtual ip address. - - This ip address will be dynamically reconfigured so that it always is the ip of the cluster leader. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create Cluster object - avi_cluster: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_cluster -""" - -RETURN = ''' -obj: - description: Cluster (api/cluster) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - name=dict(type='str', required=True), - nodes=dict(type='list',), - rejoin_nodes_automatically=dict(type='bool',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - virtual_ip=dict(type='dict',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'cluster', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_clusterclouddetails.py b/plugins/modules/network/avi/avi_clusterclouddetails.py deleted file mode 100644 index c7692d3181..0000000000 --- a/plugins/modules/network/avi/avi_clusterclouddetails.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_clusterclouddetails -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ClusterCloudDetails Avi RESTful Object -description: - - This module is used to configure ClusterCloudDetails object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - azure_info: - description: - - Azure info to configure cluster_vip on the controller. - - Field introduced in 17.2.5. - name: - description: - - Field introduced in 17.2.5. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.2.5. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Field introduced in 17.2.5. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create ClusterCloudDetails object - avi_clusterclouddetails: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_clusterclouddetails -""" - -RETURN = ''' -obj: - description: ClusterCloudDetails (api/clusterclouddetails) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - azure_info=dict(type='dict',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'clusterclouddetails', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_controllerproperties.py b/plugins/modules/network/avi/avi_controllerproperties.py deleted file mode 100644 index d5d3b6c42b..0000000000 --- a/plugins/modules/network/avi/avi_controllerproperties.py +++ /dev/null @@ -1,421 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.2 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_controllerproperties -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ControllerProperties Avi RESTful Object -description: - - This module is used to configure ControllerProperties object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - allow_ip_forwarding: - description: - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - allow_unauthenticated_apis: - description: - - Allow unauthenticated access for special apis. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - allow_unauthenticated_nodes: - description: - - Boolean flag to set allow_unauthenticated_nodes. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - api_idle_timeout: - description: - - Allowed values are 0-1440. - - Default value when not specified in API or module is interpreted by Avi Controller as 15. - api_perf_logging_threshold: - description: - - Threshold to log request timing in portal_performance.log and server-timing response header. - - Any stage taking longer than 1% of the threshold will be included in the server-timing header. - - Field introduced in 18.1.4, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 10000. - appviewx_compat_mode: - description: - - Export configuration in appviewx compatibility mode. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - attach_ip_retry_interval: - description: - - Number of attach_ip_retry_interval. - - Default value when not specified in API or module is interpreted by Avi Controller as 360. - attach_ip_retry_limit: - description: - - Number of attach_ip_retry_limit. - - Default value when not specified in API or module is interpreted by Avi Controller as 4. - bm_use_ansible: - description: - - Use ansible for se creation in baremetal. - - Field introduced in 17.2.2. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - cleanup_expired_authtoken_timeout_period: - description: - - Period for auth token cleanup job. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - cleanup_sessions_timeout_period: - description: - - Period for sessions cleanup job. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - cloud_reconcile: - description: - - Enable/disable periodic reconcile for all the clouds. - - Field introduced in 17.2.14,18.1.5,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - cluster_ip_gratuitous_arp_period: - description: - - Period for cluster ip gratuitous arp job. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - consistency_check_timeout_period: - description: - - Period for consistency check job. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - crashed_se_reboot: - description: - - Number of crashed_se_reboot. - - Default value when not specified in API or module is interpreted by Avi Controller as 900. - dead_se_detection_timer: - description: - - Number of dead_se_detection_timer. - - Default value when not specified in API or module is interpreted by Avi Controller as 360. - dns_refresh_period: - description: - - Period for refresh pool and gslb dns job. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - dummy: - description: - - Number of dummy. - enable_api_sharding: - description: - - This setting enables the controller leader to shard api requests to the followers (if any). - - Field introduced in 18.1.5, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - enable_memory_balancer: - description: - - Enable/disable memory balancer. - - Field introduced in 17.2.8. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - fatal_error_lease_time: - description: - - Number of fatal_error_lease_time. - - Default value when not specified in API or module is interpreted by Avi Controller as 120. - max_dead_se_in_grp: - description: - - Number of max_dead_se_in_grp. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - max_pcap_per_tenant: - description: - - Maximum number of pcap files stored per tenant. - - Default value when not specified in API or module is interpreted by Avi Controller as 4. - max_seq_attach_ip_failures: - description: - - Maximum number of consecutive attach ip failures that halts vs placement. - - Field introduced in 17.2.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 3. - max_seq_vnic_failures: - description: - - Number of max_seq_vnic_failures. - - Default value when not specified in API or module is interpreted by Avi Controller as 3. - persistence_key_rotate_period: - description: - - Period for rotate app persistence keys job. - - Allowed values are 1-1051200. - - Special values are 0 - 'disabled'. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - portal_token: - description: - - Token used for uploading tech-support to portal. - - Field introduced in 16.4.6,17.1.2. - process_locked_useraccounts_timeout_period: - description: - - Period for process locked user accounts job. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - process_pki_profile_timeout_period: - description: - - Period for process pki profile job. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 1440. - query_host_fail: - description: - - Number of query_host_fail. - - Default value when not specified in API or module is interpreted by Avi Controller as 180. - safenet_hsm_version: - description: - - Version of the safenet package installed on the controller. - - Field introduced in 16.5.2,17.2.3. - se_create_timeout: - description: - - Number of se_create_timeout. - - Default value when not specified in API or module is interpreted by Avi Controller as 900. - se_failover_attempt_interval: - description: - - Interval between attempting failovers to an se. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - se_from_marketplace: - description: - - This setting decides whether se is to be deployed from the cloud marketplace or to be created by the controller. - - The setting is applicable only when byol license is selected. - - Enum options - MARKETPLACE, IMAGE. - - Field introduced in 18.1.4, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as IMAGE. - se_offline_del: - description: - - Number of se_offline_del. - - Default value when not specified in API or module is interpreted by Avi Controller as 172000. - se_vnic_cooldown: - description: - - Number of se_vnic_cooldown. - - Default value when not specified in API or module is interpreted by Avi Controller as 120. - secure_channel_cleanup_timeout: - description: - - Period for secure channel cleanup job. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - secure_channel_controller_token_timeout: - description: - - Number of secure_channel_controller_token_timeout. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - secure_channel_se_token_timeout: - description: - - Number of secure_channel_se_token_timeout. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - seupgrade_fabric_pool_size: - description: - - Pool size used for all fabric commands during se upgrade. - - Default value when not specified in API or module is interpreted by Avi Controller as 20. - seupgrade_segroup_min_dead_timeout: - description: - - Time to wait before marking segroup upgrade as stuck. - - Default value when not specified in API or module is interpreted by Avi Controller as 360. - ssl_certificate_expiry_warning_days: - description: - - Number of days for ssl certificate expiry warning. - unresponsive_se_reboot: - description: - - Number of unresponsive_se_reboot. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - upgrade_dns_ttl: - description: - - Time to account for dns ttl during upgrade. - - This is in addition to vs_scalein_timeout_for_upgrade in se_group. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 5. - upgrade_lease_time: - description: - - Number of upgrade_lease_time. - - Default value when not specified in API or module is interpreted by Avi Controller as 360. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. - vnic_op_fail_time: - description: - - Number of vnic_op_fail_time. - - Default value when not specified in API or module is interpreted by Avi Controller as 180. - vs_apic_scaleout_timeout: - description: - - Time to wait for the scaled out se to become ready before marking the scaleout done, applies to apic configuration only. - - Default value when not specified in API or module is interpreted by Avi Controller as 360. - vs_awaiting_se_timeout: - description: - - Number of vs_awaiting_se_timeout. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - vs_key_rotate_period: - description: - - Period for rotate vs keys job. - - Allowed values are 1-1051200. - - Special values are 0 - 'disabled'. - - Default value when not specified in API or module is interpreted by Avi Controller as 360. - vs_scaleout_ready_check_interval: - description: - - Interval for checking scaleout_ready status while controller is waiting for scaleoutready rpc from the service engine. - - Field introduced in 18.2.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - vs_se_attach_ip_fail: - description: - - Time to wait before marking attach ip operation on an se as failed. - - Field introduced in 17.2.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 600. - vs_se_bootup_fail: - description: - - Number of vs_se_bootup_fail. - - Default value when not specified in API or module is interpreted by Avi Controller as 480. - vs_se_create_fail: - description: - - Number of vs_se_create_fail. - - Default value when not specified in API or module is interpreted by Avi Controller as 1500. - vs_se_ping_fail: - description: - - Number of vs_se_ping_fail. - - Default value when not specified in API or module is interpreted by Avi Controller as 60. - vs_se_vnic_fail: - description: - - Number of vs_se_vnic_fail. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - vs_se_vnic_ip_fail: - description: - - Number of vs_se_vnic_ip_fail. - - Default value when not specified in API or module is interpreted by Avi Controller as 120. - warmstart_se_reconnect_wait_time: - description: - - Number of warmstart_se_reconnect_wait_time. - - Default value when not specified in API or module is interpreted by Avi Controller as 480. - warmstart_vs_resync_wait_time: - description: - - Timeout for warmstart vs resync. - - Field introduced in 18.1.4, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create ControllerProperties object - avi_controllerproperties: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_controllerproperties -""" - -RETURN = ''' -obj: - description: ControllerProperties (api/controllerproperties) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - allow_ip_forwarding=dict(type='bool',), - allow_unauthenticated_apis=dict(type='bool',), - allow_unauthenticated_nodes=dict(type='bool',), - api_idle_timeout=dict(type='int',), - api_perf_logging_threshold=dict(type='int',), - appviewx_compat_mode=dict(type='bool',), - attach_ip_retry_interval=dict(type='int',), - attach_ip_retry_limit=dict(type='int',), - bm_use_ansible=dict(type='bool',), - cleanup_expired_authtoken_timeout_period=dict(type='int',), - cleanup_sessions_timeout_period=dict(type='int',), - cloud_reconcile=dict(type='bool',), - cluster_ip_gratuitous_arp_period=dict(type='int',), - consistency_check_timeout_period=dict(type='int',), - crashed_se_reboot=dict(type='int',), - dead_se_detection_timer=dict(type='int',), - dns_refresh_period=dict(type='int',), - dummy=dict(type='int',), - enable_api_sharding=dict(type='bool',), - enable_memory_balancer=dict(type='bool',), - fatal_error_lease_time=dict(type='int',), - max_dead_se_in_grp=dict(type='int',), - max_pcap_per_tenant=dict(type='int',), - max_seq_attach_ip_failures=dict(type='int',), - max_seq_vnic_failures=dict(type='int',), - persistence_key_rotate_period=dict(type='int',), - portal_token=dict(type='str', no_log=True,), - process_locked_useraccounts_timeout_period=dict(type='int',), - process_pki_profile_timeout_period=dict(type='int',), - query_host_fail=dict(type='int',), - safenet_hsm_version=dict(type='str',), - se_create_timeout=dict(type='int',), - se_failover_attempt_interval=dict(type='int',), - se_from_marketplace=dict(type='str',), - se_offline_del=dict(type='int',), - se_vnic_cooldown=dict(type='int',), - secure_channel_cleanup_timeout=dict(type='int',), - secure_channel_controller_token_timeout=dict(type='int',), - secure_channel_se_token_timeout=dict(type='int',), - seupgrade_fabric_pool_size=dict(type='int',), - seupgrade_segroup_min_dead_timeout=dict(type='int',), - ssl_certificate_expiry_warning_days=dict(type='list',), - unresponsive_se_reboot=dict(type='int',), - upgrade_dns_ttl=dict(type='int',), - upgrade_lease_time=dict(type='int',), - url=dict(type='str',), - uuid=dict(type='str',), - vnic_op_fail_time=dict(type='int',), - vs_apic_scaleout_timeout=dict(type='int',), - vs_awaiting_se_timeout=dict(type='int',), - vs_key_rotate_period=dict(type='int',), - vs_scaleout_ready_check_interval=dict(type='int',), - vs_se_attach_ip_fail=dict(type='int',), - vs_se_bootup_fail=dict(type='int',), - vs_se_create_fail=dict(type='int',), - vs_se_ping_fail=dict(type='int',), - vs_se_vnic_fail=dict(type='int',), - vs_se_vnic_ip_fail=dict(type='int',), - warmstart_se_reconnect_wait_time=dict(type='int',), - warmstart_vs_resync_wait_time=dict(type='int',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'controllerproperties', - set(['portal_token'])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_customipamdnsprofile.py b/plugins/modules/network/avi/avi_customipamdnsprofile.py deleted file mode 100644 index 05beed19a0..0000000000 --- a/plugins/modules/network/avi/avi_customipamdnsprofile.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_customipamdnsprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of CustomIpamDnsProfile Avi RESTful Object -description: - - This module is used to configure CustomIpamDnsProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - name: - description: - - Name of the custom ipam dns profile. - - Field introduced in 17.1.1. - required: true - script_params: - description: - - Parameters that are always passed to the ipam/dns script. - - Field introduced in 17.1.1. - script_uri: - description: - - Script uri of form controller //ipamdnsscripts/. - - Field introduced in 17.1.1. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.1.1. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Field introduced in 17.1.1. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create CustomIpamDnsProfile object - avi_customipamdnsprofile: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_customipamdnsprofile -""" - -RETURN = ''' -obj: - description: CustomIpamDnsProfile (api/customipamdnsprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - name=dict(type='str', required=True), - script_params=dict(type='list',), - script_uri=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'customipamdnsprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_dnspolicy.py b/plugins/modules/network/avi/avi_dnspolicy.py deleted file mode 100644 index 79b1bfc184..0000000000 --- a/plugins/modules/network/avi/avi_dnspolicy.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_dnspolicy -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of DnsPolicy Avi RESTful Object -description: - - This module is used to configure DnsPolicy object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - created_by: - description: - - Creator name. - - Field introduced in 17.1.1. - description: - description: - - Field introduced in 17.1.1. - name: - description: - - Name of the dns policy. - - Field introduced in 17.1.1. - required: true - rule: - description: - - Dns rules. - - Field introduced in 17.1.1. - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.1.1. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the dns policy. - - Field introduced in 17.1.1. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create DnsPolicy object - avi_dnspolicy: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_dnspolicy -""" - -RETURN = ''' -obj: - description: DnsPolicy (api/dnspolicy) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - created_by=dict(type='str',), - description=dict(type='str',), - name=dict(type='str', required=True), - rule=dict(type='list',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'dnspolicy', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_errorpagebody.py b/plugins/modules/network/avi/avi_errorpagebody.py deleted file mode 100644 index 2404c98a84..0000000000 --- a/plugins/modules/network/avi/avi_errorpagebody.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_errorpagebody -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ErrorPageBody Avi RESTful Object -description: - - This module is used to configure ErrorPageBody object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - error_page_body: - description: - - Error page body sent to client when match. - - Field introduced in 17.2.4. - format: - description: - - Format of an error page body html or json. - - Enum options - ERROR_PAGE_FORMAT_HTML, ERROR_PAGE_FORMAT_JSON. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as ERROR_PAGE_FORMAT_HTML. - name: - description: - - Field introduced in 17.2.4. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.2.4. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Field introduced in 17.2.4. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create ErrorPageBody object - avi_errorpagebody: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_errorpagebody -""" - -RETURN = ''' -obj: - description: ErrorPageBody (api/errorpagebody) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - error_page_body=dict(type='str',), - format=dict(type='str',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'errorpagebody', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_errorpageprofile.py b/plugins/modules/network/avi/avi_errorpageprofile.py deleted file mode 100644 index 67cff153f2..0000000000 --- a/plugins/modules/network/avi/avi_errorpageprofile.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_errorpageprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ErrorPageProfile Avi RESTful Object -description: - - This module is used to configure ErrorPageProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - app_name: - description: - - Name of the virtual service which generated the error page. - - Field deprecated in 18.1.1. - - Field introduced in 17.2.4. - - Default value when not specified in API or module is interpreted by Avi Controller as VS Name. - company_name: - description: - - Name of the company to show in error page. - - Field deprecated in 18.1.1. - - Field introduced in 17.2.4. - - Default value when not specified in API or module is interpreted by Avi Controller as Avi Networks. - error_pages: - description: - - Defined error pages for http status codes. - - Field introduced in 17.2.4. - host_name: - description: - - Fully qualified domain name for which the error page is generated. - - Field deprecated in 18.1.1. - - Field introduced in 17.2.4. - - Default value when not specified in API or module is interpreted by Avi Controller as Host Header. - name: - description: - - Field introduced in 17.2.4. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.2.4. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Field introduced in 17.2.4. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create ErrorPageProfile object - avi_errorpageprofile: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_errorpageprofile -""" - -RETURN = ''' -obj: - description: ErrorPageProfile (api/errorpageprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - app_name=dict(type='str',), - company_name=dict(type='str',), - error_pages=dict(type='list',), - host_name=dict(type='str',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'errorpageprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_gslb.py b/plugins/modules/network/avi/avi_gslb.py deleted file mode 100644 index 186543e9b4..0000000000 --- a/plugins/modules/network/avi/avi_gslb.py +++ /dev/null @@ -1,354 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_gslb -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Gslb Avi RESTful Object -description: - - This module is used to configure Gslb object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - async_interval: - description: - - Frequency with which messages are propagated to vs mgr. - - Value of 0 disables async behavior and rpc are sent inline. - - Allowed values are 0-5. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - clear_on_max_retries: - description: - - Max retries after which the remote site is treated as a fresh start. - - In fresh start all the configs are downloaded. - - Allowed values are 1-1024. - - Default value when not specified in API or module is interpreted by Avi Controller as 20. - client_ip_addr_group: - description: - - Group to specify if the client ip addresses are public or private. - - Field introduced in 17.1.2. - description: - description: - - User defined description for the object. - dns_configs: - description: - - Sub domain configuration for the gslb. - - Gslb service's fqdn must be a match one of these subdomains. - is_federated: - description: - - This field indicates that this object is replicated across gslb federation. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - leader_cluster_uuid: - description: - - Mark this site as leader of gslb configuration. - - This site is the one among the avi sites. - required: true - maintenance_mode: - description: - - This field disables the configuration operations on the leader for all federated objects. - - Cud operations on gslb, gslbservice, gslbgeodbprofile and other federated objects will be rejected. - - The rest-api disabling helps in upgrade scenarios where we don't want configuration sync operations to the gslb member when the member is being - - upgraded. - - This configuration programmatically blocks the leader from accepting new gslb configuration when member sites are undergoing upgrade. - - Field introduced in 17.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - name: - description: - - Name for the gslb object. - required: true - send_interval: - description: - - Frequency with which group members communicate. - - Allowed values are 1-3600. - - Default value when not specified in API or module is interpreted by Avi Controller as 15. - send_interval_prior_to_maintenance_mode: - description: - - The user can specify a send-interval while entering maintenance mode. - - The validity of this 'maintenance send-interval' is only during maintenance mode. - - When the user leaves maintenance mode, the original send-interval is reinstated. - - This internal variable is used to store the original send-interval. - - Field introduced in 18.2.3. - sites: - description: - - Select avi site member belonging to this gslb. - tenant_ref: - description: - - It is a reference to an object of type tenant. - third_party_sites: - description: - - Third party site member belonging to this gslb. - - Field introduced in 17.1.1. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the gslb object. - view_id: - description: - - The view-id is used in change-leader mode to differentiate partitioned groups while they have the same gslb namespace. - - Each partitioned group will be able to operate independently by using the view-id. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create Gslb object - avi_gslb: - name: "test-gslb" - avi_credentials: - username: '{{ username }}' - password: '{{ password }}' - controller: '{{ controller }}' - sites: - - name: "test-site1" - username: "gslb_username" - password: "gslb_password" - ip_addresses: - - type: "V4" - addr: "10.10.28.83" - enabled: True - member_type: "GSLB_ACTIVE_MEMBER" - port: 443 - cluster_uuid: "cluster-d4ee5fcc-3e0a-4d4f-9ae6-4182bc605829" - - name: "test-site2" - username: "gslb_username" - password: "gslb_password" - ip_addresses: - - type: "V4" - addr: "10.10.28.86" - enabled: True - member_type: "GSLB_ACTIVE_MEMBER" - port: 443 - cluster_uuid: "cluster-0c37ae8d-ab62-410c-ad3e-06fa831950b1" - dns_configs: - - domain_name: "test1.com" - - domain_name: "test2.com" - leader_cluster_uuid: "cluster-d4ee5fcc-3e0a-4d4f-9ae6-4182bc605829" - -- name: Update Gslb site's configurations (Patch Add Operation) - avi_gslb: - avi_credentials: - username: '{{ username }}' - password: '{{ password }}' - controller: '{{ controller }}' - avi_api_update_method: patch - avi_api_patch_op: add - leader_cluster_uuid: "cluster-d4ee5fcc-3e0a-4d4f-9ae6-4182bc605829" - name: "test-gslb" - dns_configs: - - domain_name: "temp1.com" - - domain_name: "temp2.com" - gslb_sites_config: - - ip_addr: "10.10.28.83" - dns_vses: - - dns_vs_uuid: "virtualservice-f2a711cd-5e78-473f-8f47-d12de660fd62" - domain_names: - - "test1.com" - - "test2.com" - - ip_addr: "10.10.28.86" - dns_vses: - - dns_vs_uuid: "virtualservice-c1a63a16-f2a1-4f41-aab4-1e90f92a5e49" - domain_names: - - "temp1.com" - - "temp2.com" - -- name: Update Gslb site's configurations (Patch Replace Operation) - avi_gslb: - avi_credentials: - username: "{{ username }}" - password: "{{ password }}" - controller: "{{ controller }}" - # On basis of cluster leader uuid dns_configs is set for that particular leader cluster - leader_cluster_uuid: "cluster-84aa795f-8f09-42bb-97a4-5103f4a53da9" - name: "test-gslb" - avi_api_update_method: patch - avi_api_patch_op: replace - dns_configs: - - domain_name: "test3.com" - - domain_name: "temp3.com" - gslb_sites_config: - # Ip address is mapping key for dns_vses field update. For the given IP address, - # dns_vses is updated. - - ip_addr: "10.10.28.83" - dns_vses: - - dns_vs_uuid: "virtualservice-7c947ed4-77f3-4a52-909c-4f12afaf5bb0" - domain_names: - - "test3.com" - - ip_addr: "10.10.28.86" - dns_vses: - - dns_vs_uuid: "virtualservice-799b2c6d-7f2d-4c3f-94c6-6e813b20b674" - domain_names: - - "temp3.com" - -- name: Update Gslb site's configurations (Patch Delete Operation) - avi_gslb: - avi_credentials: - username: "{{ username }}" - password: "{{ password }}" - controller: "{{ controller }}" - # On basis of cluster leader uuid dns_configs is set for that particular leader cluster - leader_cluster_uuid: "cluster-84aa795f-8f09-42bb-97a4-5103f4a53da9" - name: "test-gslb" - avi_api_update_method: patch - avi_api_patch_op: delete - dns_configs: - gslb_sites_config: - - ip_addr: "10.10.28.83" - - ip_addr: "10.10.28.86" -""" - -RETURN = ''' -obj: - description: Gslb (api/gslb) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) - from ansible_collections.community.general.plugins.module_utils.network.avi.avi_api import ApiSession, AviCredentials -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - async_interval=dict(type='int',), - clear_on_max_retries=dict(type='int',), - client_ip_addr_group=dict(type='dict',), - description=dict(type='str',), - dns_configs=dict(type='list',), - is_federated=dict(type='bool',), - leader_cluster_uuid=dict(type='str', required=True), - maintenance_mode=dict(type='bool',), - name=dict(type='str', required=True), - send_interval=dict(type='int',), - send_interval_prior_to_maintenance_mode=dict(type='int',), - sites=dict(type='list',), - tenant_ref=dict(type='str',), - third_party_sites=dict(type='list',), - url=dict(type='str',), - uuid=dict(type='str',), - view_id=dict(type='int',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - api_method = module.params['avi_api_update_method'] - if str(api_method).lower() == 'patch': - patch_op = module.params['avi_api_patch_op'] - # Create controller session - api_creds = AviCredentials() - api_creds.update_from_ansible_module(module) - api = ApiSession.get_session( - api_creds.controller, api_creds.username, password=api_creds.password, - timeout=api_creds.timeout, tenant=api_creds.tenant, - tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, - port=api_creds.port) - # Get existing gslb objects - rsp = api.get('gslb', api_version=api_creds.api_version) - existing_gslb = rsp.json() - gslb = existing_gslb['results'] - sites = module.params['gslb_sites_config'] - for gslb_obj in gslb: - # Update/Delete domain names in dns_configs fields in gslb object. - if 'dns_configs' in module.params: - if gslb_obj['leader_cluster_uuid'] == module.params['leader_cluster_uuid']: - if str(patch_op).lower() == 'delete': - gslb_obj['dns_configs'] = [] - elif str(patch_op).lower() == 'add': - if module.params['dns_configs'] not in gslb_obj['dns_configs']: - gslb_obj['dns_configs'].extend(module.params['dns_configs']) - else: - gslb_obj['dns_configs'] = module.params['dns_configs'] - # Update/Delete sites configuration - if sites: - for site_obj in gslb_obj['sites']: - dns_vses = site_obj.get('dns_vses', []) - for obj in sites: - config_for = obj.get('ip_addr', None) - if not config_for: - return module.fail_json(msg=( - "ip_addr of site in a configuration is mandatory. " - "Please provide ip_addr i.e. gslb site's ip.")) - if config_for == site_obj['ip_addresses'][0]['addr']: - if str(patch_op).lower() == 'delete': - site_obj['dns_vses'] = [] - else: - # Modify existing gslb sites object - for key, val in obj.items(): - if key == 'dns_vses' and str(patch_op).lower() == 'add': - found = False - # Check dns_vses field already exists on the controller - for v in dns_vses: - if val[0]['dns_vs_uuid'] != v['dns_vs_uuid']: - found = True - break - if not found: - dns_vses.extend(val) - else: - site_obj[key] = val - if str(patch_op).lower() == 'add': - site_obj['dns_vses'] = dns_vses - uni_dns_configs = [dict(tupleized) for tupleized in set(tuple(item.items()) - for item in gslb_obj['dns_configs'])] - gslb_obj['dns_configs'] = uni_dns_configs - module.params.update(gslb_obj) - module.params.update( - { - 'avi_api_update_method': 'put', - 'state': 'present' - } - ) - return avi_ansible_api(module, 'gslb', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_gslbgeodbprofile.py b/plugins/modules/network/avi/avi_gslbgeodbprofile.py deleted file mode 100644 index 8db544ff2c..0000000000 --- a/plugins/modules/network/avi/avi_gslbgeodbprofile.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.2 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_gslbgeodbprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of GslbGeoDbProfile Avi RESTful Object -description: - - This module is used to configure GslbGeoDbProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - description: - description: - - Field introduced in 17.1.1. - entries: - description: - - List of geodb entries. - - An entry can either be a geodb file or an ip address group with geo properties. - - Field introduced in 17.1.1. - is_federated: - description: - - This field indicates that this object is replicated across gslb federation. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - name: - description: - - A user-friendly name for the geodb profile. - - Field introduced in 17.1.1. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.1.1. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the geodb profile. - - Field introduced in 17.1.1. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create GslbGeoDbProfile object - avi_gslbgeodbprofile: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_gslbgeodbprofile -""" - -RETURN = ''' -obj: - description: GslbGeoDbProfile (api/gslbgeodbprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - description=dict(type='str',), - entries=dict(type='list',), - is_federated=dict(type='bool',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'gslbgeodbprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_gslbservice.py b/plugins/modules/network/avi/avi_gslbservice.py deleted file mode 100644 index 71de354626..0000000000 --- a/plugins/modules/network/avi/avi_gslbservice.py +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_gslbservice -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of GslbService Avi RESTful Object -description: - - This module is used to configure GslbService object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - application_persistence_profile_ref: - description: - - The federated application persistence associated with gslbservice site persistence functionality. - - It is a reference to an object of type applicationpersistenceprofile. - - Field introduced in 17.2.1. - controller_health_status_enabled: - description: - - Gs member's overall health status is derived based on a combination of controller and datapath health-status inputs. - - Note that the datapath status is determined by the association of health monitor profiles. - - Only the controller provided status is determined through this configuration. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - created_by: - description: - - Creator name. - - Field introduced in 17.1.2. - description: - description: - - User defined description for the object. - domain_names: - description: - - Fully qualified domain name of the gslb service. - down_response: - description: - - Response to the client query when the gslb service is down. - enabled: - description: - - Enable or disable the gslb service. - - If the gslb service is enabled, then the vips are sent in the dns responses based on reachability and configured algorithm. - - If the gslb service is disabled, then the vips are no longer available in the dns response. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - groups: - description: - - Select list of pools belonging to this gslb service. - health_monitor_refs: - description: - - Verify vs health by applying one or more health monitors. - - Active monitors generate synthetic traffic from dns service engine and to mark a vs up or down based on the response. - - It is a reference to an object of type healthmonitor. - health_monitor_scope: - description: - - Health monitor probe can be executed for all the members or it can be executed only for third-party members. - - This operational mode is useful to reduce the number of health monitor probes in case of a hybrid scenario. - - In such a case, avi members can have controller derived status while non-avi members can be probed by via health monitor probes in dataplane. - - Enum options - GSLB_SERVICE_HEALTH_MONITOR_ALL_MEMBERS, GSLB_SERVICE_HEALTH_MONITOR_ONLY_NON_AVI_MEMBERS. - - Default value when not specified in API or module is interpreted by Avi Controller as GSLB_SERVICE_HEALTH_MONITOR_ALL_MEMBERS. - hm_off: - description: - - This field is an internal field and is used in se. - - Field introduced in 18.2.2. - type: bool - is_federated: - description: - - This field indicates that this object is replicated across gslb federation. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - min_members: - description: - - The minimum number of members to distribute traffic to. - - Allowed values are 1-65535. - - Special values are 0 - 'disable'. - - Field introduced in 17.2.4. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - name: - description: - - Name for the gslb service. - required: true - num_dns_ip: - description: - - Number of ip addresses of this gslb service to be returned by the dns service. - - Enter 0 to return all ip addresses. - - Allowed values are 1-20. - - Special values are 0- 'return all ip addresses'. - pool_algorithm: - description: - - The load balancing algorithm will pick a gslb pool within the gslb service list of available pools. - - Enum options - GSLB_SERVICE_ALGORITHM_PRIORITY, GSLB_SERVICE_ALGORITHM_GEO. - - Field introduced in 17.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as GSLB_SERVICE_ALGORITHM_PRIORITY. - site_persistence_enabled: - description: - - Enable site-persistence for the gslbservice. - - Field introduced in 17.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - tenant_ref: - description: - - It is a reference to an object of type tenant. - ttl: - description: - - Ttl value (in seconds) for records served for this gslb service by the dns service. - - Allowed values are 0-86400. - url: - description: - - Avi controller URL of the object. - use_edns_client_subnet: - description: - - Use the client ip subnet from the edns option as source ipaddress for client geo-location and consistent hash algorithm. - - Default is true. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - uuid: - description: - - Uuid of the gslb service. - wildcard_match: - description: - - Enable wild-card match of fqdn if an exact match is not found in the dns table, the longest match is chosen by wild-carding the fqdn in the dns - - request. - - Default is false. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create GslbService object - avi_gslbservice: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_gslbservice -""" - -RETURN = ''' -obj: - description: GslbService (api/gslbservice) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - application_persistence_profile_ref=dict(type='str',), - controller_health_status_enabled=dict(type='bool',), - created_by=dict(type='str',), - description=dict(type='str',), - domain_names=dict(type='list',), - down_response=dict(type='dict',), - enabled=dict(type='bool',), - groups=dict(type='list',), - health_monitor_refs=dict(type='list',), - health_monitor_scope=dict(type='str',), - hm_off=dict(type='bool',), - is_federated=dict(type='bool',), - min_members=dict(type='int',), - name=dict(type='str', required=True), - num_dns_ip=dict(type='int',), - pool_algorithm=dict(type='str',), - site_persistence_enabled=dict(type='bool',), - tenant_ref=dict(type='str',), - ttl=dict(type='int',), - url=dict(type='str',), - use_edns_client_subnet=dict(type='bool',), - uuid=dict(type='str',), - wildcard_match=dict(type='bool',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'gslbservice', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_gslbservice_patch_member.py b/plugins/modules/network/avi/avi_gslbservice_patch_member.py deleted file mode 100644 index eb23ded793..0000000000 --- a/plugins/modules/network/avi/avi_gslbservice_patch_member.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/python -""" -# Created on Aug 12, 2016 -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) GitHub ID: grastogi23 -# -# module_check: supported -# -# Copyright: (c) 2016 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -""" - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: avi_gslbservice_patch_member -author: Gaurav Rastogi (@grastogi23) - -short_description: Avi API Module -description: - - This module can be used for calling any resources defined in Avi REST API. U(https://avinetworks.com/) - - This module is useful for invoking HTTP Patch methods and accessing resources that do not have an REST object associated with them. -requirements: [ avisdk ] -options: - data: - description: - - HTTP body of GSLB Service Member in YAML or JSON format. - params: - description: - - Query parameters passed to the HTTP API. - name: - description: - - Name of the GSLB Service - required: true - state: - description: - - The state that should be applied to the member. Member is - - identified using field member.ip.addr. - default: present - choices: ["absent","present"] -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = ''' - - name: Patch GSLB Service to add a new member and group - avi_gslbservice_patch_member: - controller: "{{ controller }}" - username: "{{ username }}" - password: "{{ password }}" - name: gs-3 - api_version: 17.2.1 - data: - group: - name: newfoo - priority: 60 - members: - - enabled: true - ip: - addr: 10.30.10.66 - type: V4 - ratio: 3 - - name: Patch GSLB Service to delete an existing member - avi_gslbservice_patch_member: - controller: "{{ controller }}" - username: "{{ username }}" - password: "{{ password }}" - name: gs-3 - state: absent - api_version: 17.2.1 - data: - group: - name: newfoo - members: - - enabled: true - ip: - addr: 10.30.10.68 - type: V4 - ratio: 3 - - name: Update priority of GSLB Service Pool - avi_gslbservice_patch_member: - controller: "" - username: "" - password: "" - name: gs-3 - state: present - api_version: 17.2.1 - data: - group: - name: newfoo - priority: 42 -''' - - -RETURN = ''' -obj: - description: Avi REST resource - returned: success, changed - type: dict -''' - -import json -import time -from ansible.module_utils.basic import AnsibleModule -from copy import deepcopy - -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_obj_cmp, cleanup_absent_fields, - ansible_return, AviCheckModeResponse, HAS_AVI) - from ansible_collections.community.general.plugins.module_utils.network.avi.avi_api import ( - ApiSession, AviCredentials) -except ImportError: - HAS_AVI = False - - -def delete_member(module, check_mode, api, tenant, tenant_uuid, - existing_obj, data, api_version): - members = data.get('group', {}).get('members', []) - patched_member_ids = set([m['ip']['addr'] for m in members if 'fqdn' not in m]) - patched_member_fqdns = set([m['fqdn'] for m in members if 'fqdn' in m]) - - changed = False - rsp = None - - if existing_obj and (patched_member_ids or patched_member_fqdns): - groups = [group for group in existing_obj.get('groups', []) - if group['name'] == data['group']['name']] - if groups: - changed = any( - [(lambda g: g['ip']['addr'] in patched_member_ids)(m) - for m in groups[0].get('members', []) if 'fqdn' not in m]) - changed = changed or any( - [(lambda g: g['fqdn'] in patched_member_fqdns)(m) - for m in groups[0].get('members', []) if 'fqdn' in m]) - if check_mode or not changed: - return changed, rsp - # should not come here if not found - group = groups[0] - new_members = [] - for m in group.get('members', []): - if 'fqdn' in m: - if m['fqdn'] not in patched_member_fqdns: - new_members.append(m) - elif 'ip' in m: - if m['ip']['addr'] not in patched_member_ids: - new_members.append(m) - group['members'] = new_members - if not group['members']: - # Delete this group from the existing objects if it is empty. - # Controller also does not allow empty group. - existing_obj['groups'] = [ - grp for grp in existing_obj.get('groups', []) if - grp['name'] != data['group']['name']] - # remove the members that are part of the list - # update the object - # added api version for AVI api call. - rsp = api.put('gslbservice/%s' % existing_obj['uuid'], data=existing_obj, - tenant=tenant, tenant_uuid=tenant_uuid, api_version=api_version) - return changed, rsp - - -def add_member(module, check_mode, api, tenant, tenant_uuid, - existing_obj, data, name, api_version): - rsp = None - if not existing_obj: - # create the object - changed = True - if check_mode: - rsp = AviCheckModeResponse(obj=None) - else: - # creates group with single member - req = {'name': name, - 'groups': [data['group']] - } - # added api version for AVI api call. - rsp = api.post('gslbservice', data=req, tenant=tenant, - tenant_uuid=tenant_uuid, api_version=api_version) - else: - # found GSLB object - req = deepcopy(existing_obj) - if 'groups' not in req: - req['groups'] = [] - groups = [group for group in req['groups'] - if group['name'] == data['group']['name']] - if not groups: - # did not find the group - req['groups'].append(data['group']) - else: - # just update the existing group with members - group = groups[0] - group_info_wo_members = deepcopy(data['group']) - group_info_wo_members.pop('members', None) - group.update(group_info_wo_members) - if 'members' not in group: - group['members'] = [] - new_members = [] - for patch_member in data['group'].get('members', []): - found = False - for m in group['members']: - if 'fqdn' in patch_member and m.get('fqdn', '') == patch_member['fqdn']: - found = True - break - elif m['ip']['addr'] == patch_member['ip']['addr']: - found = True - break - if not found: - new_members.append(patch_member) - else: - m.update(patch_member) - # add any new members - group['members'].extend(new_members) - cleanup_absent_fields(req) - changed = not avi_obj_cmp(req, existing_obj) - if changed and not check_mode: - obj_path = '%s/%s' % ('gslbservice', existing_obj['uuid']) - # added api version for AVI api call. - rsp = api.put(obj_path, data=req, tenant=tenant, - tenant_uuid=tenant_uuid, api_version=api_version) - return changed, rsp - - -def main(): - argument_specs = dict( - params=dict(type='dict'), - data=dict(type='dict'), - name=dict(type='str', required=True), - state=dict(default='present', - choices=['absent', 'present']) - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule(argument_spec=argument_specs) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or ansible>=2.8 is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - api_creds = AviCredentials() - api_creds.update_from_ansible_module(module) - api = ApiSession.get_session( - api_creds.controller, api_creds.username, password=api_creds.password, - timeout=api_creds.timeout, tenant=api_creds.tenant, - tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, - port=api_creds.port) - - tenant = api_creds.tenant - tenant_uuid = api_creds.tenant_uuid - params = module.params.get('params', None) - data = module.params.get('data', None) - gparams = deepcopy(params) if params else {} - gparams.update({'include_refs': '', 'include_name': ''}) - name = module.params.get('name', '') - state = module.params['state'] - # Get the api version from module. - api_version = api_creds.api_version - """ - state: present - 1. Check if the GSLB service is present - 2. If not then create the GSLB service with the member - 3. Check if the group exists - 4. if not then create the group with the member - 5. Check if the member is present - if not then add the member - state: absent - 1. check if GSLB service is present if not then exit - 2. check if group is present. if not then exit - 3. check if member is present. if present then remove it. - """ - obj_type = 'gslbservice' - # Added api version to call - existing_obj = api.get_object_by_name( - obj_type, name, tenant=tenant, tenant_uuid=tenant_uuid, - params={'include_refs': '', 'include_name': ''}, api_version=api_version) - check_mode = module.check_mode - if state == 'absent': - # Added api version to call - changed, rsp = delete_member(module, check_mode, api, tenant, - tenant_uuid, existing_obj, data, api_version) - else: - # Added api version to call - changed, rsp = add_member(module, check_mode, api, tenant, tenant_uuid, - existing_obj, data, name, api_version) - if check_mode or not changed: - return module.exit_json(changed=changed, obj=existing_obj) - return ansible_return(module, rsp, changed, req=data) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_hardwaresecuritymodulegroup.py b/plugins/modules/network/avi/avi_hardwaresecuritymodulegroup.py deleted file mode 100644 index e8502187bb..0000000000 --- a/plugins/modules/network/avi/avi_hardwaresecuritymodulegroup.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_hardwaresecuritymodulegroup -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of HardwareSecurityModuleGroup Avi RESTful Object -description: - - This module is used to configure HardwareSecurityModuleGroup object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - hsm: - description: - - Hardware security module configuration. - required: true - name: - description: - - Name of the hsm group configuration object. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the hsm group configuration object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create HardwareSecurityModuleGroup object - avi_hardwaresecuritymodulegroup: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_hardwaresecuritymodulegroup -""" - -RETURN = ''' -obj: - description: HardwareSecurityModuleGroup (api/hardwaresecuritymodulegroup) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - hsm=dict(type='dict', required=True), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'hardwaresecuritymodulegroup', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_healthmonitor.py b/plugins/modules/network/avi/avi_healthmonitor.py deleted file mode 100644 index de273fe1de..0000000000 --- a/plugins/modules/network/avi/avi_healthmonitor.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_healthmonitor -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of HealthMonitor Avi RESTful Object -description: - - This module is used to configure HealthMonitor object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - description: - description: - - User defined description for the object. - dns_monitor: - description: - - Healthmonitordns settings for healthmonitor. - external_monitor: - description: - - Healthmonitorexternal settings for healthmonitor. - failed_checks: - description: - - Number of continuous failed health checks before the server is marked down. - - Allowed values are 1-50. - - Default value when not specified in API or module is interpreted by Avi Controller as 2. - http_monitor: - description: - - Healthmonitorhttp settings for healthmonitor. - https_monitor: - description: - - Healthmonitorhttp settings for healthmonitor. - is_federated: - description: - - This field describes the object's replication scope. - - If the field is set to false, then the object is visible within the controller-cluster and its associated service-engines. - - If the field is set to true, then the object is replicated across the federation. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - monitor_port: - description: - - Use this port instead of the port defined for the server in the pool. - - If the monitor succeeds to this port, the load balanced traffic will still be sent to the port of the server defined within the pool. - - Allowed values are 1-65535. - - Special values are 0 - 'use server port'. - name: - description: - - A user friendly name for this health monitor. - required: true - radius_monitor: - description: - - Health monitor for radius. - - Field introduced in 18.2.3. - receive_timeout: - description: - - A valid response from the server is expected within the receive timeout window. - - This timeout must be less than the send interval. - - If server status is regularly flapping up and down, consider increasing this value. - - Allowed values are 1-2400. - - Default value when not specified in API or module is interpreted by Avi Controller as 4. - send_interval: - description: - - Frequency, in seconds, that monitors are sent to a server. - - Allowed values are 1-3600. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - sip_monitor: - description: - - Health monitor for sip. - - Field introduced in 17.2.8, 18.1.3, 18.2.1. - successful_checks: - description: - - Number of continuous successful health checks before server is marked up. - - Allowed values are 1-50. - - Default value when not specified in API or module is interpreted by Avi Controller as 2. - tcp_monitor: - description: - - Healthmonitortcp settings for healthmonitor. - tenant_ref: - description: - - It is a reference to an object of type tenant. - type: - description: - - Type of the health monitor. - - Enum options - HEALTH_MONITOR_PING, HEALTH_MONITOR_TCP, HEALTH_MONITOR_HTTP, HEALTH_MONITOR_HTTPS, HEALTH_MONITOR_EXTERNAL, HEALTH_MONITOR_UDP, - - HEALTH_MONITOR_DNS, HEALTH_MONITOR_GSLB, HEALTH_MONITOR_SIP, HEALTH_MONITOR_RADIUS. - required: true - udp_monitor: - description: - - Healthmonitorudp settings for healthmonitor. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the health monitor. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Create a HTTPS health monitor - avi_healthmonitor: - controller: 10.10.27.90 - username: admin - password: AviNetworks123! - https_monitor: - http_request: HEAD / HTTP/1.0 - http_response_code: - - HTTP_2XX - - HTTP_3XX - receive_timeout: 4 - failed_checks: 3 - send_interval: 10 - successful_checks: 3 - type: HEALTH_MONITOR_HTTPS - name: MyWebsite-HTTPS -""" - -RETURN = ''' -obj: - description: HealthMonitor (api/healthmonitor) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - description=dict(type='str',), - dns_monitor=dict(type='dict',), - external_monitor=dict(type='dict',), - failed_checks=dict(type='int',), - http_monitor=dict(type='dict',), - https_monitor=dict(type='dict',), - is_federated=dict(type='bool',), - monitor_port=dict(type='int',), - name=dict(type='str', required=True), - radius_monitor=dict(type='dict',), - receive_timeout=dict(type='int',), - send_interval=dict(type='int',), - sip_monitor=dict(type='dict',), - successful_checks=dict(type='int',), - tcp_monitor=dict(type='dict',), - tenant_ref=dict(type='str',), - type=dict(type='str', required=True), - udp_monitor=dict(type='dict',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'healthmonitor', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_httppolicyset.py b/plugins/modules/network/avi/avi_httppolicyset.py deleted file mode 100644 index 9bc047927a..0000000000 --- a/plugins/modules/network/avi/avi_httppolicyset.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_httppolicyset -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of HTTPPolicySet Avi RESTful Object -description: - - This module is used to configure HTTPPolicySet object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cloud_config_cksum: - description: - - Checksum of cloud configuration for pool. - - Internally set by cloud connector. - created_by: - description: - - Creator name. - description: - description: - - User defined description for the object. - http_request_policy: - description: - - Http request policy for the virtual service. - http_response_policy: - description: - - Http response policy for the virtual service. - http_security_policy: - description: - - Http security policy for the virtual service. - is_internal_policy: - description: - - Boolean flag to set is_internal_policy. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - name: - description: - - Name of the http policy set. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the http policy set. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Create a HTTP Policy set two switch between testpool1 and testpool2 - avi_httppolicyset: - controller: 10.10.27.90 - username: admin - password: AviNetworks123! - name: test-HTTP-Policy-Set - tenant_ref: admin - http_request_policy: - rules: - - index: 1 - enable: true - name: test-test1 - match: - path: - match_case: INSENSITIVE - match_str: - - /test1 - match_criteria: EQUALS - switching_action: - action: HTTP_SWITCHING_SELECT_POOL - status_code: HTTP_LOCAL_RESPONSE_STATUS_CODE_200 - pool_ref: "/api/pool?name=testpool1" - - index: 2 - enable: true - name: test-test2 - match: - path: - match_case: INSENSITIVE - match_str: - - /test2 - match_criteria: CONTAINS - switching_action: - action: HTTP_SWITCHING_SELECT_POOL - status_code: HTTP_LOCAL_RESPONSE_STATUS_CODE_200 - pool_ref: "/api/pool?name=testpool2" - is_internal_policy: false -""" - -RETURN = ''' -obj: - description: HTTPPolicySet (api/httppolicyset) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cloud_config_cksum=dict(type='str',), - created_by=dict(type='str',), - description=dict(type='str',), - http_request_policy=dict(type='dict',), - http_response_policy=dict(type='dict',), - http_security_policy=dict(type='dict',), - is_internal_policy=dict(type='bool',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'httppolicyset', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_ipaddrgroup.py b/plugins/modules/network/avi/avi_ipaddrgroup.py deleted file mode 100644 index b9f73a9321..0000000000 --- a/plugins/modules/network/avi/avi_ipaddrgroup.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_ipaddrgroup -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of IpAddrGroup Avi RESTful Object -description: - - This module is used to configure IpAddrGroup object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - addrs: - description: - - Configure ip address(es). - apic_epg_name: - description: - - Populate ip addresses from members of this cisco apic epg. - country_codes: - description: - - Populate the ip address ranges from the geo database for this country. - description: - description: - - User defined description for the object. - ip_ports: - description: - - Configure (ip address, port) tuple(s). - marathon_app_name: - description: - - Populate ip addresses from tasks of this marathon app. - marathon_service_port: - description: - - Task port associated with marathon service port. - - If marathon app has multiple service ports, this is required. - - Else, the first task port is used. - name: - description: - - Name of the ip address group. - required: true - prefixes: - description: - - Configure ip address prefix(es). - ranges: - description: - - Configure ip address range(s). - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the ip address group. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create an IP Address Group configuration - avi_ipaddrgroup: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - name: Client-Source-Block - prefixes: - - ip_addr: - addr: 10.0.0.0 - type: V4 - mask: 8 - - ip_addr: - addr: 172.16.0.0 - type: V4 - mask: 12 - - ip_addr: - addr: 192.168.0.0 - type: V4 - mask: 16 -""" - -RETURN = ''' -obj: - description: IpAddrGroup (api/ipaddrgroup) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - addrs=dict(type='list',), - apic_epg_name=dict(type='str',), - country_codes=dict(type='list',), - description=dict(type='str',), - ip_ports=dict(type='list',), - marathon_app_name=dict(type='str',), - marathon_service_port=dict(type='int',), - name=dict(type='str', required=True), - prefixes=dict(type='list',), - ranges=dict(type='list',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'ipaddrgroup', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_ipamdnsproviderprofile.py b/plugins/modules/network/avi/avi_ipamdnsproviderprofile.py deleted file mode 100644 index cfa0ad34bb..0000000000 --- a/plugins/modules/network/avi/avi_ipamdnsproviderprofile.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_ipamdnsproviderprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of IpamDnsProviderProfile Avi RESTful Object -description: - - This module is used to configure IpamDnsProviderProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - allocate_ip_in_vrf: - description: - - If this flag is set, only allocate ip from networks in the virtual service vrf. - - Applicable for avi vantage ipam only. - - Field introduced in 17.2.4. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - aws_profile: - description: - - Provider details if type is aws. - azure_profile: - description: - - Provider details if type is microsoft azure. - - Field introduced in 17.2.1. - custom_profile: - description: - - Provider details if type is custom. - - Field introduced in 17.1.1. - gcp_profile: - description: - - Provider details if type is google cloud. - infoblox_profile: - description: - - Provider details if type is infoblox. - internal_profile: - description: - - Provider details if type is avi. - name: - description: - - Name for the ipam/dns provider profile. - required: true - oci_profile: - description: - - Provider details for oracle cloud. - - Field introduced in 18.2.1,18.1.3. - openstack_profile: - description: - - Provider details if type is openstack. - proxy_configuration: - description: - - Field introduced in 17.1.1. - tenant_ref: - description: - - It is a reference to an object of type tenant. - tencent_profile: - description: - - Provider details for tencent cloud. - - Field introduced in 18.2.3. - type: - description: - - Provider type for the ipam/dns provider profile. - - Enum options - IPAMDNS_TYPE_INFOBLOX, IPAMDNS_TYPE_AWS, IPAMDNS_TYPE_OPENSTACK, IPAMDNS_TYPE_GCP, IPAMDNS_TYPE_INFOBLOX_DNS, IPAMDNS_TYPE_CUSTOM, - - IPAMDNS_TYPE_CUSTOM_DNS, IPAMDNS_TYPE_AZURE, IPAMDNS_TYPE_OCI, IPAMDNS_TYPE_TENCENT, IPAMDNS_TYPE_INTERNAL, IPAMDNS_TYPE_INTERNAL_DNS, - - IPAMDNS_TYPE_AWS_DNS, IPAMDNS_TYPE_AZURE_DNS. - required: true - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the ipam/dns provider profile. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create IPAM DNS provider setting - avi_ipamdnsproviderprofile: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - internal_profile: - dns_service_domain: - - domain_name: ashish.local - num_dns_ip: 1 - pass_through: true - record_ttl: 100 - - domain_name: guru.local - num_dns_ip: 1 - pass_through: true - record_ttl: 200 - ttl: 300 - name: Ashish-DNS - tenant_ref: Demo - type: IPAMDNS_TYPE_INTERNAL -""" - -RETURN = ''' -obj: - description: IpamDnsProviderProfile (api/ipamdnsproviderprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - allocate_ip_in_vrf=dict(type='bool',), - aws_profile=dict(type='dict',), - azure_profile=dict(type='dict',), - custom_profile=dict(type='dict',), - gcp_profile=dict(type='dict',), - infoblox_profile=dict(type='dict',), - internal_profile=dict(type='dict',), - name=dict(type='str', required=True), - oci_profile=dict(type='dict',), - openstack_profile=dict(type='dict',), - proxy_configuration=dict(type='dict',), - tenant_ref=dict(type='str',), - tencent_profile=dict(type='dict',), - type=dict(type='str', required=True), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'ipamdnsproviderprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_l4policyset.py b/plugins/modules/network/avi/avi_l4policyset.py deleted file mode 100644 index e4d069a97b..0000000000 --- a/plugins/modules/network/avi/avi_l4policyset.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_l4policyset -author: Chaitanya Deshpande (@chaitanyaavi) - -short_description: Module for setup of L4PolicySet Avi RESTful Object -description: - - This module is used to configure L4PolicySet object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - created_by: - description: - - Creator name. - - Field introduced in 17.2.7. - description: - description: - - Field introduced in 17.2.7. - is_internal_policy: - description: - - Field introduced in 17.2.7. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - l4_connection_policy: - description: - - Policy to apply when a new transport connection is setup. - - Field introduced in 17.2.7. - name: - description: - - Name of the l4 policy set. - - Field introduced in 17.2.7. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.2.7. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Id of the l4 policy set. - - Field introduced in 17.2.7. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create L4PolicySet object - avi_l4policyset: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_l4policyset -""" - -RETURN = ''' -obj: - description: L4PolicySet (api/l4policyset) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - created_by=dict(type='str',), - description=dict(type='str',), - is_internal_policy=dict(type='bool',), - l4_connection_policy=dict(type='dict',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'l4policyset', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_microservicegroup.py b/plugins/modules/network/avi/avi_microservicegroup.py deleted file mode 100644 index c1cb187e2d..0000000000 --- a/plugins/modules/network/avi/avi_microservicegroup.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_microservicegroup -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of MicroServiceGroup Avi RESTful Object -description: - - This module is used to configure MicroServiceGroup object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - created_by: - description: - - Creator name. - description: - description: - - User defined description for the object. - name: - description: - - Name of the microservice group. - required: true - service_refs: - description: - - Configure microservice(es). - - It is a reference to an object of type microservice. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the microservice group. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create a Microservice Group that can be used for setting up Network security policy - avi_microservicegroup: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - description: Group created by my Secure My App UI. - name: vs-msg-marketing - tenant_ref: admin -""" - -RETURN = ''' -obj: - description: MicroServiceGroup (api/microservicegroup) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - created_by=dict(type='str',), - description=dict(type='str',), - name=dict(type='str', required=True), - service_refs=dict(type='list',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'microservicegroup', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_network.py b/plugins/modules/network/avi/avi_network.py deleted file mode 100644 index b52d665caf..0000000000 --- a/plugins/modules/network/avi/avi_network.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_network -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Network Avi RESTful Object -description: - - This module is used to configure Network object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cloud_ref: - description: - - It is a reference to an object of type cloud. - configured_subnets: - description: - - List of subnet. - dhcp_enabled: - description: - - Select the ip address management scheme for this network. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - exclude_discovered_subnets: - description: - - When selected, excludes all discovered subnets in this network from consideration for virtual service placement. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - ip6_autocfg_enabled: - description: - - Enable ipv6 auto configuration. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - name: - description: - - Name of the object. - required: true - synced_from_se: - description: - - Boolean flag to set synced_from_se. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. - vcenter_dvs: - description: - - Boolean flag to set vcenter_dvs. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - vimgrnw_ref: - description: - - It is a reference to an object of type vimgrnwruntime. - vrf_context_ref: - description: - - It is a reference to an object of type vrfcontext. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create Network object - avi_network: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_network -""" - -RETURN = ''' -obj: - description: Network (api/network) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cloud_ref=dict(type='str',), - configured_subnets=dict(type='list',), - dhcp_enabled=dict(type='bool',), - exclude_discovered_subnets=dict(type='bool',), - ip6_autocfg_enabled=dict(type='bool',), - name=dict(type='str', required=True), - synced_from_se=dict(type='bool',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - vcenter_dvs=dict(type='bool',), - vimgrnw_ref=dict(type='str',), - vrf_context_ref=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'network', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_networkprofile.py b/plugins/modules/network/avi/avi_networkprofile.py deleted file mode 100644 index b71db84733..0000000000 --- a/plugins/modules/network/avi/avi_networkprofile.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_networkprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of NetworkProfile Avi RESTful Object -description: - - This module is used to configure NetworkProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - connection_mirror: - description: - - When enabled, avi mirrors all tcp fastpath connections to standby. - - Applicable only in legacy ha mode. - - Field introduced in 18.1.3,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - description: - description: - - User defined description for the object. - name: - description: - - The name of the network profile. - required: true - profile: - description: - - Networkprofileunion settings for networkprofile. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the network profile. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create a network profile for an UDP application - avi_networkprofile: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - name: System-UDP-Fast-Path - profile: - type: PROTOCOL_TYPE_UDP_FAST_PATH - udp_fast_path_profile: - per_pkt_loadbalance: false - session_idle_timeout: 10 - snat: true - tenant_ref: admin -""" - -RETURN = ''' -obj: - description: NetworkProfile (api/networkprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - connection_mirror=dict(type='bool',), - description=dict(type='str',), - name=dict(type='str', required=True), - profile=dict(type='dict', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'networkprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_networksecuritypolicy.py b/plugins/modules/network/avi/avi_networksecuritypolicy.py deleted file mode 100644 index 781e9f6a5c..0000000000 --- a/plugins/modules/network/avi/avi_networksecuritypolicy.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_networksecuritypolicy -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of NetworkSecurityPolicy Avi RESTful Object -description: - - This module is used to configure NetworkSecurityPolicy object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cloud_config_cksum: - description: - - Checksum of cloud configuration for network sec policy. - - Internally set by cloud connector. - created_by: - description: - - Creator name. - description: - description: - - User defined description for the object. - name: - description: - - Name of the object. - rules: - description: - - List of networksecurityrule. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create a network security policy to block clients represented by ip group known_attackers - avi_networksecuritypolicy: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - name: vs-gurutest-ns - rules: - - action: NETWORK_SECURITY_POLICY_ACTION_TYPE_DENY - age: 0 - enable: true - index: 1 - log: false - match: - client_ip: - group_refs: - - Demo:known_attackers - match_criteria: IS_IN - name: Rule 1 - tenant_ref: Demo -""" - -RETURN = ''' -obj: - description: NetworkSecurityPolicy (api/networksecuritypolicy) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cloud_config_cksum=dict(type='str',), - created_by=dict(type='str',), - description=dict(type='str',), - name=dict(type='str',), - rules=dict(type='list',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'networksecuritypolicy', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_pkiprofile.py b/plugins/modules/network/avi/avi_pkiprofile.py deleted file mode 100644 index 2b19ebd4d4..0000000000 --- a/plugins/modules/network/avi/avi_pkiprofile.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_pkiprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of PKIProfile Avi RESTful Object -description: - - This module is used to configure PKIProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - ca_certs: - description: - - List of certificate authorities (root and intermediate) trusted that is used for certificate validation. - created_by: - description: - - Creator name. - crl_check: - description: - - When enabled, avi will verify via crl checks that certificates in the trust chain have not been revoked. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - crls: - description: - - Certificate revocation lists. - ignore_peer_chain: - description: - - When enabled, avi will not trust intermediate and root certs presented by a client. - - Instead, only the chain certs configured in the certificate authority section will be used to verify trust of the client's cert. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - is_federated: - description: - - This field describes the object's replication scope. - - If the field is set to false, then the object is visible within the controller-cluster and its associated service-engines. - - If the field is set to true, then the object is replicated across the federation. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - name: - description: - - Name of the pki profile. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. - validate_only_leaf_crl: - description: - - When enabled, avi will only validate the revocation status of the leaf certificate using crl. - - To enable validation for the entire chain, disable this option and provide all the relevant crls. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create PKIProfile object - avi_pkiprofile: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_pkiprofile -""" - -RETURN = ''' -obj: - description: PKIProfile (api/pkiprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - ca_certs=dict(type='list',), - created_by=dict(type='str',), - crl_check=dict(type='bool',), - crls=dict(type='list',), - ignore_peer_chain=dict(type='bool',), - is_federated=dict(type='bool',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - validate_only_leaf_crl=dict(type='bool',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'pkiprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_pool.py b/plugins/modules/network/avi/avi_pool.py deleted file mode 100644 index fb51aed329..0000000000 --- a/plugins/modules/network/avi/avi_pool.py +++ /dev/null @@ -1,498 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_pool -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Pool Avi RESTful Object -description: - - This module is used to configure Pool object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - a_pool: - description: - - Name of container cloud application that constitutes a pool in a a-b pool configuration, if different from vs app. - - Field deprecated in 18.1.2. - ab_pool: - description: - - A/b pool configuration. - - Field deprecated in 18.1.2. - ab_priority: - description: - - Priority of this pool in a a-b pool pair. - - Internally used. - - Field deprecated in 18.1.2. - analytics_policy: - description: - - Determines analytics settings for the pool. - - Field introduced in 18.1.5, 18.2.1. - analytics_profile_ref: - description: - - Specifies settings related to analytics. - - It is a reference to an object of type analyticsprofile. - - Field introduced in 18.1.4,18.2.1. - apic_epg_name: - description: - - Synchronize cisco apic epg members with pool servers. - application_persistence_profile_ref: - description: - - Persistence will ensure the same user sticks to the same server for a desired duration of time. - - It is a reference to an object of type applicationpersistenceprofile. - autoscale_launch_config_ref: - description: - - If configured then avi will trigger orchestration of pool server creation and deletion. - - It is only supported for container clouds like mesos, openshift, kubernetes, docker, etc. - - It is a reference to an object of type autoscalelaunchconfig. - autoscale_networks: - description: - - Network ids for the launch configuration. - autoscale_policy_ref: - description: - - Reference to server autoscale policy. - - It is a reference to an object of type serverautoscalepolicy. - capacity_estimation: - description: - - Inline estimation of capacity of servers. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - capacity_estimation_ttfb_thresh: - description: - - The maximum time-to-first-byte of a server. - - Allowed values are 1-5000. - - Special values are 0 - 'automatic'. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - cloud_config_cksum: - description: - - Checksum of cloud configuration for pool. - - Internally set by cloud connector. - cloud_ref: - description: - - It is a reference to an object of type cloud. - conn_pool_properties: - description: - - Connection pool properties. - - Field introduced in 18.2.1. - connection_ramp_duration: - description: - - Duration for which new connections will be gradually ramped up to a server recently brought online. - - Useful for lb algorithms that are least connection based. - - Allowed values are 1-300. - - Special values are 0 - 'immediate'. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - created_by: - description: - - Creator name. - default_server_port: - description: - - Traffic sent to servers will use this destination server port unless overridden by the server's specific port attribute. - - The ssl checkbox enables avi to server encryption. - - Allowed values are 1-65535. - - Default value when not specified in API or module is interpreted by Avi Controller as 80. - delete_server_on_dns_refresh: - description: - - Indicates whether existing ips are disabled(false) or deleted(true) on dns hostname refreshdetail -- on a dns refresh, some ips set on pool may - - no longer be returned by the resolver. - - These ips are deleted from the pool when this knob is set to true. - - They are disabled, if the knob is set to false. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - description: - description: - - A description of the pool. - domain_name: - description: - - Comma separated list of domain names which will be used to verify the common names or subject alternative names presented by server certificates. - - It is performed only when common name check host_check_enabled is enabled. - east_west: - description: - - Inherited config from virtualservice. - type: bool - enabled: - description: - - Enable or disable the pool. - - Disabling will terminate all open connections and pause health monitors. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - external_autoscale_groups: - description: - - Names of external auto-scale groups for pool servers. - - Currently available only for aws and azure. - - Field introduced in 17.1.2. - fail_action: - description: - - Enable an action - close connection, http redirect or local http response - when a pool failure happens. - - By default, a connection will be closed, in case the pool experiences a failure. - fewest_tasks_feedback_delay: - description: - - Periodicity of feedback for fewest tasks server selection algorithm. - - Allowed values are 1-300. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - graceful_disable_timeout: - description: - - Used to gracefully disable a server. - - Virtual service waits for the specified time before terminating the existing connections to the servers that are disabled. - - Allowed values are 1-7200. - - Special values are 0 - 'immediate', -1 - 'infinite'. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - gslb_sp_enabled: - description: - - Indicates if the pool is a site-persistence pool. - - Field introduced in 17.2.1. - type: bool - health_monitor_refs: - description: - - Verify server health by applying one or more health monitors. - - Active monitors generate synthetic traffic from each service engine and mark a server up or down based on the response. - - The passive monitor listens only to client to server communication. - - It raises or lowers the ratio of traffic destined to a server based on successful responses. - - It is a reference to an object of type healthmonitor. - host_check_enabled: - description: - - Enable common name check for server certificate. - - If enabled and no explicit domain name is specified, avi will use the incoming host header to do the match. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - inline_health_monitor: - description: - - The passive monitor will monitor client to server connections and requests and adjust traffic load to servers based on successful responses. - - This may alter the expected behavior of the lb method, such as round robin. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - ipaddrgroup_ref: - description: - - Use list of servers from ip address group. - - It is a reference to an object of type ipaddrgroup. - lb_algorithm: - description: - - The load balancing algorithm will pick a server within the pool's list of available servers. - - Enum options - LB_ALGORITHM_LEAST_CONNECTIONS, LB_ALGORITHM_ROUND_ROBIN, LB_ALGORITHM_FASTEST_RESPONSE, LB_ALGORITHM_CONSISTENT_HASH, - - LB_ALGORITHM_LEAST_LOAD, LB_ALGORITHM_FEWEST_SERVERS, LB_ALGORITHM_RANDOM, LB_ALGORITHM_FEWEST_TASKS, LB_ALGORITHM_NEAREST_SERVER, - - LB_ALGORITHM_CORE_AFFINITY, LB_ALGORITHM_TOPOLOGY. - - Default value when not specified in API or module is interpreted by Avi Controller as LB_ALGORITHM_LEAST_CONNECTIONS. - lb_algorithm_consistent_hash_hdr: - description: - - Http header name to be used for the hash key. - lb_algorithm_core_nonaffinity: - description: - - Degree of non-affinity for core affinity based server selection. - - Allowed values are 1-65535. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 2. - lb_algorithm_hash: - description: - - Criteria used as a key for determining the hash between the client and server. - - Enum options - LB_ALGORITHM_CONSISTENT_HASH_SOURCE_IP_ADDRESS, LB_ALGORITHM_CONSISTENT_HASH_SOURCE_IP_ADDRESS_AND_PORT, - - LB_ALGORITHM_CONSISTENT_HASH_URI, LB_ALGORITHM_CONSISTENT_HASH_CUSTOM_HEADER, LB_ALGORITHM_CONSISTENT_HASH_CUSTOM_STRING, - - LB_ALGORITHM_CONSISTENT_HASH_CALLID. - - Default value when not specified in API or module is interpreted by Avi Controller as LB_ALGORITHM_CONSISTENT_HASH_SOURCE_IP_ADDRESS. - lookup_server_by_name: - description: - - Allow server lookup by name. - - Field introduced in 17.1.11,17.2.4. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - max_concurrent_connections_per_server: - description: - - The maximum number of concurrent connections allowed to each server within the pool. - - Note applied value will be no less than the number of service engines that the pool is placed on. - - If set to 0, no limit is applied. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - max_conn_rate_per_server: - description: - - Rate limit connections to each server. - min_health_monitors_up: - description: - - Minimum number of health monitors in up state to mark server up. - - Field introduced in 18.2.1, 17.2.12. - min_servers_up: - description: - - Minimum number of servers in up state for marking the pool up. - - Field introduced in 18.2.1, 17.2.12. - name: - description: - - The name of the pool. - required: true - networks: - description: - - (internal-use) networks designated as containing servers for this pool. - - The servers may be further narrowed down by a filter. - - This field is used internally by avi, not editable by the user. - nsx_securitygroup: - description: - - A list of nsx service groups where the servers for the pool are created. - - Field introduced in 17.1.1. - pki_profile_ref: - description: - - Avi will validate the ssl certificate present by a server against the selected pki profile. - - It is a reference to an object of type pkiprofile. - placement_networks: - description: - - Manually select the networks and subnets used to provide reachability to the pool's servers. - - Specify the subnet using the following syntax 10-1-1-0/24. - - Use static routes in vrf configuration when pool servers are not directly connected butroutable from the service engine. - prst_hdr_name: - description: - - Header name for custom header persistence. - - Field deprecated in 18.1.2. - request_queue_depth: - description: - - Minimum number of requests to be queued when pool is full. - - Default value when not specified in API or module is interpreted by Avi Controller as 128. - request_queue_enabled: - description: - - Enable request queue when pool is full. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - rewrite_host_header_to_server_name: - description: - - Rewrite incoming host header to server name of the server to which the request is proxied. - - Enabling this feature rewrites host header for requests to all servers in the pool. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - rewrite_host_header_to_sni: - description: - - If sni server name is specified, rewrite incoming host header to the sni server name. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - server_auto_scale: - description: - - Server autoscale. - - Not used anymore. - - Field deprecated in 18.1.2. - type: bool - server_count: - description: - - Field deprecated in 18.2.1. - server_name: - description: - - Fully qualified dns hostname which will be used in the tls sni extension in server connections if sni is enabled. - - If no value is specified, avi will use the incoming host header instead. - server_reselect: - description: - - Server reselect configuration for http requests. - server_timeout: - description: - - Server timeout value specifies the time within which a server connection needs to be established and a request-response exchange completes - - between avi and the server. - - Value of 0 results in using default timeout of 60 minutes. - - Allowed values are 0-3600000. - - Field introduced in 18.1.5,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - servers: - description: - - The pool directs load balanced traffic to this list of destination servers. - - The servers can be configured by ip address, name, network or via ip address group. - service_metadata: - description: - - Metadata pertaining to the service provided by this pool. - - In openshift/kubernetes environments, app metadata info is stored. - - Any user input to this field will be overwritten by avi vantage. - - Field introduced in 17.2.14,18.1.5,18.2.1. - sni_enabled: - description: - - Enable tls sni for server connections. - - If disabled, avi will not send the sni extension as part of the handshake. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - ssl_key_and_certificate_ref: - description: - - Service engines will present a client ssl certificate to the server. - - It is a reference to an object of type sslkeyandcertificate. - ssl_profile_ref: - description: - - When enabled, avi re-encrypts traffic to the backend servers. - - The specific ssl profile defines which ciphers and ssl versions will be supported. - - It is a reference to an object of type sslprofile. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - use_service_port: - description: - - Do not translate the client's destination port when sending the connection to the server. - - The pool or servers specified service port will still be used for health monitoring. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - uuid: - description: - - Uuid of the pool. - vrf_ref: - description: - - Virtual routing context that the pool is bound to. - - This is used to provide the isolation of the set of networks the pool is attached to. - - The pool inherits the virtual routing context of the virtual service, and this field is used only internally, and is set by pb-transform. - - It is a reference to an object of type vrfcontext. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Create a Pool with two servers and HTTP monitor - avi_pool: - controller: 10.10.1.20 - username: avi_user - password: avi_password - name: testpool1 - description: testpool1 - state: present - health_monitor_refs: - - '/api/healthmonitor?name=System-HTTP' - servers: - - ip: - addr: 10.10.2.20 - type: V4 - - ip: - addr: 10.10.2.21 - type: V4 - -- name: Patch pool with a single server using patch op and avi_credentials - avi_pool: - avi_api_update_method: patch - avi_api_patch_op: delete - avi_credentials: "{{avi_credentials}}" - name: test-pool - servers: - - ip: - addr: 10.90.64.13 - type: 'V4' - register: pool - when: - - state | default("present") == "present" -""" - -RETURN = ''' -obj: - description: Pool (api/pool) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - a_pool=dict(type='str',), - ab_pool=dict(type='dict',), - ab_priority=dict(type='int',), - analytics_policy=dict(type='dict',), - analytics_profile_ref=dict(type='str',), - apic_epg_name=dict(type='str',), - application_persistence_profile_ref=dict(type='str',), - autoscale_launch_config_ref=dict(type='str',), - autoscale_networks=dict(type='list',), - autoscale_policy_ref=dict(type='str',), - capacity_estimation=dict(type='bool',), - capacity_estimation_ttfb_thresh=dict(type='int',), - cloud_config_cksum=dict(type='str',), - cloud_ref=dict(type='str',), - conn_pool_properties=dict(type='dict',), - connection_ramp_duration=dict(type='int',), - created_by=dict(type='str',), - default_server_port=dict(type='int',), - delete_server_on_dns_refresh=dict(type='bool',), - description=dict(type='str',), - domain_name=dict(type='list',), - east_west=dict(type='bool',), - enabled=dict(type='bool',), - external_autoscale_groups=dict(type='list',), - fail_action=dict(type='dict',), - fewest_tasks_feedback_delay=dict(type='int',), - graceful_disable_timeout=dict(type='int',), - gslb_sp_enabled=dict(type='bool',), - health_monitor_refs=dict(type='list',), - host_check_enabled=dict(type='bool',), - inline_health_monitor=dict(type='bool',), - ipaddrgroup_ref=dict(type='str',), - lb_algorithm=dict(type='str',), - lb_algorithm_consistent_hash_hdr=dict(type='str',), - lb_algorithm_core_nonaffinity=dict(type='int',), - lb_algorithm_hash=dict(type='str',), - lookup_server_by_name=dict(type='bool',), - max_concurrent_connections_per_server=dict(type='int',), - max_conn_rate_per_server=dict(type='dict',), - min_health_monitors_up=dict(type='int',), - min_servers_up=dict(type='int',), - name=dict(type='str', required=True), - networks=dict(type='list',), - nsx_securitygroup=dict(type='list',), - pki_profile_ref=dict(type='str',), - placement_networks=dict(type='list',), - prst_hdr_name=dict(type='str',), - request_queue_depth=dict(type='int',), - request_queue_enabled=dict(type='bool',), - rewrite_host_header_to_server_name=dict(type='bool',), - rewrite_host_header_to_sni=dict(type='bool',), - server_auto_scale=dict(type='bool',), - server_count=dict(type='int',), - server_name=dict(type='str',), - server_reselect=dict(type='dict',), - server_timeout=dict(type='int',), - servers=dict(type='list',), - service_metadata=dict(type='str',), - sni_enabled=dict(type='bool',), - ssl_key_and_certificate_ref=dict(type='str',), - ssl_profile_ref=dict(type='str',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - use_service_port=dict(type='bool',), - uuid=dict(type='str',), - vrf_ref=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'pool', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_poolgroup.py b/plugins/modules/network/avi/avi_poolgroup.py deleted file mode 100644 index 8cf97197fe..0000000000 --- a/plugins/modules/network/avi/avi_poolgroup.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_poolgroup -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of PoolGroup Avi RESTful Object -description: - - This module is used to configure PoolGroup object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cloud_config_cksum: - description: - - Checksum of cloud configuration for poolgroup. - - Internally set by cloud connector. - cloud_ref: - description: - - It is a reference to an object of type cloud. - created_by: - description: - - Name of the user who created the object. - deployment_policy_ref: - description: - - When setup autoscale manager will automatically promote new pools into production when deployment goals are met. - - It is a reference to an object of type poolgroupdeploymentpolicy. - description: - description: - - Description of pool group. - fail_action: - description: - - Enable an action - close connection, http redirect, or local http response - when a pool group failure happens. - - By default, a connection will be closed, in case the pool group experiences a failure. - implicit_priority_labels: - description: - - Whether an implicit set of priority labels is generated. - - Field introduced in 17.1.9,17.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - members: - description: - - List of pool group members object of type poolgroupmember. - min_servers: - description: - - The minimum number of servers to distribute traffic to. - - Allowed values are 1-65535. - - Special values are 0 - 'disable'. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - name: - description: - - The name of the pool group. - required: true - priority_labels_ref: - description: - - Uuid of the priority labels. - - If not provided, pool group member priority label will be interpreted as a number with a larger number considered higher priority. - - It is a reference to an object of type prioritylabels. - service_metadata: - description: - - Metadata pertaining to the service provided by this poolgroup. - - In openshift/kubernetes environments, app metadata info is stored. - - Any user input to this field will be overwritten by avi vantage. - - Field introduced in 17.2.14,18.1.5,18.2.1. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the pool group. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create PoolGroup object - avi_poolgroup: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_poolgroup -""" - -RETURN = ''' -obj: - description: PoolGroup (api/poolgroup) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cloud_config_cksum=dict(type='str',), - cloud_ref=dict(type='str',), - created_by=dict(type='str',), - deployment_policy_ref=dict(type='str',), - description=dict(type='str',), - fail_action=dict(type='dict',), - implicit_priority_labels=dict(type='bool',), - members=dict(type='list',), - min_servers=dict(type='int',), - name=dict(type='str', required=True), - priority_labels_ref=dict(type='str',), - service_metadata=dict(type='str',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'poolgroup', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_poolgroupdeploymentpolicy.py b/plugins/modules/network/avi/avi_poolgroupdeploymentpolicy.py deleted file mode 100644 index c52400466c..0000000000 --- a/plugins/modules/network/avi/avi_poolgroupdeploymentpolicy.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_poolgroupdeploymentpolicy -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of PoolGroupDeploymentPolicy Avi RESTful Object -description: - - This module is used to configure PoolGroupDeploymentPolicy object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - auto_disable_old_prod_pools: - description: - - It will automatically disable old production pools once there is a new production candidate. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - description: - description: - - User defined description for the object. - evaluation_duration: - description: - - Duration of evaluation period for automatic deployment. - - Allowed values are 60-86400. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - name: - description: - - The name of the pool group deployment policy. - required: true - rules: - description: - - List of pgdeploymentrule. - scheme: - description: - - Deployment scheme. - - Enum options - BLUE_GREEN, CANARY. - - Default value when not specified in API or module is interpreted by Avi Controller as BLUE_GREEN. - target_test_traffic_ratio: - description: - - Target traffic ratio before pool is made production. - - Allowed values are 1-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 100. - tenant_ref: - description: - - It is a reference to an object of type tenant. - test_traffic_ratio_rampup: - description: - - Ratio of the traffic that is sent to the pool under test. - - Test ratio of 100 means blue green. - - Allowed values are 1-100. - - Default value when not specified in API or module is interpreted by Avi Controller as 100. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the pool group deployment policy. - webhook_ref: - description: - - Webhook configured with url that avi controller will pass back information about pool group, old and new pool information and current deployment - - rule results. - - It is a reference to an object of type webhook. - - Field introduced in 17.1.1. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create PoolGroupDeploymentPolicy object - avi_poolgroupdeploymentpolicy: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_poolgroupdeploymentpolicy -""" - -RETURN = ''' -obj: - description: PoolGroupDeploymentPolicy (api/poolgroupdeploymentpolicy) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - auto_disable_old_prod_pools=dict(type='bool',), - description=dict(type='str',), - evaluation_duration=dict(type='int',), - name=dict(type='str', required=True), - rules=dict(type='list',), - scheme=dict(type='str',), - target_test_traffic_ratio=dict(type='int',), - tenant_ref=dict(type='str',), - test_traffic_ratio_rampup=dict(type='int',), - url=dict(type='str',), - uuid=dict(type='str',), - webhook_ref=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'poolgroupdeploymentpolicy', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_prioritylabels.py b/plugins/modules/network/avi/avi_prioritylabels.py deleted file mode 100644 index 2ecbaf42c4..0000000000 --- a/plugins/modules/network/avi/avi_prioritylabels.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_prioritylabels -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of PriorityLabels Avi RESTful Object -description: - - This module is used to configure PriorityLabels object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cloud_ref: - description: - - It is a reference to an object of type cloud. - description: - description: - - A description of the priority labels. - equivalent_labels: - description: - - Equivalent priority labels in descending order. - name: - description: - - The name of the priority labels. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the priority labels. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create PriorityLabels object - avi_prioritylabels: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_prioritylabels -""" - -RETURN = ''' -obj: - description: PriorityLabels (api/prioritylabels) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cloud_ref=dict(type='str',), - description=dict(type='str',), - equivalent_labels=dict(type='list',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'prioritylabels', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_role.py b/plugins/modules/network/avi/avi_role.py deleted file mode 100644 index 4184fce498..0000000000 --- a/plugins/modules/network/avi/avi_role.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_role -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Role Avi RESTful Object -description: - - This module is used to configure Role object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - name: - description: - - Name of the object. - required: true - privileges: - description: - - List of permission. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create Role object - avi_role: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_role -""" - -RETURN = ''' -obj: - description: Role (api/role) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - name=dict(type='str', required=True), - privileges=dict(type='list',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'role', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_scheduler.py b/plugins/modules/network/avi/avi_scheduler.py deleted file mode 100644 index c79fabd785..0000000000 --- a/plugins/modules/network/avi/avi_scheduler.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_scheduler -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Scheduler Avi RESTful Object -description: - - This module is used to configure Scheduler object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - backup_config_ref: - description: - - Backup configuration to be executed by this scheduler. - - It is a reference to an object of type backupconfiguration. - enabled: - description: - - Boolean flag to set enabled. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - end_date_time: - description: - - Scheduler end date and time. - frequency: - description: - - Frequency at which custom scheduler will run. - - Allowed values are 0-60. - frequency_unit: - description: - - Unit at which custom scheduler will run. - - Enum options - SCHEDULER_FREQUENCY_UNIT_MIN, SCHEDULER_FREQUENCY_UNIT_HOUR, SCHEDULER_FREQUENCY_UNIT_DAY, SCHEDULER_FREQUENCY_UNIT_WEEK, - - SCHEDULER_FREQUENCY_UNIT_MONTH. - name: - description: - - Name of scheduler. - required: true - run_mode: - description: - - Scheduler run mode. - - Enum options - RUN_MODE_PERIODIC, RUN_MODE_AT, RUN_MODE_NOW. - run_script_ref: - description: - - Control script to be executed by this scheduler. - - It is a reference to an object of type alertscriptconfig. - scheduler_action: - description: - - Define scheduler action. - - Enum options - SCHEDULER_ACTION_RUN_A_SCRIPT, SCHEDULER_ACTION_BACKUP. - - Default value when not specified in API or module is interpreted by Avi Controller as SCHEDULER_ACTION_BACKUP. - start_date_time: - description: - - Scheduler start date and time. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create Scheduler object - avi_scheduler: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_scheduler -""" - -RETURN = ''' -obj: - description: Scheduler (api/scheduler) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - backup_config_ref=dict(type='str',), - enabled=dict(type='bool',), - end_date_time=dict(type='str',), - frequency=dict(type='int',), - frequency_unit=dict(type='str',), - name=dict(type='str', required=True), - run_mode=dict(type='str',), - run_script_ref=dict(type='str',), - scheduler_action=dict(type='str',), - start_date_time=dict(type='str',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'scheduler', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_seproperties.py b/plugins/modules/network/avi/avi_seproperties.py deleted file mode 100644 index 0eb92eeec6..0000000000 --- a/plugins/modules/network/avi/avi_seproperties.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_seproperties -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of SeProperties Avi RESTful Object -description: - - This module is used to configure SeProperties object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - se_agent_properties: - description: - - Seagentproperties settings for seproperties. - se_bootup_properties: - description: - - Sebootupproperties settings for seproperties. - se_runtime_properties: - description: - - Seruntimeproperties settings for seproperties. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. - - Default value when not specified in API or module is interpreted by Avi Controller as default. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create SeProperties object - avi_seproperties: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_seproperties -""" - -RETURN = ''' -obj: - description: SeProperties (api/seproperties) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - se_agent_properties=dict(type='dict',), - se_bootup_properties=dict(type='dict',), - se_runtime_properties=dict(type='dict',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'seproperties', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_serverautoscalepolicy.py b/plugins/modules/network/avi/avi_serverautoscalepolicy.py deleted file mode 100644 index 640258a3e6..0000000000 --- a/plugins/modules/network/avi/avi_serverautoscalepolicy.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_serverautoscalepolicy -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ServerAutoScalePolicy Avi RESTful Object -description: - - This module is used to configure ServerAutoScalePolicy object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - description: - description: - - User defined description for the object. - intelligent_autoscale: - description: - - Use avi intelligent autoscale algorithm where autoscale is performed by comparing load on the pool against estimated capacity of all the servers. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - intelligent_scalein_margin: - description: - - Maximum extra capacity as percentage of load used by the intelligent scheme. - - Scalein is triggered when available capacity is more than this margin. - - Allowed values are 1-99. - - Default value when not specified in API or module is interpreted by Avi Controller as 40. - intelligent_scaleout_margin: - description: - - Minimum extra capacity as percentage of load used by the intelligent scheme. - - Scaleout is triggered when available capacity is less than this margin. - - Allowed values are 1-99. - - Default value when not specified in API or module is interpreted by Avi Controller as 20. - max_scalein_adjustment_step: - description: - - Maximum number of servers to scalein simultaneously. - - The actual number of servers to scalein is chosen such that target number of servers is always more than or equal to the min_size. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - max_scaleout_adjustment_step: - description: - - Maximum number of servers to scaleout simultaneously. - - The actual number of servers to scaleout is chosen such that target number of servers is always less than or equal to the max_size. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - max_size: - description: - - Maximum number of servers after scaleout. - - Allowed values are 0-400. - min_size: - description: - - No scale-in happens once number of operationally up servers reach min_servers. - - Allowed values are 0-400. - name: - description: - - Name of the object. - required: true - scalein_alertconfig_refs: - description: - - Trigger scalein when alerts due to any of these alert configurations are raised. - - It is a reference to an object of type alertconfig. - scalein_cooldown: - description: - - Cooldown period during which no new scalein is triggered to allow previous scalein to successfully complete. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - scaleout_alertconfig_refs: - description: - - Trigger scaleout when alerts due to any of these alert configurations are raised. - - It is a reference to an object of type alertconfig. - scaleout_cooldown: - description: - - Cooldown period during which no new scaleout is triggered to allow previous scaleout to successfully complete. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - use_predicted_load: - description: - - Use predicted load rather than current load. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create ServerAutoScalePolicy object - avi_serverautoscalepolicy: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_serverautoscalepolicy -""" - -RETURN = ''' -obj: - description: ServerAutoScalePolicy (api/serverautoscalepolicy) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - description=dict(type='str',), - intelligent_autoscale=dict(type='bool',), - intelligent_scalein_margin=dict(type='int',), - intelligent_scaleout_margin=dict(type='int',), - max_scalein_adjustment_step=dict(type='int',), - max_scaleout_adjustment_step=dict(type='int',), - max_size=dict(type='int',), - min_size=dict(type='int',), - name=dict(type='str', required=True), - scalein_alertconfig_refs=dict(type='list',), - scalein_cooldown=dict(type='int',), - scaleout_alertconfig_refs=dict(type='list',), - scaleout_cooldown=dict(type='int',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - use_predicted_load=dict(type='bool',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'serverautoscalepolicy', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_serviceengine.py b/plugins/modules/network/avi/avi_serviceengine.py deleted file mode 100644 index e70722be81..0000000000 --- a/plugins/modules/network/avi/avi_serviceengine.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_serviceengine -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ServiceEngine Avi RESTful Object -description: - - This module is used to configure ServiceEngine object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - availability_zone: - description: - - Availability_zone of serviceengine. - cloud_ref: - description: - - It is a reference to an object of type cloud. - container_mode: - description: - - Boolean flag to set container_mode. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - container_type: - description: - - Enum options - container_type_bridge, container_type_host, container_type_host_dpdk. - - Default value when not specified in API or module is interpreted by Avi Controller as CONTAINER_TYPE_HOST. - controller_created: - description: - - Boolean flag to set controller_created. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - controller_ip: - description: - - Controller_ip of serviceengine. - data_vnics: - description: - - List of vnic. - enable_state: - description: - - Inorder to disable se set this field appropriately. - - Enum options - SE_STATE_ENABLED, SE_STATE_DISABLED_FOR_PLACEMENT, SE_STATE_DISABLED, SE_STATE_DISABLED_FORCE. - - Default value when not specified in API or module is interpreted by Avi Controller as SE_STATE_ENABLED. - flavor: - description: - - Flavor of serviceengine. - host_ref: - description: - - It is a reference to an object of type vimgrhostruntime. - hypervisor: - description: - - Enum options - default, vmware_esx, kvm, vmware_vsan, xen. - mgmt_vnic: - description: - - Vnic settings for serviceengine. - name: - description: - - Name of the object. - - Default value when not specified in API or module is interpreted by Avi Controller as VM name unknown. - resources: - description: - - Seresources settings for serviceengine. - se_group_ref: - description: - - It is a reference to an object of type serviceenginegroup. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create ServiceEngine object - avi_serviceengine: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_serviceengine -""" - -RETURN = ''' -obj: - description: ServiceEngine (api/serviceengine) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - availability_zone=dict(type='str',), - cloud_ref=dict(type='str',), - container_mode=dict(type='bool',), - container_type=dict(type='str',), - controller_created=dict(type='bool',), - controller_ip=dict(type='str',), - data_vnics=dict(type='list',), - enable_state=dict(type='str',), - flavor=dict(type='str',), - host_ref=dict(type='str',), - hypervisor=dict(type='str',), - mgmt_vnic=dict(type='dict',), - name=dict(type='str',), - resources=dict(type='dict',), - se_group_ref=dict(type='str',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'serviceengine', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_serviceenginegroup.py b/plugins/modules/network/avi/avi_serviceenginegroup.py deleted file mode 100644 index e0739777c5..0000000000 --- a/plugins/modules/network/avi/avi_serviceenginegroup.py +++ /dev/null @@ -1,1076 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_serviceenginegroup -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of ServiceEngineGroup Avi RESTful Object -description: - - This module is used to configure ServiceEngineGroup object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - accelerated_networking: - description: - - Enable accelerated networking option for azure se. - - Accelerated networking enables single root i/o virtualization (sr-iov) to a se vm. - - This improves networking performance. - - Field introduced in 17.2.14,18.1.5,18.2.1. - type: bool - active_standby: - description: - - Service engines in active/standby mode for ha failover. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - additional_config_memory: - description: - - Indicates the percent of config memory used for config updates. - - Allowed values are 0-90. - - Field deprecated in 18.1.2. - - Field introduced in 18.1.1. - advertise_backend_networks: - description: - - Advertise reach-ability of backend server networks via adc through bgp for default gateway feature. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - aggressive_failure_detection: - description: - - Enable aggressive failover configuration for ha. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - algo: - description: - - In compact placement, virtual services are placed on existing ses until max_vs_per_se limit is reached. - - Enum options - PLACEMENT_ALGO_PACKED, PLACEMENT_ALGO_DISTRIBUTED. - - Default value when not specified in API or module is interpreted by Avi Controller as PLACEMENT_ALGO_PACKED. - allow_burst: - description: - - Allow ses to be created using burst license. - - Field introduced in 17.2.5. - type: bool - app_cache_percent: - description: - - A percent value of total se memory reserved for application caching. - - This is an se bootup property and requires se restart. - - Allowed values are 0 - 100. - - Special values are 0- 'disable'. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - app_learning_memory_percent: - description: - - A percent value of total se memory reserved for application learning. - - This is an se bootup property and requires se restart. - - Allowed values are 0 - 10. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - archive_shm_limit: - description: - - Amount of se memory in gb until which shared memory is collected in core archive. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 8. - async_ssl: - description: - - Ssl handshakes will be handled by dedicated ssl threads. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - async_ssl_threads: - description: - - Number of async ssl threads per se_dp. - - Allowed values are 1-16. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - auto_rebalance: - description: - - If set, virtual services will be automatically migrated when load on an se is less than minimum or more than maximum thresholds. - - Only alerts are generated when the auto_rebalance is not set. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - auto_rebalance_capacity_per_se: - description: - - Capacities of se for auto rebalance for each criteria. - - Field introduced in 17.2.4. - auto_rebalance_criteria: - description: - - Set of criteria for se auto rebalance. - - Enum options - SE_AUTO_REBALANCE_CPU, SE_AUTO_REBALANCE_PPS, SE_AUTO_REBALANCE_MBPS, SE_AUTO_REBALANCE_OPEN_CONNS, SE_AUTO_REBALANCE_CPS. - - Field introduced in 17.2.3. - auto_rebalance_interval: - description: - - Frequency of rebalance, if 'auto rebalance' is enabled. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - auto_redistribute_active_standby_load: - description: - - Redistribution of virtual services from the takeover se to the replacement se can cause momentary traffic loss. - - If the auto-redistribute load option is left in its default off state, any desired rebalancing requires calls to rest api. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - bgp_state_update_interval: - description: - - Bgp peer state update interval. - - Allowed values are 5-100. - - Field introduced in 17.2.14,18.1.5,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - buffer_se: - description: - - Excess service engine capacity provisioned for ha failover. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - cloud_ref: - description: - - It is a reference to an object of type cloud. - config_debugs_on_all_cores: - description: - - Enable config debugs on all cores of se. - - Field introduced in 17.2.13,18.1.5,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - connection_memory_percentage: - description: - - Percentage of memory for connection state. - - This will come at the expense of memory used for http in-memory cache. - - Allowed values are 10-90. - - Default value when not specified in API or module is interpreted by Avi Controller as 50. - cpu_reserve: - description: - - Boolean flag to set cpu_reserve. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - cpu_socket_affinity: - description: - - Allocate all the cpu cores for the service engine virtual machines on the same cpu socket. - - Applicable only for vcenter cloud. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - custom_securitygroups_data: - description: - - Custom security groups to be associated with data vnics for se instances in openstack and aws clouds. - - Field introduced in 17.1.3. - custom_securitygroups_mgmt: - description: - - Custom security groups to be associated with management vnic for se instances in openstack and aws clouds. - - Field introduced in 17.1.3. - custom_tag: - description: - - Custom tag will be used to create the tags for se instance in aws. - - Note this is not the same as the prefix for se name. - data_network_id: - description: - - Subnet used to spin up the data nic for service engines, used only for azure cloud. - - Overrides the cloud level setting for service engine subnet. - - Field introduced in 18.2.3. - datascript_timeout: - description: - - Number of instructions before datascript times out. - - Allowed values are 0-100000000. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 1000000. - dedicated_dispatcher_core: - description: - - Dedicate the core that handles packet receive/transmit from the network to just the dispatching function. - - Don't use it for tcp/ip and ssl functions. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - description: - description: - - User defined description for the object. - disable_avi_securitygroups: - description: - - By default, avi creates and manages security groups along with custom sg provided by user. - - Set this to true to disallow avi to create and manage new security groups. - - Avi will only make use of custom security groups provided by user. - - This option is only supported for aws cloud type. - - Field introduced in 17.2.13,18.1.4,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - disable_csum_offloads: - description: - - Stop using tcp/udp and ip checksum offload features of nics. - - Field introduced in 17.1.14, 17.2.5, 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - disable_gro: - description: - - Disable generic receive offload (gro) in dpdk poll-mode driver packet receive path. - - Gro is on by default on nics that do not support lro (large receive offload) or do not gain performance boost from lro. - - Field introduced in 17.2.5, 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - disable_se_memory_check: - description: - - If set, disable the config memory check done in service engine. - - Field introduced in 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - disable_tso: - description: - - Disable tcp segmentation offload (tso) in dpdk poll-mode driver packet transmit path. - - Tso is on by default on nics that support it. - - Field introduced in 17.2.5, 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - disk_per_se: - description: - - Amount of disk space for each of the service engine virtual machines. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - distribute_load_active_standby: - description: - - Use both the active and standby service engines for virtual service placement in the legacy active standby ha mode. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - distribute_queues: - description: - - Distributes queue ownership among cores so multiple cores handle dispatcher duties. - - Field introduced in 17.2.8. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - enable_hsm_priming: - description: - - (this is a beta feature). - - Enable hsm key priming. - - If enabled, key handles on the hsm will be synced to se before processing client connections. - - Field introduced in 17.2.7, 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - enable_multi_lb: - description: - - Applicable only for azure cloud with basic sku lb. - - If set, additional azure lbs will be automatically created if resources in existing lb are exhausted. - - Field introduced in 17.2.10, 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - enable_routing: - description: - - Enable routing for this serviceenginegroup . - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - enable_vip_on_all_interfaces: - description: - - Enable vip on all interfaces of se. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - enable_vmac: - description: - - Use virtual mac address for interfaces on which floating interface ips are placed. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - ephemeral_portrange_end: - description: - - End local ephemeral port number for outbound connections. - - Field introduced in 17.2.13, 18.1.5, 18.2.1. - ephemeral_portrange_start: - description: - - Start local ephemeral port number for outbound connections. - - Field introduced in 17.2.13, 18.1.5, 18.2.1. - extra_config_multiplier: - description: - - Multiplier for extra config to support large vs/pool config. - - Default value when not specified in API or module is interpreted by Avi Controller as 0.0. - extra_shared_config_memory: - description: - - Extra config memory to support large geo db configuration. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - floating_intf_ip: - description: - - If serviceenginegroup is configured for legacy 1+1 active standby ha mode, floating ip's will be advertised only by the active se in the pair. - - Virtual services in this group must be disabled/enabled for any changes to the floating ip's to take effect. - - Only active se hosting vs tagged with active standby se 1 tag will advertise this floating ip when manual load distribution is enabled. - floating_intf_ip_se_2: - description: - - If serviceenginegroup is configured for legacy 1+1 active standby ha mode, floating ip's will be advertised only by the active se in the pair. - - Virtual services in this group must be disabled/enabled for any changes to the floating ip's to take effect. - - Only active se hosting vs tagged with active standby se 2 tag will advertise this floating ip when manual load distribution is enabled. - flow_table_new_syn_max_entries: - description: - - Maximum number of flow table entries that have not completed tcp three-way handshake yet. - - Field introduced in 17.2.5. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - free_list_size: - description: - - Number of entries in the free list. - - Field introduced in 17.2.10, 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 1024. - ha_mode: - description: - - High availability mode for all the virtual services using this service engine group. - - Enum options - HA_MODE_SHARED_PAIR, HA_MODE_SHARED, HA_MODE_LEGACY_ACTIVE_STANDBY. - - Default value when not specified in API or module is interpreted by Avi Controller as HA_MODE_SHARED. - hardwaresecuritymodulegroup_ref: - description: - - It is a reference to an object of type hardwaresecuritymodulegroup. - heap_minimum_config_memory: - description: - - Minimum required heap memory to apply any configuration. - - Allowed values are 0-100. - - Field introduced in 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 8. - hm_on_standby: - description: - - Enable active health monitoring from the standby se for all placed virtual services. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - host_attribute_key: - description: - - Key of a (key, value) pair identifying a label for a set of nodes usually in container clouds. - - Needs to be specified together with host_attribute_value. - - Ses can be configured differently including ha modes across different se groups. - - May also be used for isolation between different classes of virtualservices. - - Virtualservices' se group may be specified via annotations/labels. - - A openshift/kubernetes namespace maybe annotated with a matching se group label as openshift.io/node-selector apptype=prod. - - When multiple se groups are used in a cloud with host attributes specified,just a single se group can exist as a match-all se group without a - - host_attribute_key. - host_attribute_value: - description: - - Value of a (key, value) pair identifying a label for a set of nodes usually in container clouds. - - Needs to be specified together with host_attribute_key. - host_gateway_monitor: - description: - - Enable the host gateway monitor when service engine is deployed as docker container. - - Disabled by default. - - Field introduced in 17.2.4. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - hypervisor: - description: - - Override default hypervisor. - - Enum options - DEFAULT, VMWARE_ESX, KVM, VMWARE_VSAN, XEN. - ignore_rtt_threshold: - description: - - Ignore rtt samples if it is above threshold. - - Field introduced in 17.1.6,17.2.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 5000. - ingress_access_data: - description: - - Program se security group ingress rules to allow vip data access from remote cidr type. - - Enum options - SG_INGRESS_ACCESS_NONE, SG_INGRESS_ACCESS_ALL, SG_INGRESS_ACCESS_VPC. - - Field introduced in 17.1.5. - - Default value when not specified in API or module is interpreted by Avi Controller as SG_INGRESS_ACCESS_ALL. - ingress_access_mgmt: - description: - - Program se security group ingress rules to allow ssh/icmp management access from remote cidr type. - - Enum options - SG_INGRESS_ACCESS_NONE, SG_INGRESS_ACCESS_ALL, SG_INGRESS_ACCESS_VPC. - - Field introduced in 17.1.5. - - Default value when not specified in API or module is interpreted by Avi Controller as SG_INGRESS_ACCESS_ALL. - instance_flavor: - description: - - Instance/flavor name for se instance. - iptables: - description: - - Iptables rules. - least_load_core_selection: - description: - - Select core with least load for new flow. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - license_tier: - description: - - Specifies the license tier which would be used. - - This field by default inherits the value from cloud. - - Enum options - ENTERPRISE_16, ENTERPRISE_18. - - Field introduced in 17.2.5. - license_type: - description: - - If no license type is specified then default license enforcement for the cloud type is chosen. - - Enum options - LIC_BACKEND_SERVERS, LIC_SOCKETS, LIC_CORES, LIC_HOSTS, LIC_SE_BANDWIDTH, LIC_METERED_SE_BANDWIDTH. - - Field introduced in 17.2.5. - log_disksz: - description: - - Maximum disk capacity (in mb) to be allocated to an se. - - This is exclusively used for debug and log data. - - Default value when not specified in API or module is interpreted by Avi Controller as 10000. - max_cpu_usage: - description: - - When cpu usage on an se exceeds this threshold, virtual services hosted on this se may be rebalanced to other ses to reduce load. - - A new se may be created as part of this process. - - Allowed values are 40-90. - - Default value when not specified in API or module is interpreted by Avi Controller as 80. - max_memory_per_mempool: - description: - - Max bytes that can be allocated in a single mempool. - - Field introduced in 18.1.5. - - Default value when not specified in API or module is interpreted by Avi Controller as 64. - max_public_ips_per_lb: - description: - - Applicable to azure platform only. - - Maximum number of public ips per azure lb. - - Field introduced in 17.2.12, 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 30. - max_rules_per_lb: - description: - - Applicable to azure platform only. - - Maximum number of rules per azure lb. - - Field introduced in 17.2.12, 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 150. - max_scaleout_per_vs: - description: - - Maximum number of active service engines for the virtual service. - - Allowed values are 1-64. - - Default value when not specified in API or module is interpreted by Avi Controller as 4. - max_se: - description: - - Maximum number of services engines in this group. - - Allowed values are 0-1000. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - max_vs_per_se: - description: - - Maximum number of virtual services that can be placed on a single service engine. - - East west virtual services are excluded from this limit. - - Allowed values are 1-1000. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - mem_reserve: - description: - - Boolean flag to set mem_reserve. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - memory_for_config_update: - description: - - Indicates the percent of memory reserved for config updates. - - Allowed values are 0-100. - - Field introduced in 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 15. - memory_per_se: - description: - - Amount of memory for each of the service engine virtual machines. - - Default value when not specified in API or module is interpreted by Avi Controller as 2048. - mgmt_network_ref: - description: - - Management network to use for avi service engines. - - It is a reference to an object of type network. - mgmt_subnet: - description: - - Management subnet to use for avi service engines. - min_cpu_usage: - description: - - When cpu usage on an se falls below the minimum threshold, virtual services hosted on the se may be consolidated onto other underutilized ses. - - After consolidation, unused service engines may then be eligible for deletion. - - Allowed values are 20-60. - - Default value when not specified in API or module is interpreted by Avi Controller as 30. - min_scaleout_per_vs: - description: - - Minimum number of active service engines for the virtual service. - - Allowed values are 1-64. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - min_se: - description: - - Minimum number of services engines in this group (relevant for se autorebalance only). - - Allowed values are 0-1000. - - Field introduced in 17.2.13,18.1.3,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - minimum_connection_memory: - description: - - Indicates the percent of memory reserved for connections. - - Allowed values are 0-100. - - Field introduced in 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 20. - minimum_required_config_memory: - description: - - Required available config memory to apply any configuration. - - Allowed values are 0-90. - - Field deprecated in 18.1.2. - - Field introduced in 18.1.1. - n_log_streaming_threads: - description: - - Number of threads to use for log streaming. - - Allowed values are 1-100. - - Field introduced in 17.2.12, 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - name: - description: - - Name of the object. - required: true - non_significant_log_throttle: - description: - - This setting limits the number of non-significant logs generated per second per core on this se. - - Default is 100 logs per second. - - Set it to zero (0) to disable throttling. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 100. - num_dispatcher_cores: - description: - - Number of dispatcher cores (0,1,2,4,8 or 16). - - If set to 0, then number of dispatcher cores is deduced automatically. - - Allowed values are 0,1,2,4,8,16. - - Field introduced in 17.2.12, 18.1.3, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - num_flow_cores_sum_changes_to_ignore: - description: - - Number of changes in num flow cores sum to ignore. - - Default value when not specified in API or module is interpreted by Avi Controller as 8. - openstack_availability_zone: - description: - - Field deprecated in 17.1.1. - openstack_availability_zones: - description: - - Field introduced in 17.1.1. - openstack_mgmt_network_name: - description: - - Avi management network name. - openstack_mgmt_network_uuid: - description: - - Management network uuid. - os_reserved_memory: - description: - - Amount of extra memory to be reserved for use by the operating system on a service engine. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - per_app: - description: - - Per-app se mode is designed for deploying dedicated load balancers per app (vs). - - In this mode, each se is limited to a max of 2 vss. - - Vcpus in per-app ses count towards licensing usage at 25% rate. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - placement_mode: - description: - - If placement mode is 'auto', virtual services are automatically placed on service engines. - - Enum options - PLACEMENT_MODE_AUTO. - - Default value when not specified in API or module is interpreted by Avi Controller as PLACEMENT_MODE_AUTO. - realtime_se_metrics: - description: - - Enable or disable real time se metrics. - reboot_on_stop: - description: - - Reboot the system if the se is stopped. - - Field introduced in 17.2.16,18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - se_bandwidth_type: - description: - - Select the se bandwidth for the bandwidth license. - - Enum options - SE_BANDWIDTH_UNLIMITED, SE_BANDWIDTH_25M, SE_BANDWIDTH_200M, SE_BANDWIDTH_1000M, SE_BANDWIDTH_10000M. - - Field introduced in 17.2.5. - se_deprovision_delay: - description: - - Duration to preserve unused service engine virtual machines before deleting them. - - If traffic to a virtual service were to spike up abruptly, this se would still be available to be utilized again rather than creating a new se. - - If this value is set to 0, controller will never delete any ses and administrator has to manually cleanup unused ses. - - Allowed values are 0-525600. - - Default value when not specified in API or module is interpreted by Avi Controller as 120. - se_dos_profile: - description: - - Dosthresholdprofile settings for serviceenginegroup. - se_dpdk_pmd: - description: - - Determines if dpdk pool mode driver should be used or not 0 automatically determine based on hypervisor/nic type 1 unconditionally use dpdk - - poll mode driver 2 don't use dpdk poll mode driver. - - Allowed values are 0-2. - - Field introduced in 18.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - se_flow_probe_retries: - description: - - Flow probe retry count if no replies are received. - - Allowed values are 0-5. - - Field introduced in 18.1.4, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 2. - se_flow_probe_timer: - description: - - Timeout in milliseconds for flow probe entries. - - Allowed values are 10-200. - - Field introduced in 18.1.4, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 100. - se_ipc_udp_port: - description: - - Udp port for se_dp ipc in docker bridge mode. - - Field introduced in 17.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 1500. - se_name_prefix: - description: - - Prefix to use for virtual machine name of service engines. - - Default value when not specified in API or module is interpreted by Avi Controller as Avi. - se_pcap_lookahead: - description: - - Enables lookahead mode of packet receive in pcap mode. - - Introduced to overcome an issue with hv_netvsc driver. - - Lookahead mode attempts to ensure that application and kernel's view of the receive rings are consistent. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - se_pcap_reinit_frequency: - description: - - Frequency in seconds at which periodically a pcap reinit check is triggered. - - May be used in conjunction with the configuration pcap_reinit_threshold. - - (valid range 15 mins - 12 hours, 0 - disables). - - Allowed values are 900-43200. - - Special values are 0- 'disable'. - - Field introduced in 17.2.13, 18.1.3, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - se_pcap_reinit_threshold: - description: - - Threshold for input packet receive errors in pcap mode exceeding which a pcap reinit is triggered. - - If not set, an unconditional reinit is performed. - - This value is checked every pcap_reinit_frequency interval. - - Field introduced in 17.2.13, 18.1.3, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - se_probe_port: - description: - - Tcp port on se where echo service will be run. - - Field introduced in 17.2.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 7. - se_remote_punt_udp_port: - description: - - Udp port for punted packets in docker bridge mode. - - Field introduced in 17.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 1501. - se_routing: - description: - - Enable routing via service engine datapath. - - When disabled, routing is done by the linux kernel. - - Ip routing needs to be enabled in service engine group for se routing to be effective. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - se_sb_dedicated_core: - description: - - Sideband traffic will be handled by a dedicated core. - - Field introduced in 16.5.2, 17.1.9, 17.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - se_sb_threads: - description: - - Number of sideband threads per se. - - Allowed values are 1-128. - - Field introduced in 16.5.2, 17.1.9, 17.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - se_thread_multiplier: - description: - - Multiplier for se threads based on vcpu. - - Allowed values are 1-10. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - se_tracert_port_range: - description: - - Traceroute port range. - - Field introduced in 17.2.8. - se_tunnel_mode: - description: - - Determines if dsr from secondary se is active or not 0 automatically determine based on hypervisor type. - - 1 disable dsr unconditionally. - - 2 enable dsr unconditionally. - - Allowed values are 0-2. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - se_tunnel_udp_port: - description: - - Udp port for tunneled packets from secondary to primary se in docker bridge mode. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 1550. - se_udp_encap_ipc: - description: - - Determines if se-se ipc messages are encapsulated in a udp header 0 automatically determine based on hypervisor type. - - 1 use udp encap unconditionally. - - Allowed values are 0-1. - - Field introduced in 17.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - se_use_dpdk: - description: - - Determines if dpdk library should be used or not 0 automatically determine based on hypervisor type 1 use dpdk if pcap is not enabled 2 - - don't use dpdk. - - Allowed values are 0-2. - - Field introduced in 18.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - se_vs_hb_max_pkts_in_batch: - description: - - Maximum number of aggregated vs heartbeat packets to send in a batch. - - Allowed values are 1-256. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 64. - se_vs_hb_max_vs_in_pkt: - description: - - Maximum number of virtualservices for which heartbeat messages are aggregated in one packet. - - Allowed values are 1-1024. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 256. - self_se_election: - description: - - Enable ses to elect a primary amongst themselves in the absence of a connectivity to controller. - - Field introduced in 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - service_ip6_subnets: - description: - - Ipv6 subnets assigned to the se group. - - Required for vs group placement. - - Field introduced in 18.1.1. - service_ip_subnets: - description: - - Subnets assigned to the se group. - - Required for vs group placement. - - Field introduced in 17.1.1. - shm_minimum_config_memory: - description: - - Minimum required shared memory to apply any configuration. - - Allowed values are 0-100. - - Field introduced in 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 4. - significant_log_throttle: - description: - - This setting limits the number of significant logs generated per second per core on this se. - - Default is 100 logs per second. - - Set it to zero (0) to disable throttling. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 100. - ssl_preprocess_sni_hostname: - description: - - (beta) preprocess ssl client hello for sni hostname extension.if set to true, this will apply sni child's ssl protocol(s), if they are different - - from sni parent's allowed ssl protocol(s). - - Field introduced in 17.2.12, 18.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - tenant_ref: - description: - - It is a reference to an object of type tenant. - udf_log_throttle: - description: - - This setting limits the number of udf logs generated per second per core on this se. - - Udf logs are generated due to the configured client log filters or the rules with logging enabled. - - Default is 100 logs per second. - - Set it to zero (0) to disable throttling. - - Field introduced in 17.1.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 100. - url: - description: - - Avi controller URL of the object. - use_standard_alb: - description: - - Use standard sku azure load balancer. - - By default cloud level flag is set. - - If not set, it inherits/uses the use_standard_alb flag from the cloud. - - Field introduced in 18.2.3. - type: bool - uuid: - description: - - Unique object identifier of the object. - vcenter_clusters: - description: - - Vcenterclusters settings for serviceenginegroup. - vcenter_datastore_mode: - description: - - Enum options - vcenter_datastore_any, vcenter_datastore_local, vcenter_datastore_shared. - - Default value when not specified in API or module is interpreted by Avi Controller as VCENTER_DATASTORE_ANY. - vcenter_datastores: - description: - - List of vcenterdatastore. - vcenter_datastores_include: - description: - - Boolean flag to set vcenter_datastores_include. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - vcenter_folder: - description: - - Folder to place all the service engine virtual machines in vcenter. - - Default value when not specified in API or module is interpreted by Avi Controller as AviSeFolder. - vcenter_hosts: - description: - - Vcenterhosts settings for serviceenginegroup. - vcpus_per_se: - description: - - Number of vcpus for each of the service engine virtual machines. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. - vip_asg: - description: - - When vip_asg is set, vip configuration will be managed by avi.user will be able to configure vip_asg or vips individually at the time of create. - - Field introduced in 17.2.12, 18.1.2. - vs_host_redundancy: - description: - - Ensure primary and secondary service engines are deployed on different physical hosts. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - vs_scalein_timeout: - description: - - Time to wait for the scaled in se to drain existing flows before marking the scalein done. - - Default value when not specified in API or module is interpreted by Avi Controller as 30. - vs_scalein_timeout_for_upgrade: - description: - - During se upgrade, time to wait for the scaled-in se to drain existing flows before marking the scalein done. - - Default value when not specified in API or module is interpreted by Avi Controller as 30. - vs_scaleout_timeout: - description: - - Time to wait for the scaled out se to become ready before marking the scaleout done. - - Default value when not specified in API or module is interpreted by Avi Controller as 600. - vs_se_scaleout_additional_wait_time: - description: - - Wait time for sending scaleout ready notification after virtual service is marked up. - - In certain deployments, there may be an additional delay to accept traffic. - - For example, for bgp, some time is needed for route advertisement. - - Allowed values are 0-20. - - Field introduced in 18.1.5,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - vs_se_scaleout_ready_timeout: - description: - - Timeout in seconds for service engine to sendscaleout ready notification of a virtual service. - - Allowed values are 0-60. - - Field introduced in 18.1.5,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 25. - vs_switchover_timeout: - description: - - During se upgrade in a legacy active/standby segroup, time to wait for the new primary se to accept flows before marking the switchover done. - - Field introduced in 17.2.13,18.1.4,18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as 300. - vss_placement: - description: - - Parameters to place virtual services on only a subset of the cores of an se. - - Field introduced in 17.2.5. - vss_placement_enabled: - description: - - If set, virtual services will be placed on only a subset of the cores of an se. - - Field introduced in 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - waf_learning_interval: - description: - - Frequency with which se publishes waf learning. - - Allowed values are 1-43200. - - Field deprecated in 18.2.3. - - Field introduced in 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 10. - waf_learning_memory: - description: - - Amount of memory reserved on se for waf learning. - - Cannot exceed 5% of se memory. - - Field deprecated in 18.2.3. - - Field introduced in 18.1.2. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - waf_mempool: - description: - - Enable memory pool for waf. - - Field introduced in 17.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - waf_mempool_size: - description: - - Memory pool size used for waf. - - Field introduced in 17.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as 64. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create ServiceEngineGroup object - avi_serviceenginegroup: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_serviceenginegroup -""" - -RETURN = ''' -obj: - description: ServiceEngineGroup (api/serviceenginegroup) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - accelerated_networking=dict(type='bool',), - active_standby=dict(type='bool',), - additional_config_memory=dict(type='int',), - advertise_backend_networks=dict(type='bool',), - aggressive_failure_detection=dict(type='bool',), - algo=dict(type='str',), - allow_burst=dict(type='bool',), - app_cache_percent=dict(type='int',), - app_learning_memory_percent=dict(type='int',), - archive_shm_limit=dict(type='int',), - async_ssl=dict(type='bool',), - async_ssl_threads=dict(type='int',), - auto_rebalance=dict(type='bool',), - auto_rebalance_capacity_per_se=dict(type='list',), - auto_rebalance_criteria=dict(type='list',), - auto_rebalance_interval=dict(type='int',), - auto_redistribute_active_standby_load=dict(type='bool',), - bgp_state_update_interval=dict(type='int',), - buffer_se=dict(type='int',), - cloud_ref=dict(type='str',), - config_debugs_on_all_cores=dict(type='bool',), - connection_memory_percentage=dict(type='int',), - cpu_reserve=dict(type='bool',), - cpu_socket_affinity=dict(type='bool',), - custom_securitygroups_data=dict(type='list',), - custom_securitygroups_mgmt=dict(type='list',), - custom_tag=dict(type='list',), - data_network_id=dict(type='str',), - datascript_timeout=dict(type='int',), - dedicated_dispatcher_core=dict(type='bool',), - description=dict(type='str',), - disable_avi_securitygroups=dict(type='bool',), - disable_csum_offloads=dict(type='bool',), - disable_gro=dict(type='bool',), - disable_se_memory_check=dict(type='bool',), - disable_tso=dict(type='bool',), - disk_per_se=dict(type='int',), - distribute_load_active_standby=dict(type='bool',), - distribute_queues=dict(type='bool',), - enable_hsm_priming=dict(type='bool',), - enable_multi_lb=dict(type='bool',), - enable_routing=dict(type='bool',), - enable_vip_on_all_interfaces=dict(type='bool',), - enable_vmac=dict(type='bool',), - ephemeral_portrange_end=dict(type='int',), - ephemeral_portrange_start=dict(type='int',), - extra_config_multiplier=dict(type='float',), - extra_shared_config_memory=dict(type='int',), - floating_intf_ip=dict(type='list',), - floating_intf_ip_se_2=dict(type='list',), - flow_table_new_syn_max_entries=dict(type='int',), - free_list_size=dict(type='int',), - ha_mode=dict(type='str',), - hardwaresecuritymodulegroup_ref=dict(type='str',), - heap_minimum_config_memory=dict(type='int',), - hm_on_standby=dict(type='bool',), - host_attribute_key=dict(type='str',), - host_attribute_value=dict(type='str',), - host_gateway_monitor=dict(type='bool',), - hypervisor=dict(type='str',), - ignore_rtt_threshold=dict(type='int',), - ingress_access_data=dict(type='str',), - ingress_access_mgmt=dict(type='str',), - instance_flavor=dict(type='str',), - iptables=dict(type='list',), - least_load_core_selection=dict(type='bool',), - license_tier=dict(type='str',), - license_type=dict(type='str',), - log_disksz=dict(type='int',), - max_cpu_usage=dict(type='int',), - max_memory_per_mempool=dict(type='int',), - max_public_ips_per_lb=dict(type='int',), - max_rules_per_lb=dict(type='int',), - max_scaleout_per_vs=dict(type='int',), - max_se=dict(type='int',), - max_vs_per_se=dict(type='int',), - mem_reserve=dict(type='bool',), - memory_for_config_update=dict(type='int',), - memory_per_se=dict(type='int',), - mgmt_network_ref=dict(type='str',), - mgmt_subnet=dict(type='dict',), - min_cpu_usage=dict(type='int',), - min_scaleout_per_vs=dict(type='int',), - min_se=dict(type='int',), - minimum_connection_memory=dict(type='int',), - minimum_required_config_memory=dict(type='int',), - n_log_streaming_threads=dict(type='int',), - name=dict(type='str', required=True), - non_significant_log_throttle=dict(type='int',), - num_dispatcher_cores=dict(type='int',), - num_flow_cores_sum_changes_to_ignore=dict(type='int',), - openstack_availability_zone=dict(type='str',), - openstack_availability_zones=dict(type='list',), - openstack_mgmt_network_name=dict(type='str',), - openstack_mgmt_network_uuid=dict(type='str',), - os_reserved_memory=dict(type='int',), - per_app=dict(type='bool',), - placement_mode=dict(type='str',), - realtime_se_metrics=dict(type='dict',), - reboot_on_stop=dict(type='bool',), - se_bandwidth_type=dict(type='str',), - se_deprovision_delay=dict(type='int',), - se_dos_profile=dict(type='dict',), - se_dpdk_pmd=dict(type='int',), - se_flow_probe_retries=dict(type='int',), - se_flow_probe_timer=dict(type='int',), - se_ipc_udp_port=dict(type='int',), - se_name_prefix=dict(type='str',), - se_pcap_lookahead=dict(type='bool',), - se_pcap_reinit_frequency=dict(type='int',), - se_pcap_reinit_threshold=dict(type='int',), - se_probe_port=dict(type='int',), - se_remote_punt_udp_port=dict(type='int',), - se_routing=dict(type='bool',), - se_sb_dedicated_core=dict(type='bool',), - se_sb_threads=dict(type='int',), - se_thread_multiplier=dict(type='int',), - se_tracert_port_range=dict(type='dict',), - se_tunnel_mode=dict(type='int',), - se_tunnel_udp_port=dict(type='int',), - se_udp_encap_ipc=dict(type='int',), - se_use_dpdk=dict(type='int',), - se_vs_hb_max_pkts_in_batch=dict(type='int',), - se_vs_hb_max_vs_in_pkt=dict(type='int',), - self_se_election=dict(type='bool',), - service_ip6_subnets=dict(type='list',), - service_ip_subnets=dict(type='list',), - shm_minimum_config_memory=dict(type='int',), - significant_log_throttle=dict(type='int',), - ssl_preprocess_sni_hostname=dict(type='bool',), - tenant_ref=dict(type='str',), - udf_log_throttle=dict(type='int',), - url=dict(type='str',), - use_standard_alb=dict(type='bool',), - uuid=dict(type='str',), - vcenter_clusters=dict(type='dict',), - vcenter_datastore_mode=dict(type='str',), - vcenter_datastores=dict(type='list',), - vcenter_datastores_include=dict(type='bool',), - vcenter_folder=dict(type='str',), - vcenter_hosts=dict(type='dict',), - vcpus_per_se=dict(type='int',), - vip_asg=dict(type='dict',), - vs_host_redundancy=dict(type='bool',), - vs_scalein_timeout=dict(type='int',), - vs_scalein_timeout_for_upgrade=dict(type='int',), - vs_scaleout_timeout=dict(type='int',), - vs_se_scaleout_additional_wait_time=dict(type='int',), - vs_se_scaleout_ready_timeout=dict(type='int',), - vs_switchover_timeout=dict(type='int',), - vss_placement=dict(type='dict',), - vss_placement_enabled=dict(type='bool',), - waf_learning_interval=dict(type='int',), - waf_learning_memory=dict(type='int',), - waf_mempool=dict(type='bool',), - waf_mempool_size=dict(type='int',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'serviceenginegroup', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_snmptrapprofile.py b/plugins/modules/network/avi/avi_snmptrapprofile.py deleted file mode 100644 index eb10fe16a1..0000000000 --- a/plugins/modules/network/avi/avi_snmptrapprofile.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_snmptrapprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of SnmpTrapProfile Avi RESTful Object -description: - - This module is used to configure SnmpTrapProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - name: - description: - - A user-friendly name of the snmp trap configuration. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - trap_servers: - description: - - The ip address or hostname of the snmp trap destination server. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the snmp trap profile object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create SnmpTrapProfile object - avi_snmptrapprofile: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_snmptrapprofile -""" - -RETURN = ''' -obj: - description: SnmpTrapProfile (api/snmptrapprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - trap_servers=dict(type='list',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'snmptrapprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_sslkeyandcertificate.py b/plugins/modules/network/avi/avi_sslkeyandcertificate.py deleted file mode 100644 index 6a2dd12c22..0000000000 --- a/plugins/modules/network/avi/avi_sslkeyandcertificate.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_sslkeyandcertificate -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of SSLKeyAndCertificate Avi RESTful Object -description: - - This module is used to configure SSLKeyAndCertificate object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - ca_certs: - description: - - Ca certificates in certificate chain. - certificate: - description: - - Sslcertificate settings for sslkeyandcertificate. - required: true - certificate_base64: - description: - - States if the certificate is base64 encoded. - - Field introduced in 18.1.2, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - certificate_management_profile_ref: - description: - - It is a reference to an object of type certificatemanagementprofile. - created_by: - description: - - Creator name. - dynamic_params: - description: - - Dynamic parameters needed for certificate management profile. - enckey_base64: - description: - - Encrypted private key corresponding to the private key (e.g. - - Those generated by an hsm such as thales nshield). - enckey_name: - description: - - Name of the encrypted private key (e.g. - - Those generated by an hsm such as thales nshield). - format: - description: - - Format of the key/certificate file. - - Enum options - SSL_PEM, SSL_PKCS12. - - Field introduced in 18.1.2, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as SSL_PEM. - hardwaresecuritymodulegroup_ref: - description: - - It is a reference to an object of type hardwaresecuritymodulegroup. - key: - description: - - Private key. - key_base64: - description: - - States if the private key is base64 encoded. - - Field introduced in 18.1.2, 18.2.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - key_params: - description: - - Sslkeyparams settings for sslkeyandcertificate. - key_passphrase: - description: - - Passphrase used to encrypt the private key. - - Field introduced in 18.1.2, 18.2.1. - name: - description: - - Name of the object. - required: true - status: - description: - - Enum options - ssl_certificate_finished, ssl_certificate_pending. - - Default value when not specified in API or module is interpreted by Avi Controller as SSL_CERTIFICATE_FINISHED. - tenant_ref: - description: - - It is a reference to an object of type tenant. - type: - description: - - Enum options - ssl_certificate_type_virtualservice, ssl_certificate_type_system, ssl_certificate_type_ca. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Create a SSL Key and Certificate - avi_sslkeyandcertificate: - controller: 10.10.27.90 - username: admin - password: AviNetworks123! - key: | - -----BEGIN PRIVATE KEY----- - .... - -----END PRIVATE KEY----- - certificate: - self_signed: true - certificate: | - -----BEGIN CERTIFICATE----- - .... - -----END CERTIFICATE----- - type: SSL_CERTIFICATE_TYPE_VIRTUALSERVICE - name: MyTestCert -""" - -RETURN = ''' -obj: - description: SSLKeyAndCertificate (api/sslkeyandcertificate) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - ca_certs=dict(type='list',), - certificate=dict(type='dict', required=True), - certificate_base64=dict(type='bool',), - certificate_management_profile_ref=dict(type='str',), - created_by=dict(type='str',), - dynamic_params=dict(type='list',), - enckey_base64=dict(type='str',), - enckey_name=dict(type='str',), - format=dict(type='str',), - hardwaresecuritymodulegroup_ref=dict(type='str',), - key=dict(type='str', no_log=True,), - key_base64=dict(type='bool',), - key_params=dict(type='dict',), - key_passphrase=dict(type='str', no_log=True,), - name=dict(type='str', required=True), - status=dict(type='str',), - tenant_ref=dict(type='str',), - type=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'sslkeyandcertificate', - set(['key_passphrase', 'key'])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_sslprofile.py b/plugins/modules/network/avi/avi_sslprofile.py deleted file mode 100644 index 574d2fa855..0000000000 --- a/plugins/modules/network/avi/avi_sslprofile.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_sslprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of SSLProfile Avi RESTful Object -description: - - This module is used to configure SSLProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - accepted_ciphers: - description: - - Ciphers suites represented as defined by U(http://www.openssl.org/docs/apps/ciphers.html). - - Default value when not specified in API or module is interpreted by Avi Controller as AES:3DES:RC4. - accepted_versions: - description: - - Set of versions accepted by the server. - cipher_enums: - description: - - Enum options - tls_ecdhe_ecdsa_with_aes_128_gcm_sha256, tls_ecdhe_ecdsa_with_aes_256_gcm_sha384, tls_ecdhe_rsa_with_aes_128_gcm_sha256, - - tls_ecdhe_rsa_with_aes_256_gcm_sha384, tls_ecdhe_ecdsa_with_aes_128_cbc_sha256, tls_ecdhe_ecdsa_with_aes_256_cbc_sha384, - - tls_ecdhe_rsa_with_aes_128_cbc_sha256, tls_ecdhe_rsa_with_aes_256_cbc_sha384, tls_rsa_with_aes_128_gcm_sha256, tls_rsa_with_aes_256_gcm_sha384, - - tls_rsa_with_aes_128_cbc_sha256, tls_rsa_with_aes_256_cbc_sha256, tls_ecdhe_ecdsa_with_aes_128_cbc_sha, tls_ecdhe_ecdsa_with_aes_256_cbc_sha, - - tls_ecdhe_rsa_with_aes_128_cbc_sha, tls_ecdhe_rsa_with_aes_256_cbc_sha, tls_rsa_with_aes_128_cbc_sha, tls_rsa_with_aes_256_cbc_sha, - - tls_rsa_with_3des_ede_cbc_sha, tls_rsa_with_rc4_128_sha. - description: - description: - - User defined description for the object. - dhparam: - description: - - Dh parameters used in ssl. - - At this time, it is not configurable and is set to 2048 bits. - enable_ssl_session_reuse: - description: - - Enable ssl session re-use. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - name: - description: - - Name of the object. - required: true - prefer_client_cipher_ordering: - description: - - Prefer the ssl cipher ordering presented by the client during the ssl handshake over the one specified in the ssl profile. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - send_close_notify: - description: - - Send 'close notify' alert message for a clean shutdown of the ssl connection. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - ssl_rating: - description: - - Sslrating settings for sslprofile. - ssl_session_timeout: - description: - - The amount of time in seconds before an ssl session expires. - - Default value when not specified in API or module is interpreted by Avi Controller as 86400. - tags: - description: - - List of tag. - tenant_ref: - description: - - It is a reference to an object of type tenant. - type: - description: - - Ssl profile type. - - Enum options - SSL_PROFILE_TYPE_APPLICATION, SSL_PROFILE_TYPE_SYSTEM. - - Field introduced in 17.2.8. - - Default value when not specified in API or module is interpreted by Avi Controller as SSL_PROFILE_TYPE_APPLICATION. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create SSL profile with list of allowed ciphers - avi_sslprofile: - controller: '{{ controller }}' - username: '{{ username }}' - password: '{{ password }}' - accepted_ciphers: > - ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA: - ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384: - AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA: - AES256-SHA:DES-CBC3-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA384: - ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA - accepted_versions: - - type: SSL_VERSION_TLS1 - - type: SSL_VERSION_TLS1_1 - - type: SSL_VERSION_TLS1_2 - cipher_enums: - - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - - TLS_RSA_WITH_AES_128_GCM_SHA256 - - TLS_RSA_WITH_AES_256_GCM_SHA384 - - TLS_RSA_WITH_AES_128_CBC_SHA256 - - TLS_RSA_WITH_AES_256_CBC_SHA256 - - TLS_RSA_WITH_AES_128_CBC_SHA - - TLS_RSA_WITH_AES_256_CBC_SHA - - TLS_RSA_WITH_3DES_EDE_CBC_SHA - - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - name: PFS-BOTH-RSA-EC - send_close_notify: true - ssl_rating: - compatibility_rating: SSL_SCORE_EXCELLENT - performance_rating: SSL_SCORE_EXCELLENT - security_score: '100.0' - tenant_ref: Demo -""" - -RETURN = ''' -obj: - description: SSLProfile (api/sslprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - accepted_ciphers=dict(type='str',), - accepted_versions=dict(type='list',), - cipher_enums=dict(type='list',), - description=dict(type='str',), - dhparam=dict(type='str',), - enable_ssl_session_reuse=dict(type='bool',), - name=dict(type='str', required=True), - prefer_client_cipher_ordering=dict(type='bool',), - send_close_notify=dict(type='bool',), - ssl_rating=dict(type='dict',), - ssl_session_timeout=dict(type='int',), - tags=dict(type='list',), - tenant_ref=dict(type='str',), - type=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'sslprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_stringgroup.py b/plugins/modules/network/avi/avi_stringgroup.py deleted file mode 100644 index bf494cf841..0000000000 --- a/plugins/modules/network/avi/avi_stringgroup.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_stringgroup -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of StringGroup Avi RESTful Object -description: - - This module is used to configure StringGroup object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - description: - description: - - User defined description for the object. - kv: - description: - - Configure key value in the string group. - name: - description: - - Name of the string group. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - type: - description: - - Type of stringgroup. - - Enum options - SG_TYPE_STRING, SG_TYPE_KEYVAL. - - Default value when not specified in API or module is interpreted by Avi Controller as SG_TYPE_STRING. - required: true - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the string group. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create a string group configuration - avi_stringgroup: - controller: '{{ controller }}' - password: '{{ password }}' - username: '{{ username }}' - kv: - - key: text/html - - key: text/xml - - key: text/plain - - key: text/css - - key: text/javascript - - key: application/javascript - - key: application/x-javascript - - key: application/xml - - key: application/pdf - name: System-Compressible-Content-Types - tenant_ref: admin - type: SG_TYPE_STRING -""" - -RETURN = ''' -obj: - description: StringGroup (api/stringgroup) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - description=dict(type='str',), - kv=dict(type='list',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - type=dict(type='str', required=True), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'stringgroup', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_systemconfiguration.py b/plugins/modules/network/avi/avi_systemconfiguration.py deleted file mode 100644 index b338720fec..0000000000 --- a/plugins/modules/network/avi/avi_systemconfiguration.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_systemconfiguration -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of SystemConfiguration Avi RESTful Object -description: - - This module is used to configure SystemConfiguration object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - admin_auth_configuration: - description: - - Adminauthconfiguration settings for systemconfiguration. - default_license_tier: - description: - - Specifies the default license tier which would be used by new clouds. - - Enum options - ENTERPRISE_16, ENTERPRISE_18. - - Field introduced in 17.2.5. - - Default value when not specified in API or module is interpreted by Avi Controller as ENTERPRISE_18. - dns_configuration: - description: - - Dnsconfiguration settings for systemconfiguration. - dns_virtualservice_refs: - description: - - Dns virtualservices hosting fqdn records for applications across avi vantage. - - If no virtualservices are provided, avi vantage will provide dns services for configured applications. - - Switching back to avi vantage from dns virtualservices is not allowed. - - It is a reference to an object of type virtualservice. - docker_mode: - description: - - Boolean flag to set docker_mode. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - email_configuration: - description: - - Emailconfiguration settings for systemconfiguration. - global_tenant_config: - description: - - Tenantconfiguration settings for systemconfiguration. - linux_configuration: - description: - - Linuxconfiguration settings for systemconfiguration. - mgmt_ip_access_control: - description: - - Configure ip access control for controller to restrict open access. - ntp_configuration: - description: - - Ntpconfiguration settings for systemconfiguration. - portal_configuration: - description: - - Portalconfiguration settings for systemconfiguration. - proxy_configuration: - description: - - Proxyconfiguration settings for systemconfiguration. - secure_channel_configuration: - description: - - Configure secure channel properties. - - Field introduced in 18.1.4, 18.2.1. - snmp_configuration: - description: - - Snmpconfiguration settings for systemconfiguration. - ssh_ciphers: - description: - - Allowed ciphers list for ssh to the management interface on the controller and service engines. - - If this is not specified, all the default ciphers are allowed. - ssh_hmacs: - description: - - Allowed hmac list for ssh to the management interface on the controller and service engines. - - If this is not specified, all the default hmacs are allowed. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. - welcome_workflow_complete: - description: - - This flag is set once the initial controller setup workflow is complete. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create SystemConfiguration object - avi_systemconfiguration: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_systemconfiguration -""" - -RETURN = ''' -obj: - description: SystemConfiguration (api/systemconfiguration) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - admin_auth_configuration=dict(type='dict',), - default_license_tier=dict(type='str',), - dns_configuration=dict(type='dict',), - dns_virtualservice_refs=dict(type='list',), - docker_mode=dict(type='bool',), - email_configuration=dict(type='dict',), - global_tenant_config=dict(type='dict',), - linux_configuration=dict(type='dict',), - mgmt_ip_access_control=dict(type='dict',), - ntp_configuration=dict(type='dict',), - portal_configuration=dict(type='dict',), - proxy_configuration=dict(type='dict',), - secure_channel_configuration=dict(type='dict',), - snmp_configuration=dict(type='dict',), - ssh_ciphers=dict(type='list',), - ssh_hmacs=dict(type='list',), - url=dict(type='str',), - uuid=dict(type='str',), - welcome_workflow_complete=dict(type='bool',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'systemconfiguration', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_tenant.py b/plugins/modules/network/avi/avi_tenant.py deleted file mode 100644 index 3828f09e35..0000000000 --- a/plugins/modules/network/avi/avi_tenant.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_tenant -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Tenant Avi RESTful Object -description: - - This module is used to configure Tenant object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - config_settings: - description: - - Tenantconfiguration settings for tenant. - created_by: - description: - - Creator of this tenant. - description: - description: - - User defined description for the object. - local: - description: - - Boolean flag to set local. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - name: - description: - - Name of the object. - required: true - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ - - name: Create Tenant using Service Engines in provider mode - avi_tenant: - controller: '{{ controller }}' - password: '{{ password }}' - username: '{{ username }}' - config_settings: - se_in_provider_context: false - tenant_access_to_provider_se: true - tenant_vrf: false - description: VCenter, Open Stack, AWS Virtual services - local: true - name: Demo -""" - -RETURN = ''' -obj: - description: Tenant (api/tenant) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - config_settings=dict(type='dict',), - created_by=dict(type='str',), - description=dict(type='str',), - local=dict(type='bool',), - name=dict(type='str', required=True), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'tenant', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_trafficcloneprofile.py b/plugins/modules/network/avi/avi_trafficcloneprofile.py deleted file mode 100644 index 10b24c24fc..0000000000 --- a/plugins/modules/network/avi/avi_trafficcloneprofile.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_trafficcloneprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of TrafficCloneProfile Avi RESTful Object -description: - - This module is used to configure TrafficCloneProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - clone_servers: - description: - - Field introduced in 17.1.1. - cloud_ref: - description: - - It is a reference to an object of type cloud. - - Field introduced in 17.1.1. - name: - description: - - Name for the traffic clone profile. - - Field introduced in 17.1.1. - required: true - preserve_client_ip: - description: - - Specifies if client ip needs to be preserved to clone destination. - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.1.1. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the traffic clone profile. - - Field introduced in 17.1.1. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create TrafficCloneProfile object - avi_trafficcloneprofile: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_trafficcloneprofile -""" - -RETURN = ''' -obj: - description: TrafficCloneProfile (api/trafficcloneprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - clone_servers=dict(type='list',), - cloud_ref=dict(type='str',), - name=dict(type='str', required=True), - preserve_client_ip=dict(type='bool',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'trafficcloneprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_user.py b/plugins/modules/network/avi/avi_user.py deleted file mode 100644 index f87bb5345f..0000000000 --- a/plugins/modules/network/avi/avi_user.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/python -""" -# Created on Aug 2, 2018 -# -# @author: Shrikant Chaudhari (shrikant.chaudhari@avinetworks.com) GitHub ID: gitshrikant -# -# module_check: supported -# -# 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 . -# -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_user -author: Shrikant Chaudhari (@gitshrikant) -short_description: Avi User Module -description: - - This module can be used for creation, updation and deletion of a user. -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - type: str - name: - description: - - Full name of the user. - required: true - type: str - obj_username: - description: - - Name that the user will supply when signing into Avi Vantage, such as jdoe or jdoe@avinetworks.com. - required: true - type: str - obj_password: - description: - - You may either enter a case-sensitive password in this field for the new or existing user. - required: true - type: str - email: - description: - - Email address of the user. This field is used when a user loses their password and requests to have it reset. See Password Recovery. - type: str - access: - description: - - Access settings (write, read, or no access) for each type of resource within Vantage. - type: list - is_superuser: - description: - - If the user will need to have the same privileges as the admin account, set it to true. - type: bool - is_active: - description: - - Activates the current user account. - type: bool - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["post", "put", "patch"] - type: str - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - type: str - user_profile_ref: - description: - - Refer user profile. - - This can also be full URI same as it comes in response payload - type: str - default_tenant_ref: - description: - - Default tenant reference. - - This can also be full URI same as it comes in response payload - default: /api/tenant?name=admin - type: str - - -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = ''' - - name: user creation - avi_user: - controller: "" - username: "" - password: "" - api_version: "" - name: "testuser" - obj_username: "testuser" - obj_password: "test123" - email: "test@abc.test" - access: - - role_ref: "/api/role?name=Tenant-Admin" - tenant_ref: "/api/tenant/admin#admin" - user_profile_ref: "/api/useraccountprofile?name=Default-User-Account-Profile" - is_active: true - is_superuser: true - default_tenant_ref: "/api/tenant?name=admin" - - - name: user creation - avi_user: - controller: "" - username: "" - password: "" - api_version: "" - name: "testuser" - obj_username: "testuser2" - obj_password: "password" - email: "testuser2@abc.test" - access: - - role_ref: "https://192.0.2.10/api/role?name=Tenant-Admin" - tenant_ref: "https://192.0.2.10/api/tenant/admin#admin" - user_profile_ref: "https://192.0.2.10/api/useraccountprofile?name=Default-User-Account-Profile" - is_active: true - is_superuser: true - default_tenant_ref: "https://192.0.2.10/api/tenant?name=admin" -''' - -RETURN = ''' -obj: - description: Avi REST resource - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule - -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, ansible_return, HAS_AVI) - from ansible_collections.community.general.plugins.module_utils.network.avi.ansible_utils import ( - avi_ansible_api) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - name=dict(type='str', required=True), - obj_username=dict(type='str', required=True), - obj_password=dict(type='str', required=True, no_log=True), - access=dict(type='list',), - email=dict(type='str',), - is_superuser=dict(type='bool',), - is_active=dict(type='bool',), - avi_api_update_method=dict(default='put', - choices=['post', 'put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - user_profile_ref=dict(type='str',), - default_tenant_ref=dict(type='str', default='/api/tenant?name=admin'), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule(argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'user', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_useraccount.py b/plugins/modules/network/avi/avi_useraccount.py deleted file mode 100644 index 98cfc68c38..0000000000 --- a/plugins/modules/network/avi/avi_useraccount.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/python -""" -# Created on Aug 12, 2016 -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) GitHub ID: grastogi23 -# -# module_check: not supported -# -# 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 . -# -""" - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_useraccount -author: Chaitanya Deshpande (@chaitanyaavi) -short_description: Avi UserAccount Module -description: - - This module can be used for updating the password of a user. - - This module is useful for setting up admin password for Controller bootstrap. -requirements: [ avisdk ] -options: - old_password: - description: - - Old password for update password or default password for bootstrap. - force_change: - description: - - If specifically set to true then old password is tried first for controller and then the new password is - tried. If not specified this flag then the new password is tried first. - -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = ''' - - name: Update user password - avi_useraccount: - controller: "" - username: "" - password: new_password - old_password: "" - api_version: "" - force_change: false - - - name: Update user password using avi_credentials - avi_useraccount: - avi_credentials: "" - old_password: "" - force_change: false -''' - -RETURN = ''' -obj: - description: Avi REST resource - returned: success, changed - type: dict -''' - -import json -import time -from ansible.module_utils.basic import AnsibleModule -from copy import deepcopy - -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, ansible_return, avi_obj_cmp, - cleanup_absent_fields, HAS_AVI) - from ansible_collections.community.general.plugins.module_utils.network.avi.avi_api import ( - ApiSession, AviCredentials) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - old_password=dict(type='str', required=True, no_log=True), - # Flag to specify priority of old/new password while establishing session with controller. - # To handle both Saas and conventional (Entire state in playbook) scenario. - force_change=dict(type='bool', default=False) - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule(argument_spec=argument_specs) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - api_creds = AviCredentials() - api_creds.update_from_ansible_module(module) - old_password = module.params.get('old_password') - force_change = module.params.get('force_change', False) - data = { - 'old_password': old_password, - 'password': api_creds.password - } - # First try old password if 'force_change' is set to true - if force_change: - first_pwd = old_password - second_pwd = api_creds.password - # First try new password if 'force_change' is set to false or not specified in playbook. - else: - first_pwd = api_creds.password - second_pwd = old_password - password_changed = False - try: - api = ApiSession.get_session( - api_creds.controller, api_creds.username, - password=first_pwd, timeout=api_creds.timeout, - tenant=api_creds.tenant, tenant_uuid=api_creds.tenant_uuid, - token=api_creds.token, port=api_creds.port) - if force_change: - rsp = api.put('useraccount', data=data) - if rsp: - password_changed = True - except Exception: - pass - if not password_changed: - api = ApiSession.get_session( - api_creds.controller, api_creds.username, password=second_pwd, - timeout=api_creds.timeout, tenant=api_creds.tenant, - tenant_uuid=api_creds.tenant_uuid, token=api_creds.token, - port=api_creds.port) - if not force_change: - rsp = api.put('useraccount', data=data) - if rsp: - password_changed = True - if password_changed: - return ansible_return(module, rsp, True, req=data) - else: - return ansible_return(module, rsp, False, req=data) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_useraccountprofile.py b/plugins/modules/network/avi/avi_useraccountprofile.py deleted file mode 100644 index 6786777945..0000000000 --- a/plugins/modules/network/avi/avi_useraccountprofile.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_useraccountprofile -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of UserAccountProfile Avi RESTful Object -description: - - This module is used to configure UserAccountProfile object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - account_lock_timeout: - description: - - Lock timeout period (in minutes). - - Default is 30 minutes. - - Default value when not specified in API or module is interpreted by Avi Controller as 30. - credentials_timeout_threshold: - description: - - The time period after which credentials expire. - - Default is 180 days. - - Default value when not specified in API or module is interpreted by Avi Controller as 180. - max_concurrent_sessions: - description: - - Maximum number of concurrent sessions allowed. - - There are unlimited sessions by default. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - max_login_failure_count: - description: - - Number of login attempts before lockout. - - Default is 3 attempts. - - Default value when not specified in API or module is interpreted by Avi Controller as 3. - max_password_history_count: - description: - - Maximum number of passwords to be maintained in the password history. - - Default is 4 passwords. - - Default value when not specified in API or module is interpreted by Avi Controller as 4. - name: - description: - - Name of the object. - required: true - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create UserAccountProfile object - avi_useraccountprofile: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_useraccountprofile -""" - -RETURN = ''' -obj: - description: UserAccountProfile (api/useraccountprofile) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - account_lock_timeout=dict(type='int',), - credentials_timeout_threshold=dict(type='int',), - max_concurrent_sessions=dict(type='int',), - max_login_failure_count=dict(type='int',), - max_password_history_count=dict(type='int',), - name=dict(type='str', required=True), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'useraccountprofile', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_virtualservice.py b/plugins/modules/network/avi/avi_virtualservice.py deleted file mode 100644 index 026855920c..0000000000 --- a/plugins/modules/network/avi/avi_virtualservice.py +++ /dev/null @@ -1,653 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_virtualservice -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of VirtualService Avi RESTful Object -description: - - This module is used to configure VirtualService object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - active_standby_se_tag: - description: - - This configuration only applies if the virtualservice is in legacy active standby ha mode and load distribution among active standby is enabled. - - This field is used to tag the virtualservice so that virtualservices with the same tag will share the same active serviceengine. - - Virtualservices with different tags will have different active serviceengines. - - If one of the serviceengine's in the serviceenginegroup fails, all virtualservices will end up using the same active serviceengine. - - Redistribution of the virtualservices can be either manual or automated when the failed serviceengine recovers. - - Redistribution is based on the auto redistribute property of the serviceenginegroup. - - Enum options - ACTIVE_STANDBY_SE_1, ACTIVE_STANDBY_SE_2. - - Default value when not specified in API or module is interpreted by Avi Controller as ACTIVE_STANDBY_SE_1. - allow_invalid_client_cert: - description: - - Process request even if invalid client certificate is presented. - - Datascript apis need to be used for processing of such requests. - - Field introduced in 18.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - analytics_policy: - description: - - Determines analytics settings for the application. - analytics_profile_ref: - description: - - Specifies settings related to analytics. - - It is a reference to an object of type analyticsprofile. - apic_contract_graph: - description: - - The name of the contract/graph associated with the virtual service. - - Should be in the format. - - This is applicable only for service integration mode with cisco apic controller. - - Field introduced in 17.2.12,18.1.2. - application_profile_ref: - description: - - Enable application layer specific features for the virtual service. - - It is a reference to an object of type applicationprofile. - auto_allocate_floating_ip: - description: - - Auto-allocate floating/elastic ip from the cloud infrastructure. - - Field deprecated in 17.1.1. - type: bool - auto_allocate_ip: - description: - - Auto-allocate vip from the provided subnet. - - Field deprecated in 17.1.1. - type: bool - availability_zone: - description: - - Availability-zone to place the virtual service. - - Field deprecated in 17.1.1. - avi_allocated_fip: - description: - - (internal-use) fip allocated by avi in the cloud infrastructure. - - Field deprecated in 17.1.1. - type: bool - avi_allocated_vip: - description: - - (internal-use) vip allocated by avi in the cloud infrastructure. - - Field deprecated in 17.1.1. - type: bool - azure_availability_set: - description: - - (internal-use)applicable for azure only. - - Azure availability set to which this vs is associated. - - Internally set by the cloud connector. - - Field introduced in 17.2.12, 18.1.2. - bulk_sync_kvcache: - description: - - (this is a beta feature). - - Sync key-value cache to the new ses when vs is scaled out. - - For ex ssl sessions are stored using vs's key-value cache. - - When the vs is scaled out, the ssl session information is synced to the new se, allowing existing ssl sessions to be reused on the new se. - - Field introduced in 17.2.7, 18.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - client_auth: - description: - - Http authentication configuration for protected resources. - close_client_conn_on_config_update: - description: - - Close client connection on vs config update. - - Field introduced in 17.2.4. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - cloud_config_cksum: - description: - - Checksum of cloud configuration for vs. - - Internally set by cloud connector. - cloud_ref: - description: - - It is a reference to an object of type cloud. - cloud_type: - description: - - Enum options - cloud_none, cloud_vcenter, cloud_openstack, cloud_aws, cloud_vca, cloud_apic, cloud_mesos, cloud_linuxserver, cloud_docker_ucp, - - cloud_rancher, cloud_oshift_k8s, cloud_azure, cloud_gcp. - - Default value when not specified in API or module is interpreted by Avi Controller as CLOUD_NONE. - connections_rate_limit: - description: - - Rate limit the incoming connections to this virtual service. - content_rewrite: - description: - - Profile used to match and rewrite strings in request and/or response body. - created_by: - description: - - Creator name. - delay_fairness: - description: - - Select the algorithm for qos fairness. - - This determines how multiple virtual services sharing the same service engines will prioritize traffic over a congested network. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - description: - description: - - User defined description for the object. - discovered_network_ref: - description: - - (internal-use) discovered networks providing reachability for client facing virtual service ip. - - This field is deprecated. - - It is a reference to an object of type network. - - Field deprecated in 17.1.1. - discovered_networks: - description: - - (internal-use) discovered networks providing reachability for client facing virtual service ip. - - This field is used internally by avi, not editable by the user. - - Field deprecated in 17.1.1. - discovered_subnet: - description: - - (internal-use) discovered subnets providing reachability for client facing virtual service ip. - - This field is deprecated. - - Field deprecated in 17.1.1. - dns_info: - description: - - Service discovery specific data including fully qualified domain name, type and time-to-live of the dns record. - - Note that only one of fqdn and dns_info setting is allowed. - dns_policies: - description: - - Dns policies applied on the dns traffic of the virtual service. - - Field introduced in 17.1.1. - east_west_placement: - description: - - Force placement on all se's in service group (mesos mode only). - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - enable_autogw: - description: - - Response traffic to clients will be sent back to the source mac address of the connection, rather than statically sent to a default gateway. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - enable_rhi: - description: - - Enable route health injection using the bgp config in the vrf context. - type: bool - enable_rhi_snat: - description: - - Enable route health injection for source nat'ted floating ip address using the bgp config in the vrf context. - type: bool - enabled: - description: - - Enable or disable the virtual service. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - error_page_profile_ref: - description: - - Error page profile to be used for this virtualservice.this profile is used to send the custom error page to the client generated by the proxy. - - It is a reference to an object of type errorpageprofile. - - Field introduced in 17.2.4. - floating_ip: - description: - - Floating ip to associate with this virtual service. - - Field deprecated in 17.1.1. - floating_subnet_uuid: - description: - - If auto_allocate_floating_ip is true and more than one floating-ip subnets exist, then the subnet for the floating ip address allocation. - - This field is applicable only if the virtualservice belongs to an openstack or aws cloud. - - In openstack or aws cloud it is required when auto_allocate_floating_ip is selected. - - Field deprecated in 17.1.1. - flow_dist: - description: - - Criteria for flow distribution among ses. - - Enum options - LOAD_AWARE, CONSISTENT_HASH_SOURCE_IP_ADDRESS, CONSISTENT_HASH_SOURCE_IP_ADDRESS_AND_PORT. - - Default value when not specified in API or module is interpreted by Avi Controller as LOAD_AWARE. - flow_label_type: - description: - - Criteria for flow labelling. - - Enum options - NO_LABEL, APPLICATION_LABEL, SERVICE_LABEL. - - Default value when not specified in API or module is interpreted by Avi Controller as NO_LABEL. - fqdn: - description: - - Dns resolvable, fully qualified domain name of the virtualservice. - - Only one of 'fqdn' and 'dns_info' configuration is allowed. - host_name_xlate: - description: - - Translate the host name sent to the servers to this value. - - Translate the host name sent from servers back to the value used by the client. - http_policies: - description: - - Http policies applied on the data traffic of the virtual service. - ign_pool_net_reach: - description: - - Ignore pool servers network reachability constraints for virtual service placement. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - ip_address: - description: - - Ip address of the virtual service. - - Field deprecated in 17.1.1. - ipam_network_subnet: - description: - - Subnet and/or network for allocating virtualservice ip by ipam provider module. - - Field deprecated in 17.1.1. - l4_policies: - description: - - L4 policies applied to the data traffic of the virtual service. - - Field introduced in 17.2.7. - limit_doser: - description: - - Limit potential dos attackers who exceed max_cps_per_client significantly to a fraction of max_cps_per_client for a while. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - max_cps_per_client: - description: - - Maximum connections per second per client ip. - - Allowed values are 10-1000. - - Special values are 0- 'unlimited'. - - Default value when not specified in API or module is interpreted by Avi Controller as 0. - microservice_ref: - description: - - Microservice representing the virtual service. - - It is a reference to an object of type microservice. - min_pools_up: - description: - - Minimum number of up pools to mark vs up. - - Field introduced in 18.2.1, 17.2.12. - name: - description: - - Name for the virtual service. - required: true - network_profile_ref: - description: - - Determines network settings such as protocol, tcp or udp, and related options for the protocol. - - It is a reference to an object of type networkprofile. - network_ref: - description: - - Manually override the network on which the virtual service is placed. - - It is a reference to an object of type network. - - Field deprecated in 17.1.1. - network_security_policy_ref: - description: - - Network security policies for the virtual service. - - It is a reference to an object of type networksecuritypolicy. - nsx_securitygroup: - description: - - A list of nsx service groups representing the clients which can access the virtual ip of the virtual service. - - Field introduced in 17.1.1. - performance_limits: - description: - - Optional settings that determine performance limits like max connections or bandwidth etc. - pool_group_ref: - description: - - The pool group is an object that contains pools. - - It is a reference to an object of type poolgroup. - pool_ref: - description: - - The pool is an object that contains destination servers and related attributes such as load-balancing and persistence. - - It is a reference to an object of type pool. - port_uuid: - description: - - (internal-use) network port assigned to the virtual service ip address. - - Field deprecated in 17.1.1. - remove_listening_port_on_vs_down: - description: - - Remove listening port if virtualservice is down. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - requests_rate_limit: - description: - - Rate limit the incoming requests to this virtual service. - saml_sp_config: - description: - - Application-specific saml config. - - Field introduced in 18.2.3. - scaleout_ecmp: - description: - - Disable re-distribution of flows across service engines for a virtual service. - - Enable if the network itself performs flow hashing with ecmp in environments such as gcp. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - se_group_ref: - description: - - The service engine group to use for this virtual service. - - Moving to a new se group is disruptive to existing connections for this vs. - - It is a reference to an object of type serviceenginegroup. - security_policy_ref: - description: - - Security policy applied on the traffic of the virtual service. - - This policy is used to perform security actions such as distributed denial of service (ddos) attack mitigation, etc. - - It is a reference to an object of type securitypolicy. - - Field introduced in 18.2.1. - server_network_profile_ref: - description: - - Determines the network settings profile for the server side of tcp proxied connections. - - Leave blank to use the same settings as the client to vs side of the connection. - - It is a reference to an object of type networkprofile. - service_metadata: - description: - - Metadata pertaining to the service provided by this virtual service. - - In openshift/kubernetes environments, egress pod info is stored. - - Any user input to this field will be overwritten by avi vantage. - service_pool_select: - description: - - Select pool based on destination port. - services: - description: - - List of services defined for this virtual service. - sideband_profile: - description: - - Sideband configuration to be used for this virtualservice.it can be used for sending traffic to sideband vips for external inspection etc. - snat_ip: - description: - - Nat'ted floating source ip address(es) for upstream connection to servers. - sp_pool_refs: - description: - - Gslb pools used to manage site-persistence functionality. - - Each site-persistence pool contains the virtualservices in all the other sites, that is auto-generated by the gslb manager. - - This is a read-only field for the user. - - It is a reference to an object of type pool. - - Field introduced in 17.2.2. - ssl_key_and_certificate_refs: - description: - - Select or create one or two certificates, ec and/or rsa, that will be presented to ssl/tls terminated connections. - - It is a reference to an object of type sslkeyandcertificate. - ssl_profile_ref: - description: - - Determines the set of ssl versions and ciphers to accept for ssl/tls terminated connections. - - It is a reference to an object of type sslprofile. - ssl_profile_selectors: - description: - - Select ssl profile based on client ip address match. - - Field introduced in 18.2.3. - ssl_sess_cache_avg_size: - description: - - Expected number of ssl session cache entries (may be exceeded). - - Allowed values are 1024-16383. - - Default value when not specified in API or module is interpreted by Avi Controller as 1024. - sso_policy: - description: - - Client authentication and authorization policy for the virtualservice. - - Field deprecated in 18.2.3. - - Field introduced in 18.2.1. - sso_policy_ref: - description: - - The sso policy attached to the virtualservice. - - It is a reference to an object of type ssopolicy. - - Field introduced in 18.2.3. - static_dns_records: - description: - - List of static dns records applied to this virtual service. - - These are static entries and no health monitoring is performed against the ip addresses. - subnet: - description: - - Subnet providing reachability for client facing virtual service ip. - - Field deprecated in 17.1.1. - subnet_uuid: - description: - - It represents subnet for the virtual service ip address allocation when auto_allocate_ip is true.it is only applicable in openstack or aws cloud. - - This field is required if auto_allocate_ip is true. - - Field deprecated in 17.1.1. - tenant_ref: - description: - - It is a reference to an object of type tenant. - topology_policies: - description: - - Topology policies applied on the dns traffic of the virtual service based ongslb topology algorithm. - - Field introduced in 18.2.3. - traffic_clone_profile_ref: - description: - - Server network or list of servers for cloning traffic. - - It is a reference to an object of type trafficcloneprofile. - - Field introduced in 17.1.1. - traffic_enabled: - description: - - Knob to enable the virtual service traffic on its assigned service engines. - - This setting is effective only when the enabled flag is set to true. - - Field introduced in 17.2.8. - - Default value when not specified in API or module is interpreted by Avi Controller as True. - type: bool - type: - description: - - Specify if this is a normal virtual service, or if it is the parent or child of an sni-enabled virtual hosted virtual service. - - Enum options - VS_TYPE_NORMAL, VS_TYPE_VH_PARENT, VS_TYPE_VH_CHILD. - - Default value when not specified in API or module is interpreted by Avi Controller as VS_TYPE_NORMAL. - url: - description: - - Avi controller URL of the object. - use_bridge_ip_as_vip: - description: - - Use bridge ip as vip on each host in mesos deployments. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - use_vip_as_snat: - description: - - Use the virtual ip as the snat ip for health monitoring and sending traffic to the backend servers instead of the service engine interface ip. - - The caveat of enabling this option is that the virtualservice cannot be configued in an active-active ha mode. - - Dns based multi vip solution has to be used for ha & non-disruptive upgrade purposes. - - Field introduced in 17.1.9,17.2.3. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - uuid: - description: - - Uuid of the virtualservice. - vh_domain_name: - description: - - The exact name requested from the client's sni-enabled tls hello domain name field. - - If this is a match, the parent vs will forward the connection to this child vs. - vh_parent_vs_uuid: - description: - - Specifies the virtual service acting as virtual hosting (sni) parent. - vip: - description: - - List of virtual service ips. - - While creating a 'shared vs',please use vsvip_ref to point to the shared entities. - - Field introduced in 17.1.1. - vrf_context_ref: - description: - - Virtual routing context that the virtual service is bound to. - - This is used to provide the isolation of the set of networks the application is attached to. - - It is a reference to an object of type vrfcontext. - vs_datascripts: - description: - - Datascripts applied on the data traffic of the virtual service. - vsvip_cloud_config_cksum: - description: - - Checksum of cloud configuration for vsvip. - - Internally set by cloud connector. - - Field introduced in 17.2.9, 18.1.2. - vsvip_ref: - description: - - Mostly used during the creation of shared vs, this field refers to entities that can be shared across virtual services. - - It is a reference to an object of type vsvip. - - Field introduced in 17.1.1. - waf_policy_ref: - description: - - Waf policy for the virtual service. - - It is a reference to an object of type wafpolicy. - - Field introduced in 17.2.1. - weight: - description: - - The quality of service weight to assign to traffic transmitted from this virtual service. - - A higher weight will prioritize traffic versus other virtual services sharing the same service engines. - - Allowed values are 1-128. - - Default value when not specified in API or module is interpreted by Avi Controller as 1. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Create SSL Virtual Service using Pool testpool2 - avi_virtualservice: - controller: 10.10.27.90 - username: admin - password: AviNetworks123! - name: newtestvs - state: present - performance_limits: - max_concurrent_connections: 1000 - services: - - port: 443 - enable_ssl: true - - port: 80 - ssl_profile_ref: '/api/sslprofile?name=System-Standard' - application_profile_ref: '/api/applicationprofile?name=System-Secure-HTTP' - ssl_key_and_certificate_refs: - - '/api/sslkeyandcertificate?name=System-Default-Cert' - ip_address: - addr: 10.90.131.103 - type: V4 - pool_ref: '/api/pool?name=testpool2' -""" - -RETURN = ''' -obj: - description: VirtualService (api/virtualservice) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - active_standby_se_tag=dict(type='str',), - allow_invalid_client_cert=dict(type='bool',), - analytics_policy=dict(type='dict',), - analytics_profile_ref=dict(type='str',), - apic_contract_graph=dict(type='str',), - application_profile_ref=dict(type='str',), - auto_allocate_floating_ip=dict(type='bool',), - auto_allocate_ip=dict(type='bool',), - availability_zone=dict(type='str',), - avi_allocated_fip=dict(type='bool',), - avi_allocated_vip=dict(type='bool',), - azure_availability_set=dict(type='str',), - bulk_sync_kvcache=dict(type='bool',), - client_auth=dict(type='dict',), - close_client_conn_on_config_update=dict(type='bool',), - cloud_config_cksum=dict(type='str',), - cloud_ref=dict(type='str',), - cloud_type=dict(type='str',), - connections_rate_limit=dict(type='dict',), - content_rewrite=dict(type='dict',), - created_by=dict(type='str',), - delay_fairness=dict(type='bool',), - description=dict(type='str',), - discovered_network_ref=dict(type='list',), - discovered_networks=dict(type='list',), - discovered_subnet=dict(type='list',), - dns_info=dict(type='list',), - dns_policies=dict(type='list',), - east_west_placement=dict(type='bool',), - enable_autogw=dict(type='bool',), - enable_rhi=dict(type='bool',), - enable_rhi_snat=dict(type='bool',), - enabled=dict(type='bool',), - error_page_profile_ref=dict(type='str',), - floating_ip=dict(type='dict',), - floating_subnet_uuid=dict(type='str',), - flow_dist=dict(type='str',), - flow_label_type=dict(type='str',), - fqdn=dict(type='str',), - host_name_xlate=dict(type='str',), - http_policies=dict(type='list',), - ign_pool_net_reach=dict(type='bool',), - ip_address=dict(type='dict',), - ipam_network_subnet=dict(type='dict',), - l4_policies=dict(type='list',), - limit_doser=dict(type='bool',), - max_cps_per_client=dict(type='int',), - microservice_ref=dict(type='str',), - min_pools_up=dict(type='int',), - name=dict(type='str', required=True), - network_profile_ref=dict(type='str',), - network_ref=dict(type='str',), - network_security_policy_ref=dict(type='str',), - nsx_securitygroup=dict(type='list',), - performance_limits=dict(type='dict',), - pool_group_ref=dict(type='str',), - pool_ref=dict(type='str',), - port_uuid=dict(type='str',), - remove_listening_port_on_vs_down=dict(type='bool',), - requests_rate_limit=dict(type='dict',), - saml_sp_config=dict(type='dict',), - scaleout_ecmp=dict(type='bool',), - se_group_ref=dict(type='str',), - security_policy_ref=dict(type='str',), - server_network_profile_ref=dict(type='str',), - service_metadata=dict(type='str',), - service_pool_select=dict(type='list',), - services=dict(type='list',), - sideband_profile=dict(type='dict',), - snat_ip=dict(type='list',), - sp_pool_refs=dict(type='list',), - ssl_key_and_certificate_refs=dict(type='list',), - ssl_profile_ref=dict(type='str',), - ssl_profile_selectors=dict(type='list',), - ssl_sess_cache_avg_size=dict(type='int',), - sso_policy=dict(type='dict',), - sso_policy_ref=dict(type='str',), - static_dns_records=dict(type='list',), - subnet=dict(type='dict',), - subnet_uuid=dict(type='str',), - tenant_ref=dict(type='str',), - topology_policies=dict(type='list',), - traffic_clone_profile_ref=dict(type='str',), - traffic_enabled=dict(type='bool',), - type=dict(type='str',), - url=dict(type='str',), - use_bridge_ip_as_vip=dict(type='bool',), - use_vip_as_snat=dict(type='bool',), - uuid=dict(type='str',), - vh_domain_name=dict(type='list',), - vh_parent_vs_uuid=dict(type='str',), - vip=dict(type='list',), - vrf_context_ref=dict(type='str',), - vs_datascripts=dict(type='list',), - vsvip_cloud_config_cksum=dict(type='str',), - vsvip_ref=dict(type='str',), - waf_policy_ref=dict(type='str',), - weight=dict(type='int',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'virtualservice', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_vrfcontext.py b/plugins/modules/network/avi/avi_vrfcontext.py deleted file mode 100644 index 2f57f0399c..0000000000 --- a/plugins/modules/network/avi/avi_vrfcontext.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.2 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_vrfcontext -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of VrfContext Avi RESTful Object -description: - - This module is used to configure VrfContext object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - bgp_profile: - description: - - Bgp local and peer info. - cloud_ref: - description: - - It is a reference to an object of type cloud. - debugvrfcontext: - description: - - Configure debug flags for vrf. - - Field introduced in 17.1.1. - description: - description: - - User defined description for the object. - gateway_mon: - description: - - Configure ping based heartbeat check for gateway in service engines of vrf. - internal_gateway_monitor: - description: - - Configure ping based heartbeat check for all default gateways in service engines of vrf. - - Field introduced in 17.1.1. - name: - description: - - Name of the object. - required: true - static_routes: - description: - - List of staticroute. - system_default: - description: - - Boolean flag to set system_default. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Unique object identifier of the object. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create VrfContext object - avi_vrfcontext: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_vrfcontext -""" - -RETURN = ''' -obj: - description: VrfContext (api/vrfcontext) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - bgp_profile=dict(type='dict',), - cloud_ref=dict(type='str',), - debugvrfcontext=dict(type='dict',), - description=dict(type='str',), - gateway_mon=dict(type='list',), - internal_gateway_monitor=dict(type='dict',), - name=dict(type='str', required=True), - static_routes=dict(type='list',), - system_default=dict(type='bool',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'vrfcontext', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_vsdatascriptset.py b/plugins/modules/network/avi/avi_vsdatascriptset.py deleted file mode 100644 index 07115c2c11..0000000000 --- a/plugins/modules/network/avi/avi_vsdatascriptset.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.1 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_vsdatascriptset -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of VSDataScriptSet Avi RESTful Object -description: - - This module is used to configure VSDataScriptSet object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - created_by: - description: - - Creator name. - - Field introduced in 17.1.11,17.2.4. - datascript: - description: - - Datascripts to execute. - description: - description: - - User defined description for the object. - ipgroup_refs: - description: - - Uuid of ip groups that could be referred by vsdatascriptset objects. - - It is a reference to an object of type ipaddrgroup. - name: - description: - - Name for the virtual service datascript collection. - required: true - pool_group_refs: - description: - - Uuid of pool groups that could be referred by vsdatascriptset objects. - - It is a reference to an object of type poolgroup. - pool_refs: - description: - - Uuid of pools that could be referred by vsdatascriptset objects. - - It is a reference to an object of type pool. - protocol_parser_refs: - description: - - List of protocol parsers that could be referred by vsdatascriptset objects. - - It is a reference to an object of type protocolparser. - - Field introduced in 18.2.3. - string_group_refs: - description: - - Uuid of string groups that could be referred by vsdatascriptset objects. - - It is a reference to an object of type stringgroup. - tenant_ref: - description: - - It is a reference to an object of type tenant. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the virtual service datascript collection. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create VSDataScriptSet object - avi_vsdatascriptset: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_vsdatascriptset -""" - -RETURN = ''' -obj: - description: VSDataScriptSet (api/vsdatascriptset) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - created_by=dict(type='str',), - datascript=dict(type='list',), - description=dict(type='str',), - ipgroup_refs=dict(type='list',), - name=dict(type='str', required=True), - pool_group_refs=dict(type='list',), - pool_refs=dict(type='list',), - protocol_parser_refs=dict(type='list',), - string_group_refs=dict(type='list',), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'vsdatascriptset', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_vsvip.py b/plugins/modules/network/avi/avi_vsvip.py deleted file mode 100644 index fc54b3f11b..0000000000 --- a/plugins/modules/network/avi/avi_vsvip.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# Avi Version: 17.1.2 -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_vsvip -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of VsVip Avi RESTful Object -description: - - This module is used to configure VsVip object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - cloud_ref: - description: - - It is a reference to an object of type cloud. - - Field introduced in 17.1.1. - dns_info: - description: - - Service discovery specific data including fully qualified domain name, type and time-to-live of the dns record. - - Field introduced in 17.1.1. - east_west_placement: - description: - - Force placement on all service engines in the service engine group (container clouds only). - - Field introduced in 17.1.1. - - Default value when not specified in API or module is interpreted by Avi Controller as False. - type: bool - name: - description: - - Name for the vsvip object. - - Field introduced in 17.1.1. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.1.1. - url: - description: - - Avi controller URL of the object. - use_standard_alb: - description: - - This overrides the cloud level default and needs to match the se group value in which it will be used if the se group use_standard_alb value is - - set. - - This is only used when fip is used for vs on azure cloud. - - Field introduced in 18.2.3. - type: bool - uuid: - description: - - Uuid of the vsvip object. - - Field introduced in 17.1.1. - vip: - description: - - List of virtual service ips and other shareable entities. - - Field introduced in 17.1.1. - vrf_context_ref: - description: - - Virtual routing context that the virtual service is bound to. - - This is used to provide the isolation of the set of networks the application is attached to. - - It is a reference to an object of type vrfcontext. - - Field introduced in 17.1.1. - vsvip_cloud_config_cksum: - description: - - Checksum of cloud configuration for vsvip. - - Internally set by cloud connector. - - Field introduced in 17.2.9, 18.1.2. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create VsVip object - avi_vsvip: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_vsvip -""" - -RETURN = ''' -obj: - description: VsVip (api/vsvip) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - cloud_ref=dict(type='str',), - dns_info=dict(type='list',), - east_west_placement=dict(type='bool',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - use_standard_alb=dict(type='bool',), - uuid=dict(type='str',), - vip=dict(type='list',), - vrf_context_ref=dict(type='str',), - vsvip_cloud_config_cksum=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'vsvip', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/avi/avi_webhook.py b/plugins/modules/network/avi/avi_webhook.py deleted file mode 100644 index e42b8a5cbf..0000000000 --- a/plugins/modules/network/avi/avi_webhook.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/python -# -# @author: Gaurav Rastogi (grastogi@avinetworks.com) -# Eric Anderson (eanderson@avinetworks.com) -# module_check: supported -# -# Copyright: (c) 2017 Gaurav Rastogi, -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: avi_webhook -author: Gaurav Rastogi (@grastogi23) - -short_description: Module for setup of Webhook Avi RESTful Object -description: - - This module is used to configure Webhook object - - more examples at U(https://github.com/avinetworks/devops) -requirements: [ avisdk ] -options: - state: - description: - - The state that should be applied on the entity. - default: present - choices: ["absent", "present"] - avi_api_update_method: - description: - - Default method for object update is HTTP PUT. - - Setting to patch will override that behavior to use HTTP PATCH. - default: put - choices: ["put", "patch"] - avi_api_patch_op: - description: - - Patch operation to use when using avi_api_update_method as patch. - choices: ["add", "replace", "delete"] - callback_url: - description: - - Callback url for the webhook. - - Field introduced in 17.1.1. - description: - description: - - Field introduced in 17.1.1. - name: - description: - - The name of the webhook profile. - - Field introduced in 17.1.1. - required: true - tenant_ref: - description: - - It is a reference to an object of type tenant. - - Field introduced in 17.1.1. - url: - description: - - Avi controller URL of the object. - uuid: - description: - - Uuid of the webhook profile. - - Field introduced in 17.1.1. - verification_token: - description: - - Verification token sent back with the callback asquery parameters. - - Field introduced in 17.1.1. -extends_documentation_fragment: -- community.general.avi - -''' - -EXAMPLES = """ -- name: Example to create Webhook object - avi_webhook: - controller: 10.10.25.42 - username: admin - password: something - state: present - name: sample_webhook -""" - -RETURN = ''' -obj: - description: Webhook (api/webhook) object - returned: success, changed - type: dict -''' - -from ansible.module_utils.basic import AnsibleModule -try: - from ansible_collections.community.general.plugins.module_utils.network.avi.avi import ( - avi_common_argument_spec, avi_ansible_api, HAS_AVI) -except ImportError: - HAS_AVI = False - - -def main(): - argument_specs = dict( - state=dict(default='present', - choices=['absent', 'present']), - avi_api_update_method=dict(default='put', - choices=['put', 'patch']), - avi_api_patch_op=dict(choices=['add', 'replace', 'delete']), - callback_url=dict(type='str',), - description=dict(type='str',), - name=dict(type='str', required=True), - tenant_ref=dict(type='str',), - url=dict(type='str',), - uuid=dict(type='str',), - verification_token=dict(type='str',), - ) - argument_specs.update(avi_common_argument_spec()) - module = AnsibleModule( - argument_spec=argument_specs, supports_check_mode=True) - if not HAS_AVI: - return module.fail_json(msg=( - 'Avi python API SDK (avisdk>=17.1) or requests is not installed. ' - 'For more details visit https://github.com/avinetworks/sdk.')) - return avi_ansible_api(module, 'webhook', - set([])) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/bigswitch/bcf_switch.py b/plugins/modules/network/bigswitch/bcf_switch.py deleted file mode 100644 index dedabc8cce..0000000000 --- a/plugins/modules/network/bigswitch/bcf_switch.py +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, Ted Elhourani -# 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: bcf_switch -author: "Ted (@tedelhourani)" -short_description: Create and remove a bcf switch. -description: - - Create and remove a Big Cloud Fabric switch. -options: - name: - description: - - The name of the switch. - required: true - fabric_role: - description: - - Fabric role of the switch. - choices: ['spine', 'leaf'] - required: true - leaf_group: - description: - - The leaf group of the switch if the switch is a leaf. - required: false - mac: - description: - - The MAC address of the switch. - required: true - state: - description: - - Whether the switch should be present or absent. - default: present - choices: ['present', 'absent'] - controller: - description: - - The controller IP address. - required: true - validate_certs: - description: - - If C(false), SSL certificates will not be validated. This should only be used - on personally controlled devices using self-signed certificates. - required: false - default: true - type: bool - access_token: - description: - - Big Cloud Fabric access token. If this isn't set then the environment variable C(BIGSWITCH_ACCESS_TOKEN) is used. -''' - - -EXAMPLES = ''' -- name: bcf leaf switch - bcf_switch: - name: Rack1Leaf1 - fabric_role: leaf - leaf_group: R1 - mac: 00:00:00:02:00:02 - controller: '{{ inventory_hostname }}' - state: present - validate_certs: false -''' - - -RETURN = ''' # ''' - -import os -import traceback - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.bigswitch.bigswitch import Rest -from ansible.module_utils._text import to_native - - -def switch(module, check_mode): - try: - access_token = module.params['access_token'] or os.environ['BIGSWITCH_ACCESS_TOKEN'] - except KeyError as e: - module.fail_json(msg='Unable to load %s' % e.message, exception=traceback.format_exc()) - - name = module.params['name'] - fabric_role = module.params['fabric_role'] - leaf_group = module.params['leaf_group'] - dpid = '00:00:' + module.params['mac'] - state = module.params['state'] - controller = module.params['controller'] - - rest = Rest(module, - {'content-type': 'application/json', 'Cookie': 'session_cookie=' + access_token}, - 'https://' + controller + ':8443/api/v1/data/controller/core') - - response = rest.get('switch-config', data={}) - if response.status_code != 200: - module.fail_json(msg="failed to obtain existing switch config: {0}".format(response.json['description'])) - - config_present = False - for switch in response.json: - if all((switch['name'] == name, - switch['fabric-role'] == fabric_role, - switch['dpid'] == dpid)): - config_present = switch.get('leaf-group', None) == leaf_group - if config_present: - break - - if state in ('present') and config_present: - module.exit_json(changed=False) - - if state in ('absent') and not config_present: - module.exit_json(changed=False) - - if check_mode: - module.exit_json(changed=True) - - if state in ('present'): - data = {'name': name, 'fabric-role': fabric_role, 'leaf-group': leaf_group, 'dpid': dpid} - response = rest.put('switch-config[name="%s"]' % name, data) - if response.status_code == 204: - module.exit_json(changed=True) - else: - module.fail_json(msg="error configuring switch '{0}': {1}".format(name, response.json['description'])) - - if state in ('absent'): - response = rest.delete('switch-config[name="%s"]' % name, data={}) - if response.status_code == 204: - module.exit_json(changed=True) - else: - module.fail_json(msg="error deleting switch '{0}': {1}".format(name, response.json['description'])) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(type='str', required=True), - fabric_role=dict(choices=['spine', 'leaf'], required=True), - leaf_group=dict(type='str', required=False), - mac=dict(type='str', required=True), - controller=dict(type='str', required=True), - state=dict(choices=['present', 'absent'], default='present'), - validate_certs=dict(type='bool', default='True'), - access_token=dict(type='str', no_log=True) - ), - supports_check_mode=True, - ) - - try: - switch(module, check_mode=module.check_mode) - except Exception as e: - module.fail_json(msg=to_native(e), exception=traceback.format_exc()) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/bigswitch/bigmon_chain.py b/plugins/modules/network/bigswitch/bigmon_chain.py deleted file mode 100644 index 67428b257c..0000000000 --- a/plugins/modules/network/bigswitch/bigmon_chain.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Ted Elhourani -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# Ansible module to manage Big Monitoring Fabric service chains - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: bigmon_chain -author: "Ted (@tedelhourani)" -short_description: Create and remove a bigmon inline service chain. -description: - - Create and remove a bigmon inline service chain. -options: - name: - description: - - The name of the chain. - required: true - state: - description: - - Whether the service chain should be present or absent. - default: present - choices: ['present', 'absent'] - controller: - description: - - The controller IP address. - required: true - validate_certs: - description: - - If C(false), SSL certificates will not be validated. This should only be used - on personally controlled devices using self-signed certificates. - required: false - default: true - type: bool - access_token: - description: - - Bigmon access token. If this isn't set, the environment variable C(BIGSWITCH_ACCESS_TOKEN) is used. -''' - - -EXAMPLES = ''' -- name: bigmon inline service chain - bigmon_chain: - name: MyChain - controller: '{{ inventory_hostname }}' - state: present - validate_certs: false -''' - - -RETURN = ''' # ''' - -import os -import traceback - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.bigswitch.bigswitch import Rest -from ansible.module_utils._text import to_native - - -def chain(module): - try: - access_token = module.params['access_token'] or os.environ['BIGSWITCH_ACCESS_TOKEN'] - except KeyError as e: - module.fail_json(msg='Unable to load %s' % e.message, exception=traceback.format_exc()) - - name = module.params['name'] - state = module.params['state'] - controller = module.params['controller'] - - rest = Rest(module, - {'content-type': 'application/json', 'Cookie': 'session_cookie=' + access_token}, - 'https://' + controller + ':8443/api/v1/data/controller/applications/bigchain') - - if None in (name, state, controller): - module.fail_json(msg='parameter `name` is missing') - - response = rest.get('chain?config=true', data={}) - if response.status_code != 200: - module.fail_json(msg="failed to obtain existing chain config: {0}".format(response.json['description'])) - - config_present = False - matching = [chain for chain in response.json if chain['name'] == name] - if matching: - config_present = True - - if state in ('present') and config_present: - module.exit_json(changed=False) - - if state in ('absent') and not config_present: - module.exit_json(changed=False) - - if state in ('present'): - response = rest.put('chain[name="%s"]' % name, data={'name': name}) - if response.status_code == 204: - module.exit_json(changed=True) - else: - module.fail_json(msg="error creating chain '{0}': {1}".format(name, response.json['description'])) - - if state in ('absent'): - response = rest.delete('chain[name="%s"]' % name, data={}) - if response.status_code == 204: - module.exit_json(changed=True) - else: - module.fail_json(msg="error deleting chain '{0}': {1}".format(name, response.json['description'])) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(type='str', required=True), - controller=dict(type='str', required=True), - state=dict(choices=['present', 'absent'], default='present'), - validate_certs=dict(type='bool', default='True'), - access_token=dict(type='str', no_log=True) - ) - ) - - try: - chain(module) - except Exception as e: - module.fail_json(msg=to_native(e), exception=traceback.format_exc()) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/bigswitch/bigmon_policy.py b/plugins/modules/network/bigswitch/bigmon_policy.py deleted file mode 100644 index 421c76f225..0000000000 --- a/plugins/modules/network/bigswitch/bigmon_policy.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2016, Ted Elhourani -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# Ansible module to manage Big Monitoring Fabric service chains - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: bigmon_policy -author: "Ted (@tedelhourani)" -short_description: Create and remove a bigmon out-of-band policy. -description: - - Create and remove a bigmon out-of-band policy. -options: - name: - description: - - The name of the policy. - required: true - policy_description: - description: - - Description of policy. - action: - description: - - Forward matching packets to delivery interfaces, Drop is for measure rate of matching packets, - but do not forward to delivery interfaces, capture packets and write to a PCAP file, or enable NetFlow generation. - default: forward - choices: ['forward', 'drop', 'flow-gen'] - priority: - description: - - A priority associated with this policy. The higher priority policy takes precedence over a lower priority. - default: 100 - duration: - description: - - Run policy for duration duration or until delivery_packet_count packets are delivered, whichever comes first. - default: 0 - start_time: - description: - - Date the policy becomes active - default: ansible_date_time.iso8601 - delivery_packet_count: - description: - - Run policy until delivery_packet_count packets are delivered. - default: 0 - state: - description: - - Whether the policy should be present or absent. - default: present - choices: ['present', 'absent'] - controller: - description: - - The controller address. - required: true - validate_certs: - description: - - If C(false), SSL certificates will not be validated. This should only be used - on personally controlled devices using self-signed certificates. - required: false - default: true - type: bool - access_token: - description: - - Bigmon access token. If this isn't set, the environment variable C(BIGSWITCH_ACCESS_TOKEN) is used. - -''' - -EXAMPLES = ''' -- name: policy to aggregate filter and deliver data center (DC) 1 traffic - bigmon_policy: - name: policy1 - policy_description: DC 1 traffic policy - action: drop - controller: '{{ inventory_hostname }}' - state: present - validate_certs: false -''' - -RETURN = ''' # ''' - -import datetime -import os -import traceback - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.bigswitch.bigswitch import Rest -from ansible.module_utils._text import to_native - - -def policy(module): - try: - access_token = module.params['access_token'] or os.environ['BIGSWITCH_ACCESS_TOKEN'] - except KeyError as e: - module.fail_json(msg='Unable to load %s' % e.message, exception=traceback.format_exc()) - - name = module.params['name'] - policy_description = module.params['policy_description'] - action = module.params['action'] - priority = module.params['priority'] - duration = module.params['duration'] - start_time = module.params['start_time'] - delivery_packet_count = module.params['delivery_packet_count'] - state = module.params['state'] - controller = module.params['controller'] - - rest = Rest(module, - {'content-type': 'application/json', 'Cookie': 'session_cookie=' + access_token}, - 'https://' + controller + ':8443/api/v1/data/controller/applications/bigtap') - - if name is None: - module.fail_json(msg='parameter `name` is missing') - - response = rest.get('policy?config=true', data={}) - if response.status_code != 200: - module.fail_json(msg="failed to obtain existing policy config: {0}".format(response.json['description'])) - - config_present = False - - matching = [policy for policy in response.json - if policy['name'] == name and - policy['duration'] == duration and - policy['delivery-packet-count'] == delivery_packet_count and - policy['policy-description'] == policy_description and - policy['action'] == action and - policy['priority'] == priority] - - if matching: - config_present = True - - if state in ('present') and config_present: - module.exit_json(changed=False) - - if state in ('absent') and not config_present: - module.exit_json(changed=False) - - if state in ('present'): - data = {'name': name, 'action': action, 'policy-description': policy_description, - 'priority': priority, 'duration': duration, 'start-time': start_time, - 'delivery-packet-count': delivery_packet_count} - - response = rest.put('policy[name="%s"]' % name, data=data) - if response.status_code == 204: - module.exit_json(changed=True) - else: - module.fail_json(msg="error creating policy '{0}': {1}".format(name, response.json['description'])) - - if state in ('absent'): - response = rest.delete('policy[name="%s"]' % name, data={}) - if response.status_code == 204: - module.exit_json(changed=True) - else: - module.fail_json(msg="error deleting policy '{0}': {1}".format(name, response.json['description'])) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(type='str', required=True), - policy_description=dict(type='str', default=''), - action=dict(choices=['forward', 'drop', 'capture', 'flow-gen'], default='forward'), - priority=dict(type='int', default=100), - duration=dict(type='int', default=0), - start_time=dict(type='str', default=datetime.datetime.now().isoformat() + '+00:00'), - delivery_packet_count=dict(type='int', default=0), - controller=dict(type='str', required=True), - state=dict(choices=['present', 'absent'], default='present'), - validate_certs=dict(type='bool', default='True'), - access_token=dict(type='str', no_log=True) - ) - ) - - try: - policy(module) - except Exception as e: - module.fail_json(msg=to_native(e), exception=traceback.format_exc()) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_access_layer_facts.py b/plugins/modules/network/check_point/checkpoint_access_layer_facts.py deleted file mode 100644 index c18de677e8..0000000000 --- a/plugins/modules/network/check_point/checkpoint_access_layer_facts.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_access_layer_facts -short_description: Get access layer facts on Check Point over Web Services API -description: - - Get access layer facts on Check Point devices. - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - uid: - description: - - UID of access layer object. - type: str - name: - description: - - Name of the access layer object. - type: str -''' - -EXAMPLES = """ -- name: Get object facts - checkpoint_access_layer_facts: -""" - -RETURN = """ -ansible_facts: - description: The checkpoint access layer facts. - returned: always. - type: list -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection - - -def get_access_layer(module, connection): - uid = module.params['uid'] - name = module.params['name'] - - payload = {} - - if uid: - payload = {'uid': uid} - code, result = connection.send_request('/web_api/show-access-layer', payload) - elif name: - payload = {'name': name} - code, result = connection.send_request('/web_api/show-access-layer', payload) - else: - code, result = connection.send_request('/web_api/show-access-layers', payload) - - return code, result - - -def main(): - argument_spec = dict( - uid=dict(type='str', default=None), - name=dict(type='str', default=None) - ) - - module = AnsibleModule(argument_spec=argument_spec) - connection = Connection(module._socket_path) - - code, response = get_access_layer(module, connection) - - if code == 200: - module.exit_json(ansible_facts=dict(checkpoint_access_layers=response)) - else: - module.fail_json(msg='Check Point device returned error {0} with message {1}'.format(code, response)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_access_rule.py b/plugins/modules/network/check_point/checkpoint_access_rule.py deleted file mode 100644 index c081734173..0000000000 --- a/plugins/modules/network/check_point/checkpoint_access_rule.py +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_access_rule -short_description: Manages access rules on Check Point over Web Services API -description: - - Manages access rules on Check Point devices including creating, updating, removing access rules objects, - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - name: - description: - - Name of the access rule. - type: str - layer: - description: - - Layer to attach the access rule to. - required: True - type: str - position: - description: - - Position of the access rule. - type: str - source: - description: - - Source object of the access rule. - type: str - destination: - description: - - Destination object of the access rule. - type: str - action: - description: - - Action of the access rule (accept, drop, inform, etc). - type: str - default: drop - enabled: - description: - - Enabled or disabled flag. - type: bool - default: True - state: - description: - - State of the access rule (present or absent). Defaults to present. - type: str - default: present - auto_publish_session: - description: - - Publish the current session if changes have been performed - after task completes. - type: bool - default: 'yes' - auto_install_policy: - description: - - Install the package policy if changes have been performed - after the task completes. - type: bool - default: 'yes' - policy_package: - description: - - Package policy name to be installed. - type: str - default: 'standard' - targets: - description: - - Targets to install the package policy on. - type: list -''' - -EXAMPLES = """ -- name: Create access rule - checkpoint_access_rule: - layer: Network - name: "Drop attacker" - position: top - source: attacker - destination: Any - action: Drop - -- name: Delete access rule - checkpoint_access_rule: - layer: Network - name: "Drop attacker" -""" - -RETURN = """ -checkpoint_access_rules: - description: The checkpoint access rule object created or updated. - returned: always, except when deleting the access rule. - type: list -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.check_point.mgmt.plugins.module_utils.checkpoint import checkpoint_argument_spec, publish, install_policy - - -def get_access_rule(module, connection): - name = module.params['name'] - layer = module.params['layer'] - - payload = {'name': name, 'layer': layer} - - code, response = connection.send_request('/web_api/show-access-rule', payload) - - return code, response - - -def create_access_rule(module, connection): - name = module.params['name'] - layer = module.params['layer'] - position = module.params['position'] - source = module.params['source'] - destination = module.params['destination'] - action = module.params['action'] - - payload = {'name': name, - 'layer': layer, - 'position': position, - 'source': source, - 'destination': destination, - 'action': action} - - code, response = connection.send_request('/web_api/add-access-rule', payload) - - return code, response - - -def update_access_rule(module, connection): - name = module.params['name'] - layer = module.params['layer'] - position = module.params['position'] - source = module.params['source'] - destination = module.params['destination'] - action = module.params['action'] - enabled = module.params['enabled'] - - payload = {'name': name, - 'layer': layer, - 'position': position, - 'source': source, - 'destination': destination, - 'action': action, - 'enabled': enabled} - - code, response = connection.send_request('/web_api/set-access-rule', payload) - - return code, response - - -def delete_access_rule(module, connection): - name = module.params['name'] - layer = module.params['layer'] - - payload = {'name': name, - 'layer': layer, - } - - code, response = connection.send_request('/web_api/delete-access-rule', payload) - - return code, response - - -def needs_update(module, access_rule): - res = False - - if module.params['source'] and module.params['source'] != access_rule['source'][0]['name']: - res = True - if module.params['destination'] and module.params['destination'] != access_rule['destination'][0]['name']: - res = True - if module.params['action'] != access_rule['action']['name']: - res = True - if module.params['enabled'] != access_rule['enabled']: - res = True - - return res - - -def main(): - argument_spec = dict( - name=dict(type='str', required=True), - layer=dict(type='str'), - position=dict(type='str'), - source=dict(type='str'), - destination=dict(type='str'), - action=dict(type='str', default='drop'), - enabled=dict(type='bool', default=True), - state=dict(type='str', default='present') - ) - argument_spec.update(checkpoint_argument_spec) - - required_if = [('state', 'present', ('layer', 'position'))] - module = AnsibleModule(argument_spec=argument_spec, required_if=required_if) - connection = Connection(module._socket_path) - code, response = get_access_rule(module, connection) - result = {'changed': False} - - if module.params['state'] == 'present': - if code == 200: - if needs_update(module, response): - code, response = update_access_rule(module, connection) - if code != 200: - module.fail_json(msg=response) - if module.params['auto_publish_session']: - publish(connection) - - if module.params['auto_install_policy']: - install_policy(connection, module.params['policy_package'], module.params['targets']) - - result['changed'] = True - result['checkpoint_access_rules'] = response - else: - pass - elif code == 404: - code, response = create_access_rule(module, connection) - if code != 200: - module.fail_json(msg=response) - if module.params['auto_publish_session']: - publish(connection) - - if module.params['auto_install_policy']: - install_policy(connection, module.params['policy_package'], module.params['targets']) - - result['changed'] = True - result['checkpoint_access_rules'] = response - else: - if code == 200: - code, response = delete_access_rule(module, connection) - if code != 200: - module.fail_json(msg=response) - if module.params['auto_publish_session']: - publish(connection) - - if module.params['auto_install_policy']: - install_policy(connection, module.params['policy_package'], module.params['targets']) - - result['changed'] = True - result['checkpoint_access_rules'] = response - elif code == 404: - pass - - result['checkpoint_session_uid'] = connection.get_session_uid() - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_access_rule_facts.py b/plugins/modules/network/check_point/checkpoint_access_rule_facts.py deleted file mode 100644 index a477bd407e..0000000000 --- a/plugins/modules/network/check_point/checkpoint_access_rule_facts.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_access_rule_facts -short_description: Get access rules objects facts on Check Point over Web Services API -description: - - Get access rules objects facts on Check Point devices. - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - name: - description: - - Name of the access rule. If not provided, UID is required. - type: str - uid: - description: - - UID of the access rule. If not provided, name is required. - type: str - layer: - description: - - Layer the access rule is attached to. - required: True - type: str -''' - -EXAMPLES = """ -- name: Get access rule facts - checkpoint_access_rule_facts: - layer: Network - name: "Drop attacker" -""" - -RETURN = """ -ansible_facts: - description: The checkpoint access rule object facts. - returned: always. - type: list -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection - - -def get_access_rule(module, connection): - name = module.params['name'] - uid = module.params['uid'] - layer = module.params['layer'] - - if uid: - payload = {'uid': uid, 'layer': layer} - elif name: - payload = {'name': name, 'layer': layer} - - code, response = connection.send_request('/web_api/show-access-rule', payload) - - return code, response - - -def main(): - argument_spec = dict( - name=dict(type='str'), - uid=dict(type='str'), - layer=dict(type='str', required=True), - ) - - module = AnsibleModule(argument_spec=argument_spec) - connection = Connection(module._socket_path) - code, response = get_access_rule(module, connection) - if code == 200: - module.exit_json(ansible_facts=dict(checkpoint_access_rules=response)) - else: - module.fail_json(msg='Checkpoint device returned error {0} with message {1}'.format(code, response)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_host.py b/plugins/modules/network/check_point/checkpoint_host.py deleted file mode 100644 index 5d651c3007..0000000000 --- a/plugins/modules/network/check_point/checkpoint_host.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_host -short_description: Manages host objects on Check Point over Web Services API -description: - - Manages host objects on Check Point devices including creating, updating, removing access rules objects. - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - name: - description: - - Name of the access rule. - type: str - required: True - ip_address: - description: - - IP address of the host object. - type: str - state: - description: - - State of the access rule (present or absent). Defaults to present. - type: str - default: present - auto_publish_session: - description: - - Publish the current session if changes have been performed - after task completes. - type: bool - default: 'yes' - auto_install_policy: - description: - - Install the package policy if changes have been performed - after the task completes. - type: bool - default: 'yes' - policy_package: - description: - - Package policy name to be installed. - type: str - default: 'standard' - targets: - description: - - Targets to install the package policy on. - type: list -''' - -EXAMPLES = """ -- name: Create host object - checkpoint_host: - name: attacker - ip_address: 192.168.0.15 - -- name: Delete host object - checkpoint_host: - name: attacker - state: absent -""" - -RETURN = """ -checkpoint_hosts: - description: The checkpoint host object created or updated. - returned: always, except when deleting the host. - type: list -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.check_point.mgmt.plugins.module_utils.checkpoint import checkpoint_argument_spec, publish, install_policy - - -def get_host(module, connection): - name = module.params['name'] - - payload = {'name': name} - - code, response = connection.send_request('/web_api/show-host', payload) - - return code, response - - -def create_host(module, connection): - name = module.params['name'] - ip_address = module.params['ip_address'] - - payload = {'name': name, - 'ip-address': ip_address} - - code, response = connection.send_request('/web_api/add-host', payload) - - return code, response - - -def update_host(module, connection): - name = module.params['name'] - ip_address = module.params['ip_address'] - - payload = {'name': name, - 'ip-address': ip_address} - - code, response = connection.send_request('/web_api/set-host', payload) - - return code, response - - -def delete_host(module, connection): - name = module.params['name'] - - payload = {'name': name} - - code, response = connection.send_request('/web_api/delete-host', payload) - - return code, response - - -def needs_update(module, host): - res = False - - if module.params['ip_address'] != host['ipv4-address']: - res = True - - return res - - -def main(): - argument_spec = dict( - name=dict(type='str', required=True), - ip_address=dict(type='str'), - state=dict(type='str', default='present') - ) - argument_spec.update(checkpoint_argument_spec) - - module = AnsibleModule(argument_spec=argument_spec) - connection = Connection(module._socket_path) - code, response = get_host(module, connection) - result = {'changed': False} - - if module.params['state'] == 'present': - if code == 200: - if needs_update(module, response): - code, response = update_host(module, connection) - if code != 200: - module.fail_json(msg=response) - if module.params['auto_publish_session']: - publish(connection) - - if module.params['auto_install_policy']: - install_policy(connection, module.params['policy_package'], module.params['targets']) - - result['changed'] = True - result['checkpoint_hosts'] = response - else: - pass - elif code == 404: - code, response = create_host(module, connection) - if code != 200: - module.fail_json(msg=response) - if module.params['auto_publish_session']: - publish(connection) - - if module.params['auto_install_policy']: - install_policy(connection, module.params['policy_package'], module.params['targets']) - - result['changed'] = True - result['checkpoint_hosts'] = response - else: - if code == 200: - # Handle deletion - code, response = delete_host(module, connection) - if code != 200: - module.fail_json(msg=response) - if module.params['auto_publish_session']: - publish(connection) - - if module.params['auto_install_policy']: - install_policy(connection, module.params['policy_package'], module.params['targets']) - - result['changed'] = True - result['checkpoint_hosts'] = response - elif code == 404: - pass - - result['checkpoint_session_uid'] = connection.get_session_uid() - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_host_facts.py b/plugins/modules/network/check_point/checkpoint_host_facts.py deleted file mode 100644 index c8a8d8aa38..0000000000 --- a/plugins/modules/network/check_point/checkpoint_host_facts.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_host_facts -short_description: Get host objects facts on Check Point over Web Services API -description: - - Get host objects facts on Check Point devices. - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - name: - description: - - Name of the host object. If name is not provided, UID is required. - type: str - uid: - description: - - UID of the host object. If UID is not provided, name is required. - type: str -''' - -EXAMPLES = """ -- name: Get host object facts - checkpoint_host_facts: - name: attacker -""" - -RETURN = """ -ansible_hosts: - description: The checkpoint host object facts. - returned: always. - type: list -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection - - -def get_host(module, connection): - name = module.params['name'] - uid = module.params['uid'] - - if uid: - payload = {'uid': uid} - elif name: - payload = {'name': name} - - code, result = connection.send_request('/web_api/show-host', payload) - - return code, result - - -def main(): - argument_spec = dict( - name=dict(type='str'), - uid=dict(type='str'), - ) - - required_one_of = [('name', 'uid')] - module = AnsibleModule(argument_spec=argument_spec, required_one_of=required_one_of) - connection = Connection(module._socket_path) - - code, response = get_host(module, connection) - - if code == 200: - module.exit_json(ansible_facts=dict(checkpoint_hosts=response)) - else: - module.fail_json(msg='Checkpoint device returned error {0} with message {1}'.format(code, response)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_object_facts.py b/plugins/modules/network/check_point/checkpoint_object_facts.py deleted file mode 100644 index 96d0145126..0000000000 --- a/plugins/modules/network/check_point/checkpoint_object_facts.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_object_facts -short_description: Get object facts on Check Point over Web Services API -description: - - Get object facts on Check Point devices. - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - uid: - description: - - UID of the object. If UID is not provided, it will do a full search - which can be filtered with the filter argument. - object_filter: - description: - - Filter expression for search. It accepts AND/OR logical operators and performs a textual - and IP address search. To search only by IP address, set ip_only argument to True. - which can be filtered with the filter argument. - ip_only: - description: - - Filter only by IP address. - type: bool - default: false - object_type: - description: - - Type of the object to search. Must be a valid API resource name - type: str -''' - -EXAMPLES = """ -- name: Get object facts - checkpoint_object_facts: - object_filter: 192.168.30.30 - ip_only: yes -""" - -RETURN = """ -ansible_hosts: - description: The checkpoint object facts. - returned: always. - type: list -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection - - -def get_object(module, connection): - uid = module.params['uid'] - object_filter = module.params['object_filter'] - ip_only = module.params['ip_only'] - object_type = module.params['object_type'] - - if uid: - payload = {'uid': uid} - code, result = connection.send_request('/web_api/show-object', payload) - else: - payload = {'filter': object_filter, 'ip-only': ip_only, 'type': object_type} - code, result = connection.send_request('/web_api/show-objects', payload) - - return code, result - - -def main(): - argument_spec = dict( - uid=dict(type='str', default=None), - object_filter=dict(type='str', default=None), - ip_only=dict(type='bool', default=False), - object_type=dict(type='str', default=None) - ) - - module = AnsibleModule(argument_spec=argument_spec) - connection = Connection(module._socket_path) - - code, response = get_object(module, connection) - - if code == 200: - module.exit_json(ansible_facts=dict(checkpoint_objects=response)) - else: - module.fail_json(msg='Check Point device returned error {0} with message {1}'.format(code, response)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_run_script.py b/plugins/modules/network/check_point/checkpoint_run_script.py deleted file mode 100644 index abee6b5093..0000000000 --- a/plugins/modules/network/check_point/checkpoint_run_script.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_run_script -short_description: Run scripts on Check Point devices over Web Services API -description: - - Run scripts on Check Point devices. - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - script_name: - description: - - Name of the script. - type: str - required: True - script: - description: - - Script body contents. - type: str - required: True - targets: - description: - - Targets the script should be run against. Can reference either name or UID. - type: list - required: True -''' - -EXAMPLES = """ -- name: Run script - checkpoint_run_script: - script_name: "List root" - script: ls -l / - targets: - - mycheckpointgw -""" - -RETURN = """ -checkpoint_run_script: - description: The checkpoint run script output. - returned: always. - type: list -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection - - -def run_script(module, connection): - script_name = module.params['script_name'] - script = module.params['script'] - targets = module.params['targets'] - - payload = {'script-name': script_name, - 'script': script, - 'targets': targets} - - code, response = connection.send_request('/web_api/run-script', payload) - - return code, response - - -def main(): - argument_spec = dict( - script_name=dict(type='str', required=True), - script=dict(type='str', required=True), - targets=dict(type='list', required=True) - ) - - module = AnsibleModule(argument_spec=argument_spec) - connection = Connection(module._socket_path) - code, response = run_script(module, connection) - result = {'changed': True} - - if code == 200: - result['checkpoint_run_script'] = response - else: - module.fail_json(msg='Checkpoint device returned error {0} with message {1}'.format(code, response)) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_session.py b/plugins/modules/network/check_point/checkpoint_session.py deleted file mode 100644 index e1d45cd03c..0000000000 --- a/plugins/modules/network/check_point/checkpoint_session.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_session -short_description: Manages session objects on Check Point over Web Services API -description: - - Manages session objects on Check Point devices performing actions like publish and discard. - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - uid: - description: - - UID of the session. - type: str - required: True - state: - description: - - Action to perform on the session object. Valid choices are published and discarded. - type: str - choices: ['published', 'discarded'] - default: published -''' - -EXAMPLES = """ -- name: Publish session - checkpoint_session: - uid: 7a13a360-9b24-40d7-acd3-5b50247be33e - state: published - -- name: Discard session - checkpoint_session: - uid: 7a13a360-9b24-40d7-acd3-5b50247be33e - state: discarded -""" - -RETURN = """ -checkpoint_session: - description: The checkpoint session output per return from API. It will differ depending on action. - returned: always. - type: list -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection - - -def get_session(module, connection): - payload = {'uid': module.params['uid']} - - code, result = connection.send_request('/web_api/show-session', payload) - - return code, result - - -def main(): - argument_spec = dict( - uid=dict(type='str', default=None), - state=dict(type='str', default='published', choices=['published', 'discarded']) - ) - - module = AnsibleModule(argument_spec=argument_spec) - connection = Connection(module._socket_path) - code, response = get_session(module, connection) - result = {'changed': False} - - if code == 200: - result['changed'] = True - payload = None - - if module.params['uid']: - payload = {'uid': module.params['uid']} - - if module.params['state'] == 'published': - code, response = connection.send_request('/web_api/publish', payload) - else: - code, response = connection.send_request('/web_api/discard', payload) - if code != 200: - module.fail_json(msg=response) - result['checkpoint_session'] = response - else: - module.fail_json(msg='Check Point device returned error {0} with message {1}'.format(code, response)) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/checkpoint_task_facts.py b/plugins/modules/network/check_point/checkpoint_task_facts.py deleted file mode 100644 index 632113a534..0000000000 --- a/plugins/modules/network/check_point/checkpoint_task_facts.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'network'} - - -DOCUMENTATION = ''' ---- -module: checkpoint_task_facts -short_description: Get task objects facts on Check Point over Web Services API -description: - - Get task objects facts on Check Point devices. - All operations are performed over Web Services API. -author: "Ansible by Red Hat (@rcarrillocruz)" -options: - task_id: - description: - - ID of the task object. - type: str - required: True -''' - -EXAMPLES = """ -- name: Get task facts - checkpoint_task_facts: - task_id: 2eec70e5-78a8-4bdb-9a76-cfb5601d0bcb -""" - -RETURN = """ -ansible_facts: - description: The checkpoint task facts. - returned: always. - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection - - -def get_task(module, connection): - task_id = module.params['task_id'] - - if task_id: - payload = {'task-id': task_id, - 'details-level': 'full'} - - code, response = connection.send_request('/web_api/show-task', payload) - else: - code, response = connection.send_request('/web_api/show-tasks', None) - - return code, response - - -def main(): - argument_spec = dict( - task_id=dict(type='str'), - ) - - module = AnsibleModule(argument_spec=argument_spec) - connection = Connection(module._socket_path) - code, response = get_task(module, connection) - if code == 200: - module.exit_json(ansible_facts=dict(checkpoint_tasks=response)) - else: - module.fail_json(msg='Checkpoint device returned error {0} with message {1}'.format(code, response)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/check_point/cp_publish.py b/plugins/modules/network/check_point/cp_publish.py deleted file mode 100644 index cece9ee0d4..0000000000 --- a/plugins/modules/network/check_point/cp_publish.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage Check Point Firewall (c) 2019 -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) - -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cp_publish -short_description: All the changes done by this user will be seen by all users only after publish is called. -description: - - All the changes done by this user will be seen by all users only after publish is called. - All operations are performed over Web Services API. -author: "Or Soffer (@chkp-orso)" -options: - uid: - description: - - Session unique identifier. Specify it to publish a different session than the one you currently use. - type: str -extends_documentation_fragment: -- check_point.mgmt.checkpoint_commands - -''' - -EXAMPLES = """ -- name: publish - cp_publish: -""" - -RETURN = """ -cp_publish: - description: The checkpoint publish output. - returned: always. - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.check_point.mgmt.plugins.module_utils.checkpoint import checkpoint_argument_spec_for_commands, api_command - - -def main(): - argument_spec = dict( - uid=dict(type='str') - ) - argument_spec.update(checkpoint_argument_spec_for_commands) - - module = AnsibleModule(argument_spec=argument_spec) - - command = "publish" - - result = api_command(module, command) - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_aaa_server.py b/plugins/modules/network/cloudengine/ce_aaa_server.py deleted file mode 100644 index c7da3fdeb3..0000000000 --- a/plugins/modules/network/cloudengine/ce_aaa_server.py +++ /dev/null @@ -1,2180 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: ce_aaa_server -short_description: Manages AAA server global configuration on HUAWEI CloudEngine switches. -description: - - Manages AAA server global configuration on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - type: str - choices: [ absent, present ] - default: present - authen_scheme_name: - description: - - Name of an authentication scheme. - The value is a string of 1 to 32 characters. - type: str - first_authen_mode: - description: - - Preferred authentication mode. - type: str - choices: ['invalid', 'local', 'hwtacacs', 'radius', 'none'] - default: local - author_scheme_name: - description: - - Name of an authorization scheme. - The value is a string of 1 to 32 characters. - type: str - first_author_mode: - description: - - Preferred authorization mode. - type: str - choices: ['invalid', 'local', 'hwtacacs', 'if-authenticated', 'none'] - default: local - acct_scheme_name: - description: - - Accounting scheme name. - The value is a string of 1 to 32 characters. - type: str - accounting_mode: - description: - - Accounting Mode. - type: str - choices: ['invalid', 'hwtacacs', 'radius', 'none'] - default: none - domain_name: - description: - - Name of a domain. - The value is a string of 1 to 64 characters. - type: str - radius_server_group: - description: - - RADIUS server group's name. - The value is a string of 1 to 32 case-insensitive characters. - type: str - hwtacas_template: - description: - - Name of a HWTACACS template. - The value is a string of 1 to 32 case-insensitive characters. - type: str - local_user_group: - description: - - Name of the user group where the user belongs. The user inherits all the rights of the user group. - The value is a string of 1 to 32 characters. - type: str -''' - -EXAMPLES = r''' - -- name: AAA server test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Radius authentication Server Basic settings" - ce_aaa_server: - state: present - authen_scheme_name: test1 - first_authen_mode: radius - radius_server_group: test2 - provider: "{{ cli }}" - - - name: "Undo radius authentication Server Basic settings" - ce_aaa_server: - state: absent - authen_scheme_name: test1 - first_authen_mode: radius - radius_server_group: test2 - provider: "{{ cli }}" - - - name: "Hwtacacs accounting Server Basic settings" - ce_aaa_server: - state: present - acct_scheme_name: test1 - accounting_mode: hwtacacs - hwtacas_template: test2 - provider: "{{ cli }}" - - - name: "Undo hwtacacs accounting Server Basic settings" - ce_aaa_server: - state: absent - acct_scheme_name: test1 - accounting_mode: hwtacacs - hwtacas_template: test2 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"accounting_mode": "hwtacacs", "acct_scheme_name": "test1", - "hwtacas_template": "test2", "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"accounting scheme": [["hwtacacs"], ["default"]], - "hwtacacs template": ["huawei"]} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"accounting scheme": [["hwtacacs", "test1"]], - "hwtacacs template": ["huawei", "test2"]} -updates: - description: command sent to the device - returned: always - type: list - sample: ["accounting-scheme test1", - "accounting-mode hwtacacs", - "hwtacacs server template test2", - "hwtacacs enable"] -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -SUCCESS = """success""" -FAILED = """failed""" - -INVALID_SCHEME_CHAR = [' ', '/', '\\', ':', '*', '?', '"', '|', '<', '>'] -INVALID_DOMAIN_CHAR = [' ', '*', '?', '"', '\''] -INVALID_GROUP_CHAR = ['/', '\\', ':', '*', '?', '"', '|', '<', '>'] - - -# get authentication scheme -CE_GET_AUTHENTICATION_SCHEME = """ - - - - - - - - - - - -""" - -# merge authentication scheme -CE_MERGE_AUTHENTICATION_SCHEME = """ - - - - - %s - %s - invalid - - - - -""" - -# create authentication scheme -CE_CREATE_AUTHENTICATION_SCHEME = """ - - - - - %s - %s - invalid - - - - -""" - -# delete authentication scheme -CE_DELETE_AUTHENTICATION_SCHEME = """ - - - - - %s - %s - invalid - - - - -""" - -# get authorization scheme -CE_GET_AUTHORIZATION_SCHEME = """ - - - - - - - - - - - -""" - -# merge authorization scheme -CE_MERGE_AUTHORIZATION_SCHEME = """ - - - - - %s - %s - invalid - - - - -""" - -# create authorization scheme -CE_CREATE_AUTHORIZATION_SCHEME = """ - - - - - %s - %s - invalid - - - - -""" - -# delete authorization scheme -CE_DELETE_AUTHORIZATION_SCHEME = """ - - - - - %s - %s - invalid - - - - -""" - -# get accounting scheme -CE_GET_ACCOUNTING_SCHEME = """ - - - - - - - - - - -""" - -# merge accounting scheme -CE_MERGE_ACCOUNTING_SCHEME = """ - - - - - %s - %s - - - - -""" - -# create accounting scheme -CE_CREATE_ACCOUNTING_SCHEME = """ - - - - - %s - %s - - - - -""" - -# delete accounting scheme -CE_DELETE_ACCOUNTING_SCHEME = """ - - - - - %s - %s - - - - -""" - -# get authentication domain -CE_GET_AUTHENTICATION_DOMAIN = """ - - - - - - - - - - -""" - -# merge authentication domain -CE_MERGE_AUTHENTICATION_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# create authentication domain -CE_CREATE_AUTHENTICATION_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# delete authentication domain -CE_DELETE_AUTHENTICATION_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# get authorization domain -CE_GET_AUTHORIZATION_DOMAIN = """ - - - - - - - - - - -""" - -# merge authorization domain -CE_MERGE_AUTHORIZATION_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# create authorization domain -CE_CREATE_AUTHORIZATION_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# delete authorization domain -CE_DELETE_AUTHORIZATION_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# get accounting domain -CE_GET_ACCOUNTING_DOMAIN = """ - - - - - - - - - - -""" - -# merge accounting domain -CE_MERGE_ACCOUNTING_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# create accounting domain -CE_CREATE_ACCOUNTING_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# delete accounting domain -CE_DELETE_ACCOUNTING_DOMAIN = """ - - - - - %s - %s - - - - -""" - -# get radius template -CE_GET_RADIUS_TEMPLATE = """ - - - - - - - - - - - -""" - -# merge radius template -CE_MERGE_RADIUS_TEMPLATE = """ - - - - - %s - 3 - 5 - - - - -""" - -# create radius template -CE_CREATE_RADIUS_TEMPLATE = """ - - - - - %s - 3 - 5 - - - - -""" - -# delete radius template -CE_DELETE_RADIUS_TEMPLATE = """ - - - - - %s - 3 - 5 - - - - -""" - -# get hwtacacs template -CE_GET_HWTACACS_TEMPLATE = """ - - - - - - - - - - - -""" - -# merge hwtacacs template -CE_MERGE_HWTACACS_TEMPLATE = """ - - - - - %s - true - 5 - - - - -""" - -# create hwtacacs template -CE_CREATE_HWTACACS_TEMPLATE = """ - - - - - %s - true - 5 - - - - -""" - -# delete hwtacacs template -CE_DELETE_HWTACACS_TEMPLATE = """ - - - - - %s - - - - -""" - -# get radius client -CE_GET_RADIUS_CLIENT = """ - - - - - - - - - -""" - -# merge radius client -CE_MERGE_RADIUS_CLIENT = """ - - - - %s - - - -""" - -# get hwtacacs global config -CE_GET_HWTACACS_GLOBAL_CFG = """ - - - - - - - - - -""" - -# merge hwtacacs global config -CE_MERGE_HWTACACS_GLOBAL_CFG = """ - - - - %s - - - -""" - -# get local user group -CE_GET_LOCAL_USER_GROUP = """ - - - - - - - - - -""" -# merge local user group -CE_MERGE_LOCAL_USER_GROUP = """ - - - - - %s - - - - -""" -# delete local user group -CE_DELETE_LOCAL_USER_GROUP = """ - - - - - %s - - - - -""" - - -class AaaServer(object): - """ Manages aaa configuration """ - - def netconf_get_config(self, **kwargs): - """ Get configure by netconf """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = get_nc_config(module, conf_str) - - return xml_str - - def netconf_set_config(self, **kwargs): - """ Set configure by netconf """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - recv_xml = set_nc_config(module, conf_str) - - return recv_xml - - def get_authentication_scheme(self, **kwargs): - """ Get scheme of authentication """ - - module = kwargs["module"] - conf_str = CE_GET_AUTHENTICATION_SCHEME - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*\s*' - r'(.*).*\s*' - r'(.*).*\s*', xml_str) - - if re_find: - return re_find - else: - return result - - def get_authentication_domain(self, **kwargs): - """ Get domain of authentication """ - - module = kwargs["module"] - conf_str = CE_GET_AUTHENTICATION_DOMAIN - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*\s*' - r'(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_authentication_scheme(self, **kwargs): - """ Merge scheme of authentication """ - - authen_scheme_name = kwargs["authen_scheme_name"] - first_authen_mode = kwargs["first_authen_mode"] - module = kwargs["module"] - conf_str = CE_MERGE_AUTHENTICATION_SCHEME % ( - authen_scheme_name, first_authen_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge authentication scheme failed.') - - cmds = [] - cmd = "authentication-scheme %s" % authen_scheme_name - cmds.append(cmd) - cmd = "authentication-mode %s" % first_authen_mode - cmds.append(cmd) - - return cmds - - def merge_authentication_domain(self, **kwargs): - """ Merge domain of authentication """ - - domain_name = kwargs["domain_name"] - authen_scheme_name = kwargs["authen_scheme_name"] - module = kwargs["module"] - conf_str = CE_MERGE_AUTHENTICATION_DOMAIN % ( - domain_name, authen_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge authentication domain failed.') - - cmds = [] - cmd = "domain %s" % domain_name - cmds.append(cmd) - cmd = "authentication-scheme %s" % authen_scheme_name - cmds.append(cmd) - - return cmds - - def create_authentication_scheme(self, **kwargs): - """ Create scheme of authentication """ - - authen_scheme_name = kwargs["authen_scheme_name"] - first_authen_mode = kwargs["first_authen_mode"] - module = kwargs["module"] - conf_str = CE_CREATE_AUTHENTICATION_SCHEME % ( - authen_scheme_name, first_authen_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Create authentication scheme failed.') - - cmds = [] - cmd = "authentication-scheme %s" % authen_scheme_name - cmds.append(cmd) - cmd = "authentication-mode %s" % first_authen_mode - cmds.append(cmd) - - return cmds - - def create_authentication_domain(self, **kwargs): - """ Create domain of authentication """ - - domain_name = kwargs["domain_name"] - authen_scheme_name = kwargs["authen_scheme_name"] - module = kwargs["module"] - conf_str = CE_CREATE_AUTHENTICATION_DOMAIN % ( - domain_name, authen_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Create authentication domain failed.') - - cmds = [] - cmd = "domain %s" % domain_name - cmds.append(cmd) - cmd = "authentication-scheme %s" % authen_scheme_name - cmds.append(cmd) - - return cmds - - def delete_authentication_scheme(self, **kwargs): - """ Delete scheme of authentication """ - - authen_scheme_name = kwargs["authen_scheme_name"] - first_authen_mode = kwargs["first_authen_mode"] - module = kwargs["module"] - - if authen_scheme_name == "default": - return SUCCESS - - conf_str = CE_DELETE_AUTHENTICATION_SCHEME % ( - authen_scheme_name, first_authen_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete authentication scheme failed.') - - cmds = [] - cmd = "undo authentication-scheme %s" % authen_scheme_name - cmds.append(cmd) - cmd = "authentication-mode none" - cmds.append(cmd) - - return cmds - - def delete_authentication_domain(self, **kwargs): - """ Delete domain of authentication """ - - domain_name = kwargs["domain_name"] - authen_scheme_name = kwargs["authen_scheme_name"] - module = kwargs["module"] - - if domain_name == "default": - return SUCCESS - - conf_str = CE_DELETE_AUTHENTICATION_DOMAIN % ( - domain_name, authen_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete authentication domain failed.') - - cmds = [] - cmd = "undo authentication-scheme" - cmds.append(cmd) - cmd = "undo domain %s" % domain_name - cmds.append(cmd) - - return cmds - - def get_authorization_scheme(self, **kwargs): - """ Get scheme of authorization """ - - module = kwargs["module"] - conf_str = CE_GET_AUTHORIZATION_SCHEME - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*\s*' - r'(.*).*\s*' - r'(.*).*\s*', xml_str) - - if re_find: - return re_find - else: - return result - - def get_authorization_domain(self, **kwargs): - """ Get domain of authorization """ - - module = kwargs["module"] - conf_str = CE_GET_AUTHORIZATION_DOMAIN - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*\s*' - r'(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_authorization_scheme(self, **kwargs): - """ Merge scheme of authorization """ - - author_scheme_name = kwargs["author_scheme_name"] - first_author_mode = kwargs["first_author_mode"] - module = kwargs["module"] - conf_str = CE_MERGE_AUTHORIZATION_SCHEME % ( - author_scheme_name, first_author_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge authorization scheme failed.') - - cmds = [] - cmd = "authorization-scheme %s" % author_scheme_name - cmds.append(cmd) - cmd = "authorization-mode %s" % first_author_mode - cmds.append(cmd) - - return cmds - - def merge_authorization_domain(self, **kwargs): - """ Merge domain of authorization """ - - domain_name = kwargs["domain_name"] - author_scheme_name = kwargs["author_scheme_name"] - module = kwargs["module"] - conf_str = CE_MERGE_AUTHORIZATION_DOMAIN % ( - domain_name, author_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge authorization domain failed.') - - cmds = [] - cmd = "domain %s" % domain_name - cmds.append(cmd) - cmd = "authorization-scheme %s" % author_scheme_name - cmds.append(cmd) - - return cmds - - def create_authorization_scheme(self, **kwargs): - """ Create scheme of authorization """ - - author_scheme_name = kwargs["author_scheme_name"] - first_author_mode = kwargs["first_author_mode"] - module = kwargs["module"] - conf_str = CE_CREATE_AUTHORIZATION_SCHEME % ( - author_scheme_name, first_author_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Create authorization scheme failed.') - - cmds = [] - cmd = "authorization-scheme %s" % author_scheme_name - cmds.append(cmd) - cmd = "authorization-mode %s" % first_author_mode - cmds.append(cmd) - - return cmds - - def create_authorization_domain(self, **kwargs): - """ Create domain of authorization """ - - domain_name = kwargs["domain_name"] - author_scheme_name = kwargs["author_scheme_name"] - module = kwargs["module"] - conf_str = CE_CREATE_AUTHORIZATION_DOMAIN % ( - domain_name, author_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Create authorization domain failed.') - - cmds = [] - cmd = "domain %s" % domain_name - cmds.append(cmd) - cmd = "authorization-scheme %s" % author_scheme_name - cmds.append(cmd) - - return cmds - - def delete_authorization_scheme(self, **kwargs): - """ Delete scheme of authorization """ - - author_scheme_name = kwargs["author_scheme_name"] - first_author_mode = kwargs["first_author_mode"] - module = kwargs["module"] - - if author_scheme_name == "default": - return SUCCESS - - conf_str = CE_DELETE_AUTHORIZATION_SCHEME % ( - author_scheme_name, first_author_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete authorization scheme failed.') - - cmds = [] - cmd = "undo authorization-scheme %s" % author_scheme_name - cmds.append(cmd) - cmd = "authorization-mode none" - cmds.append(cmd) - - return cmds - - def delete_authorization_domain(self, **kwargs): - """ Delete domain of authorization """ - - domain_name = kwargs["domain_name"] - author_scheme_name = kwargs["author_scheme_name"] - module = kwargs["module"] - - if domain_name == "default": - return SUCCESS - - conf_str = CE_DELETE_AUTHORIZATION_DOMAIN % ( - domain_name, author_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete authorization domain failed.') - - cmds = [] - cmd = "undo authorization-scheme" - cmds.append(cmd) - cmd = "undo domain %s" % domain_name - cmds.append(cmd) - - return cmds - - def get_accounting_scheme(self, **kwargs): - """ Get scheme of accounting """ - - module = kwargs["module"] - conf_str = CE_GET_ACCOUNTING_SCHEME - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall(r'.*(.*)\s*(.*)', xml_str) - if re_find: - return re_find - else: - return result - - def get_accounting_domain(self, **kwargs): - """ Get domain of accounting """ - - module = kwargs["module"] - conf_str = CE_GET_ACCOUNTING_DOMAIN - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*\s*' - r'(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_accounting_scheme(self, **kwargs): - """ Merge scheme of accounting """ - - acct_scheme_name = kwargs["acct_scheme_name"] - accounting_mode = kwargs["accounting_mode"] - module = kwargs["module"] - conf_str = CE_MERGE_ACCOUNTING_SCHEME % ( - acct_scheme_name, accounting_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge accounting scheme failed.') - - cmds = [] - cmd = "accounting-scheme %s" % acct_scheme_name - cmds.append(cmd) - cmd = "accounting-mode %s" % accounting_mode - cmds.append(cmd) - - return cmds - - def merge_accounting_domain(self, **kwargs): - """ Merge domain of accounting """ - - domain_name = kwargs["domain_name"] - acct_scheme_name = kwargs["acct_scheme_name"] - module = kwargs["module"] - conf_str = CE_MERGE_ACCOUNTING_DOMAIN % (domain_name, acct_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge accounting domain failed.') - - cmds = [] - cmd = "domain %s" % domain_name - cmds.append(cmd) - cmd = "accounting-scheme %s" % acct_scheme_name - cmds.append(cmd) - - return cmds - - def create_accounting_scheme(self, **kwargs): - """ Create scheme of accounting """ - - acct_scheme_name = kwargs["acct_scheme_name"] - accounting_mode = kwargs["accounting_mode"] - module = kwargs["module"] - conf_str = CE_CREATE_ACCOUNTING_SCHEME % ( - acct_scheme_name, accounting_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Create accounting scheme failed.') - - cmds = [] - cmd = "accounting-scheme %s" % acct_scheme_name - cmds.append(cmd) - cmd = "accounting-mode %s" % accounting_mode - cmds.append(cmd) - - return cmds - - def create_accounting_domain(self, **kwargs): - """ Create domain of accounting """ - - domain_name = kwargs["domain_name"] - acct_scheme_name = kwargs["acct_scheme_name"] - module = kwargs["module"] - conf_str = CE_CREATE_ACCOUNTING_DOMAIN % ( - domain_name, acct_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Create accounting domain failed.') - - cmds = [] - cmd = "domain %s" % domain_name - cmds.append(cmd) - cmd = "accounting-scheme %s" % acct_scheme_name - cmds.append(cmd) - - return cmds - - def delete_accounting_scheme(self, **kwargs): - """ Delete scheme of accounting """ - - acct_scheme_name = kwargs["acct_scheme_name"] - accounting_mode = kwargs["accounting_mode"] - module = kwargs["module"] - - if acct_scheme_name == "default": - return SUCCESS - - conf_str = CE_DELETE_ACCOUNTING_SCHEME % ( - acct_scheme_name, accounting_mode) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete accounting scheme failed.') - - cmds = [] - cmd = "undo accounting-scheme %s" % acct_scheme_name - cmds.append(cmd) - cmd = "accounting-mode none" - cmds.append(cmd) - - return cmds - - def delete_accounting_domain(self, **kwargs): - """ Delete domain of accounting """ - - domain_name = kwargs["domain_name"] - acct_scheme_name = kwargs["acct_scheme_name"] - module = kwargs["module"] - - if domain_name == "default": - return SUCCESS - - conf_str = CE_DELETE_ACCOUNTING_DOMAIN % ( - domain_name, acct_scheme_name) - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete accounting domain failed.') - - cmds = [] - cmd = "undo domain %s" % domain_name - cmds.append(cmd) - cmd = "undo accounting-scheme" - cmds.append(cmd) - - return cmds - - def get_radius_template(self, **kwargs): - """ Get radius template """ - - module = kwargs["module"] - conf_str = CE_GET_RADIUS_TEMPLATE - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_radius_template(self, **kwargs): - """ Merge radius template """ - - radius_server_group = kwargs["radius_server_group"] - module = kwargs["module"] - conf_str = CE_MERGE_RADIUS_TEMPLATE % radius_server_group - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge radius template failed.') - - cmds = [] - cmd = "radius server group %s" % radius_server_group - cmds.append(cmd) - - return cmds - - def create_radius_template(self, **kwargs): - """ Create radius template """ - - radius_server_group = kwargs["radius_server_group"] - module = kwargs["module"] - conf_str = CE_CREATE_RADIUS_TEMPLATE % radius_server_group - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Create radius template failed.') - - cmds = [] - cmd = "radius server group %s" % radius_server_group - cmds.append(cmd) - - return cmds - - def delete_radius_template(self, **kwargs): - """ Delete radius template """ - - radius_server_group = kwargs["radius_server_group"] - module = kwargs["module"] - conf_str = CE_DELETE_RADIUS_TEMPLATE % radius_server_group - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete radius template failed.') - - cmds = [] - cmd = "undo radius server group %s" % radius_server_group - cmds.append(cmd) - - return cmds - - def get_radius_client(self, **kwargs): - """ Get radius client """ - - module = kwargs["module"] - conf_str = CE_GET_RADIUS_CLIENT - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_radius_client(self, **kwargs): - """ Merge radius client """ - - enable = kwargs["isEnable"] - module = kwargs["module"] - conf_str = CE_MERGE_RADIUS_CLIENT % enable - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge radius client failed.') - - cmds = [] - if enable == "true": - cmd = "radius enable" - else: - cmd = "undo radius enable" - cmds.append(cmd) - - return cmds - - def get_hwtacacs_template(self, **kwargs): - """ Get hwtacacs template """ - - module = kwargs["module"] - conf_str = CE_GET_HWTACACS_TEMPLATE - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_hwtacacs_template(self, **kwargs): - """ Merge hwtacacs template """ - - hwtacas_template = kwargs["hwtacas_template"] - module = kwargs["module"] - conf_str = CE_MERGE_HWTACACS_TEMPLATE % hwtacas_template - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge hwtacacs template failed.') - - cmds = [] - cmd = "hwtacacs server template %s" % hwtacas_template - cmds.append(cmd) - - return cmds - - def create_hwtacacs_template(self, **kwargs): - """ Create hwtacacs template """ - - hwtacas_template = kwargs["hwtacas_template"] - module = kwargs["module"] - conf_str = CE_CREATE_HWTACACS_TEMPLATE % hwtacas_template - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Create hwtacacs template failed.') - - cmds = [] - cmd = "hwtacacs server template %s" % hwtacas_template - cmds.append(cmd) - - return cmds - - def delete_hwtacacs_template(self, **kwargs): - """ Delete hwtacacs template """ - - hwtacas_template = kwargs["hwtacas_template"] - module = kwargs["module"] - conf_str = CE_DELETE_HWTACACS_TEMPLATE % hwtacas_template - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete hwtacacs template failed.') - - cmds = [] - cmd = "undo hwtacacs server template %s" % hwtacas_template - cmds.append(cmd) - - return cmds - - def get_hwtacacs_global_cfg(self, **kwargs): - """ Get hwtacacs global configure """ - - module = kwargs["module"] - conf_str = CE_GET_HWTACACS_GLOBAL_CFG - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_hwtacacs_global_cfg(self, **kwargs): - """ Merge hwtacacs global configure """ - - enable = kwargs["isEnable"] - module = kwargs["module"] - conf_str = CE_MERGE_HWTACACS_GLOBAL_CFG % enable - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge hwtacacs global config failed.') - - cmds = [] - - if enable == "true": - cmd = "hwtacacs enable" - else: - cmd = "undo hwtacacs enable" - cmds.append(cmd) - - return cmds - - def get_local_user_group(self, **kwargs): - """ Get local user group """ - - module = kwargs["module"] - conf_str = CE_GET_LOCAL_USER_GROUP - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_local_user_group(self, **kwargs): - """ Merge local user group """ - - local_user_group = kwargs["local_user_group"] - module = kwargs["module"] - conf_str = CE_MERGE_LOCAL_USER_GROUP % local_user_group - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Merge local user group failed.') - - cmds = [] - cmd = "user-group %s" % local_user_group - cmds.append(cmd) - - return cmds - - def delete_local_user_group(self, **kwargs): - """ Delete local user group """ - - local_user_group = kwargs["local_user_group"] - module = kwargs["module"] - conf_str = CE_DELETE_LOCAL_USER_GROUP % local_user_group - - xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in xml: - module.fail_json(msg='Error: Delete local user group failed.') - - cmds = [] - cmd = "undo user-group %s" % local_user_group - cmds.append(cmd) - - return cmds - - -def check_name(**kwargs): - """ Check invalid name """ - - module = kwargs["module"] - name = kwargs["name"] - invalid_char = kwargs["invalid_char"] - - for item in invalid_char: - if item in name: - module.fail_json( - msg='Error: invalid char %s is in the name %s.' % (item, name)) - - -def check_module_argument(**kwargs): - """ Check module argument """ - - module = kwargs["module"] - - authen_scheme_name = module.params['authen_scheme_name'] - author_scheme_name = module.params['author_scheme_name'] - acct_scheme_name = module.params['acct_scheme_name'] - domain_name = module.params['domain_name'] - radius_server_group = module.params['radius_server_group'] - hwtacas_template = module.params['hwtacas_template'] - local_user_group = module.params['local_user_group'] - - if authen_scheme_name: - if len(authen_scheme_name) > 32: - module.fail_json( - msg='Error: authen_scheme_name %s ' - 'is large than 32.' % authen_scheme_name) - check_name(module=module, name=authen_scheme_name, - invalid_char=INVALID_SCHEME_CHAR) - - if author_scheme_name: - if len(author_scheme_name) > 32: - module.fail_json( - msg='Error: author_scheme_name %s ' - 'is large than 32.' % author_scheme_name) - check_name(module=module, name=author_scheme_name, - invalid_char=INVALID_SCHEME_CHAR) - - if acct_scheme_name: - if len(acct_scheme_name) > 32: - module.fail_json( - msg='Error: acct_scheme_name %s ' - 'is large than 32.' % acct_scheme_name) - check_name(module=module, name=acct_scheme_name, - invalid_char=INVALID_SCHEME_CHAR) - - if domain_name: - if len(domain_name) > 64: - module.fail_json( - msg='Error: domain_name %s ' - 'is large than 64.' % domain_name) - check_name(module=module, name=domain_name, - invalid_char=INVALID_DOMAIN_CHAR) - if domain_name == "-" or domain_name == "--": - module.fail_json(msg='domain_name %s ' - 'is invalid.' % domain_name) - - if radius_server_group and len(radius_server_group) > 32: - module.fail_json(msg='Error: radius_server_group %s ' - 'is large than 32.' % radius_server_group) - - if hwtacas_template and len(hwtacas_template) > 32: - module.fail_json( - msg='Error: hwtacas_template %s ' - 'is large than 32.' % hwtacas_template) - - if local_user_group: - if len(local_user_group) > 32: - module.fail_json( - msg='Error: local_user_group %s ' - 'is large than 32.' % local_user_group) - check_name(module=module, name=local_user_group, invalid_char=INVALID_GROUP_CHAR) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - authen_scheme_name=dict(type='str'), - first_authen_mode=dict(default='local', choices=['invalid', 'local', 'hwtacacs', 'radius', 'none']), - author_scheme_name=dict(type='str'), - first_author_mode=dict(default='local', choices=['invalid', 'local', 'hwtacacs', 'if-authenticated', 'none']), - acct_scheme_name=dict(type='str'), - accounting_mode=dict(default='none', choices=['invalid', 'hwtacacs', 'radius', 'none']), - domain_name=dict(type='str'), - radius_server_group=dict(type='str'), - hwtacas_template=dict(type='str'), - local_user_group=dict(type='str') - ) - - argument_spec.update(ce_argument_spec) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - check_module_argument(module=module) - - changed = False - proposed = dict() - existing = dict() - end_state = dict() - updates = [] - - state = module.params['state'] - authen_scheme_name = module.params['authen_scheme_name'] - first_authen_mode = module.params['first_authen_mode'] - author_scheme_name = module.params['author_scheme_name'] - first_author_mode = module.params['first_author_mode'] - acct_scheme_name = module.params['acct_scheme_name'] - accounting_mode = module.params['accounting_mode'] - domain_name = module.params['domain_name'] - radius_server_group = module.params['radius_server_group'] - hwtacas_template = module.params['hwtacas_template'] - local_user_group = module.params['local_user_group'] - - ce_aaa_server = AaaServer() - - if not ce_aaa_server: - module.fail_json(msg='Error: init module failed.') - - # get proposed - proposed["state"] = state - if authen_scheme_name: - proposed["authen_scheme_name"] = authen_scheme_name - if first_authen_mode: - proposed["first_authen_mode"] = first_authen_mode - if author_scheme_name: - proposed["author_scheme_name"] = author_scheme_name - if first_author_mode: - proposed["first_author_mode"] = first_author_mode - if acct_scheme_name: - proposed["acct_scheme_name"] = acct_scheme_name - if accounting_mode: - proposed["accounting_mode"] = accounting_mode - if domain_name: - proposed["domain_name"] = domain_name - if radius_server_group: - proposed["radius_server_group"] = radius_server_group - if hwtacas_template: - proposed["hwtacas_template"] = hwtacas_template - if local_user_group: - proposed["local_user_group"] = local_user_group - - # authentication - if authen_scheme_name: - - scheme_exist = ce_aaa_server.get_authentication_scheme(module=module) - scheme_new = (authen_scheme_name.lower(), first_authen_mode.lower(), "invalid") - - existing["authentication scheme"] = scheme_exist - - if state == "present": - # present authentication scheme - if len(scheme_exist) == 0: - cmd = ce_aaa_server.create_authentication_scheme( - module=module, - authen_scheme_name=authen_scheme_name, - first_authen_mode=first_authen_mode) - - updates.append(cmd) - changed = True - - elif scheme_new not in scheme_exist: - cmd = ce_aaa_server.merge_authentication_scheme( - module=module, - authen_scheme_name=authen_scheme_name, - first_authen_mode=first_authen_mode) - updates.append(cmd) - changed = True - - # present authentication domain - if domain_name: - domain_exist = ce_aaa_server.get_authentication_domain( - module=module) - domain_new = (domain_name.lower(), authen_scheme_name.lower()) - - if len(domain_exist) == 0: - cmd = ce_aaa_server.create_authentication_domain( - module=module, - domain_name=domain_name, - authen_scheme_name=authen_scheme_name) - updates.append(cmd) - changed = True - - elif domain_new not in domain_exist: - cmd = ce_aaa_server.merge_authentication_domain( - module=module, - domain_name=domain_name, - authen_scheme_name=authen_scheme_name) - updates.append(cmd) - changed = True - - else: - # absent authentication scheme - if not domain_name: - if len(scheme_exist) == 0: - pass - elif scheme_new not in scheme_exist: - pass - else: - cmd = ce_aaa_server.delete_authentication_scheme( - module=module, - authen_scheme_name=authen_scheme_name, - first_authen_mode=first_authen_mode) - updates.append(cmd) - changed = True - - # absent authentication domain - else: - domain_exist = ce_aaa_server.get_authentication_domain( - module=module) - domain_new = (domain_name.lower(), authen_scheme_name.lower()) - - if len(domain_exist) == 0: - pass - elif domain_new not in domain_exist: - pass - else: - cmd = ce_aaa_server.delete_authentication_domain( - module=module, - domain_name=domain_name, - authen_scheme_name=authen_scheme_name) - updates.append(cmd) - changed = True - - scheme_end = ce_aaa_server.get_authentication_scheme(module=module) - end_state["authentication scheme"] = scheme_end - - # authorization - if author_scheme_name: - - scheme_exist = ce_aaa_server.get_authorization_scheme(module=module) - scheme_new = (author_scheme_name.lower(), first_author_mode.lower(), "invalid") - - existing["authorization scheme"] = scheme_exist - - if state == "present": - # present authorization scheme - if len(scheme_exist) == 0: - cmd = ce_aaa_server.create_authorization_scheme( - module=module, - author_scheme_name=author_scheme_name, - first_author_mode=first_author_mode) - updates.append(cmd) - changed = True - elif scheme_new not in scheme_exist: - cmd = ce_aaa_server.merge_authorization_scheme( - module=module, - author_scheme_name=author_scheme_name, - first_author_mode=first_author_mode) - updates.append(cmd) - changed = True - - # present authorization domain - if domain_name: - domain_exist = ce_aaa_server.get_authorization_domain( - module=module) - domain_new = (domain_name.lower(), author_scheme_name.lower()) - - if len(domain_exist) == 0: - cmd = ce_aaa_server.create_authorization_domain( - module=module, - domain_name=domain_name, - author_scheme_name=author_scheme_name) - updates.append(cmd) - changed = True - elif domain_new not in domain_exist: - cmd = ce_aaa_server.merge_authorization_domain( - module=module, - domain_name=domain_name, - author_scheme_name=author_scheme_name) - updates.append(cmd) - changed = True - - else: - # absent authorization scheme - if not domain_name: - if len(scheme_exist) == 0: - pass - elif scheme_new not in scheme_exist: - pass - else: - cmd = ce_aaa_server.delete_authorization_scheme( - module=module, - author_scheme_name=author_scheme_name, - first_author_mode=first_author_mode) - updates.append(cmd) - changed = True - - # absent authorization domain - else: - domain_exist = ce_aaa_server.get_authorization_domain( - module=module) - domain_new = (domain_name.lower(), author_scheme_name.lower()) - - if len(domain_exist) == 0: - pass - elif domain_new not in domain_exist: - pass - else: - cmd = ce_aaa_server.delete_authorization_domain( - module=module, - domain_name=domain_name, - author_scheme_name=author_scheme_name) - updates.append(cmd) - changed = True - - scheme_end = ce_aaa_server.get_authorization_scheme(module=module) - end_state["authorization scheme"] = scheme_end - - # accounting - if acct_scheme_name: - - scheme_exist = ce_aaa_server.get_accounting_scheme(module=module) - scheme_new = (acct_scheme_name.lower(), accounting_mode.lower()) - - existing["accounting scheme"] = scheme_exist - - if state == "present": - # present accounting scheme - if len(scheme_exist) == 0: - cmd = ce_aaa_server.create_accounting_scheme( - module=module, - acct_scheme_name=acct_scheme_name, - accounting_mode=accounting_mode) - updates.append(cmd) - changed = True - elif scheme_new not in scheme_exist: - cmd = ce_aaa_server.merge_accounting_scheme( - module=module, - acct_scheme_name=acct_scheme_name, - accounting_mode=accounting_mode) - updates.append(cmd) - changed = True - - # present accounting domain - if domain_name: - domain_exist = ce_aaa_server.get_accounting_domain( - module=module) - domain_new = (domain_name.lower(), acct_scheme_name.lower()) - - if len(domain_exist) == 0: - cmd = ce_aaa_server.create_accounting_domain( - module=module, - domain_name=domain_name, - acct_scheme_name=acct_scheme_name) - updates.append(cmd) - changed = True - elif domain_new not in domain_exist: - cmd = ce_aaa_server.merge_accounting_domain( - module=module, - domain_name=domain_name, - acct_scheme_name=acct_scheme_name) - updates.append(cmd) - changed = True - - else: - # absent accounting scheme - if not domain_name: - if len(scheme_exist) == 0: - pass - elif scheme_new not in scheme_exist: - pass - else: - cmd = ce_aaa_server.delete_accounting_scheme( - module=module, - acct_scheme_name=acct_scheme_name, - accounting_mode=accounting_mode) - updates.append(cmd) - changed = True - - # absent accounting domain - else: - domain_exist = ce_aaa_server.get_accounting_domain( - module=module) - domain_new = (domain_name.lower(), acct_scheme_name.lower()) - if len(domain_exist) == 0: - pass - elif domain_new not in domain_exist: - pass - else: - cmd = ce_aaa_server.delete_accounting_domain( - module=module, - domain_name=domain_name, - acct_scheme_name=acct_scheme_name) - updates.append(cmd) - changed = True - - scheme_end = ce_aaa_server.get_accounting_scheme(module=module) - end_state["accounting scheme"] = scheme_end - - # radius group name - if (authen_scheme_name and first_authen_mode.lower() == "radius") \ - or (acct_scheme_name and accounting_mode.lower() == "radius"): - - if not radius_server_group: - module.fail_json(msg='please input radius_server_group when use radius.') - - rds_template_exist = ce_aaa_server.get_radius_template(module=module) - rds_template_new = (radius_server_group) - - rds_enable_exist = ce_aaa_server.get_radius_client(module=module) - - existing["radius template"] = rds_template_exist - existing["radius enable"] = rds_enable_exist - - if state == "present": - # present radius group name - if len(rds_template_exist) == 0: - cmd = ce_aaa_server.create_radius_template( - module=module, radius_server_group=radius_server_group) - updates.append(cmd) - changed = True - elif rds_template_new not in rds_template_exist: - cmd = ce_aaa_server.merge_radius_template( - module=module, radius_server_group=radius_server_group) - updates.append(cmd) - changed = True - - rds_enable_new = ("true") - if rds_enable_new not in rds_enable_exist: - cmd = ce_aaa_server.merge_radius_client( - module=module, isEnable="true") - updates.append(cmd) - changed = True - - else: - # absent radius group name - if len(rds_template_exist) == 0: - pass - elif rds_template_new not in rds_template_exist: - pass - else: - cmd = ce_aaa_server.delete_radius_template( - module=module, radius_server_group=radius_server_group) - updates.append(cmd) - changed = True - - rds_enable_new = ("false") - if rds_enable_new not in rds_enable_exist: - cmd = ce_aaa_server.merge_radius_client( - module=module, isEnable="false") - updates.append(cmd) - changed = True - else: - pass - - rds_template_end = ce_aaa_server.get_radius_template(module=module) - end_state["radius template"] = rds_template_end - - rds_enable_end = ce_aaa_server.get_radius_client(module=module) - end_state["radius enable"] = rds_enable_end - - tmp_scheme = author_scheme_name - - # hwtacas template - if (authen_scheme_name and first_authen_mode.lower() == "hwtacacs") \ - or (tmp_scheme and first_author_mode.lower() == "hwtacacs") \ - or (acct_scheme_name and accounting_mode.lower() == "hwtacacs"): - - if not hwtacas_template: - module.fail_json( - msg='please input hwtacas_template when use hwtacas.') - - hwtacacs_exist = ce_aaa_server.get_hwtacacs_template(module=module) - hwtacacs_new = (hwtacas_template) - - hwtacacs_enbale_exist = ce_aaa_server.get_hwtacacs_global_cfg( - module=module) - - existing["hwtacacs template"] = hwtacacs_exist - existing["hwtacacs enable"] = hwtacacs_enbale_exist - - if state == "present": - # present hwtacas template - if len(hwtacacs_exist) == 0: - cmd = ce_aaa_server.create_hwtacacs_template( - module=module, hwtacas_template=hwtacas_template) - updates.append(cmd) - changed = True - elif hwtacacs_new not in hwtacacs_exist: - cmd = ce_aaa_server.merge_hwtacacs_template( - module=module, hwtacas_template=hwtacas_template) - updates.append(cmd) - changed = True - - hwtacacs_enbale_new = ("true") - if hwtacacs_enbale_new not in hwtacacs_enbale_exist: - cmd = ce_aaa_server.merge_hwtacacs_global_cfg( - module=module, isEnable="true") - updates.append(cmd) - changed = True - - else: - # absent hwtacas template - if len(hwtacacs_exist) == 0: - pass - elif hwtacacs_new not in hwtacacs_exist: - pass - else: - cmd = ce_aaa_server.delete_hwtacacs_template( - module=module, hwtacas_template=hwtacas_template) - updates.append(cmd) - changed = True - - hwtacacs_enbale_new = ("false") - if hwtacacs_enbale_new not in hwtacacs_enbale_exist: - cmd = ce_aaa_server.merge_hwtacacs_global_cfg( - module=module, isEnable="false") - updates.append(cmd) - changed = True - else: - pass - - hwtacacs_end = ce_aaa_server.get_hwtacacs_template(module=module) - end_state["hwtacacs template"] = hwtacacs_end - - hwtacacs_enable_end = ce_aaa_server.get_hwtacacs_global_cfg( - module=module) - end_state["hwtacacs enable"] = hwtacacs_enable_end - - # local user group - if local_user_group: - - user_group_exist = ce_aaa_server.get_local_user_group(module=module) - user_group_new = (local_user_group) - - existing["local user group"] = user_group_exist - - if state == "present": - # present local user group - if len(user_group_exist) == 0: - cmd = ce_aaa_server.merge_local_user_group( - module=module, local_user_group=local_user_group) - updates.append(cmd) - changed = True - elif user_group_new not in user_group_exist: - cmd = ce_aaa_server.merge_local_user_group( - module=module, local_user_group=local_user_group) - updates.append(cmd) - changed = True - - else: - # absent local user group - if len(user_group_exist) == 0: - pass - elif user_group_new not in user_group_exist: - pass - else: - cmd = ce_aaa_server.delete_local_user_group( - module=module, local_user_group=local_user_group) - updates.append(cmd) - changed = True - - user_group_end = ce_aaa_server.get_local_user_group(module=module) - end_state["local user group"] = user_group_end - - results = dict() - results['proposed'] = proposed - results['existing'] = existing - results['changed'] = changed - results['end_state'] = end_state - results['updates'] = updates - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_aaa_server_host.py b/plugins/modules/network/cloudengine/ce_aaa_server_host.py deleted file mode 100644 index 89777c75fd..0000000000 --- a/plugins/modules/network/cloudengine/ce_aaa_server_host.py +++ /dev/null @@ -1,2640 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_aaa_server_host -short_description: Manages AAA server host configuration on HUAWEI CloudEngine switches. -description: - - Manages AAA server host configuration on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present', 'absent'] - local_user_name: - description: - - Name of a local user. - The value is a string of 1 to 253 characters. - local_password: - description: - - Login password of a user. The password can contain letters, numbers, and special characters. - The value is a string of 1 to 255 characters. - local_service_type: - description: - - The type of local user login through, such as ftp ssh snmp telnet. - local_ftp_dir: - description: - - FTP user directory. - The value is a string of 1 to 255 characters. - local_user_level: - description: - - Login level of a local user. - The value is an integer ranging from 0 to 15. - local_user_group: - description: - - Name of the user group where the user belongs. The user inherits all the rights of the user group. - The value is a string of 1 to 32 characters. - radius_group_name: - description: - - RADIUS server group's name. - The value is a string of 1 to 32 case-insensitive characters. - radius_server_type: - description: - - Type of Radius Server. - choices: ['Authentication', 'Accounting'] - radius_server_ip: - description: - - IPv4 address of configured server. - The value is a string of 0 to 255 characters, in dotted decimal notation. - radius_server_ipv6: - description: - - IPv6 address of configured server. - The total length is 128 bits. - radius_server_port: - description: - - Configured server port for a particular server. - The value is an integer ranging from 1 to 65535. - radius_server_mode: - description: - - Configured primary or secondary server for a particular server. - choices: ['Secondary-server', 'Primary-server'] - radius_vpn_name: - description: - - Set VPN instance. - The value is a string of 1 to 31 case-sensitive characters. - radius_server_name: - description: - - Hostname of configured server. - The value is a string of 0 to 255 case-sensitive characters. - hwtacacs_template: - description: - - Name of a HWTACACS template. - The value is a string of 1 to 32 case-insensitive characters. - hwtacacs_server_ip: - description: - - Server IPv4 address. Must be a valid unicast IP address. - The value is a string of 0 to 255 characters, in dotted decimal notation. - hwtacacs_server_ipv6: - description: - - Server IPv6 address. Must be a valid unicast IP address. - The total length is 128 bits. - hwtacacs_server_type: - description: - - Hwtacacs server type. - choices: ['Authentication', 'Authorization', 'Accounting', 'Common'] - hwtacacs_is_secondary_server: - description: - - Whether the server is secondary. - type: bool - default: 'no' - hwtacacs_vpn_name: - description: - - VPN instance name. - hwtacacs_is_public_net: - description: - - Set the public-net. - type: bool - default: 'no' - hwtacacs_server_host_name: - description: - - Hwtacacs server host name. -''' - -EXAMPLES = ''' - -- name: AAA server host test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config local user when use local scheme" - ce_aaa_server_host: - state: present - local_user_name: user1 - local_password: 123456 - provider: "{{ cli }}" - - - name: "Undo local user when use local scheme" - ce_aaa_server_host: - state: absent - local_user_name: user1 - local_password: 123456 - provider: "{{ cli }}" - - - name: "Config radius server ip" - ce_aaa_server_host: - state: present - radius_group_name: group1 - radius_server_type: Authentication - radius_server_ip: 10.1.10.1 - radius_server_port: 2000 - radius_server_mode: Primary-server - radius_vpn_name: _public_ - provider: "{{ cli }}" - - - name: "Undo radius server ip" - ce_aaa_server_host: - state: absent - radius_group_name: group1 - radius_server_type: Authentication - radius_server_ip: 10.1.10.1 - radius_server_port: 2000 - radius_server_mode: Primary-server - radius_vpn_name: _public_ - provider: "{{ cli }}" - - - name: "Config hwtacacs server ip" - ce_aaa_server_host: - state: present - hwtacacs_template: template - hwtacacs_server_ip: 10.10.10.10 - hwtacacs_server_type: Authorization - hwtacacs_vpn_name: _public_ - provider: "{{ cli }}" - - - name: "Undo hwtacacs server ip" - ce_aaa_server_host: - state: absent - hwtacacs_template: template - hwtacacs_server_ip: 10.10.10.10 - hwtacacs_server_type: Authorization - hwtacacs_vpn_name: _public_ - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"hwtacacs_is_public_net": "false", - "hwtacacs_is_secondary_server": "false", - "hwtacacs_server_ip": "10.135.182.157", - "hwtacacs_server_type": "Authorization", - "hwtacacs_template": "wdz", - "hwtacacs_vpn_name": "_public_", - "local_password": "******", - "state": "present"} -existing: - description: k/v pairs of existing aaa server host - returned: always - type: dict - sample: {"radius server ipv4": []} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"radius server ipv4": [ - [ - "10.1.10.1", - "Authentication", - "2000", - "Primary-server", - "_public_" - ] - ]} -updates: - description: command sent to the device - returned: always - type: list - sample: ["hwtacacs server template test", - "hwtacacs server authorization 10.135.182.157 vpn-instance test_vpn public-net"] -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - -SUCCESS = """success""" -FAILED = """failed""" - -INVALID_USER_NAME_CHAR = [' ', '/', '\\', - ':', '*', '?', '"', '\'', '<', '>', '%'] - -# get local user name -CE_GET_LOCAL_USER_INFO_HEADER = """ - - - - - - - -""" -CE_GET_LOCAL_USER_INFO_TAIL = """ - - - - - -""" - -# merge local user name -CE_MERGE_LOCAL_USER_INFO_HEADER = """ - - - - - - %s -""" -CE_MERGE_LOCAL_USER_INFO_TAIL = """ - - - - - -""" - -# delete local user name -CE_DELETE_LOCAL_USER_INFO_HEADER = """ - - - - - - %s -""" -CE_DELETE_LOCAL_USER_INFO_TAIL = """ - - - - - -""" - -# get radius server config ipv4 -CE_GET_RADIUS_SERVER_CFG_IPV4 = """ - - - - - %s - - - - - - - - - - - - - -""" - -# merge radius server config ipv4 -CE_MERGE_RADIUS_SERVER_CFG_IPV4 = """ - - - - - %s - - - %s - %s - %s - %s - %s - - - - - - -""" - -# delete radius server config ipv4 -CE_DELETE_RADIUS_SERVER_CFG_IPV4 = """ - - - - - %s - - - %s - %s - %s - %s - %s - - - - - - -""" - -# get radius server config ipv6 -CE_GET_RADIUS_SERVER_CFG_IPV6 = """ - - - - - %s - - - - - - - - - - - - -""" - -# merge radius server config ipv6 -CE_MERGE_RADIUS_SERVER_CFG_IPV6 = """ - - - - - %s - - - %s - %s - %s - %s - - - - - - -""" - -# delete radius server config ipv6 -CE_DELETE_RADIUS_SERVER_CFG_IPV6 = """ - - - - - %s - - - %s - %s - %s - %s - - - - - - -""" - -# get radius server name -CE_GET_RADIUS_SERVER_NAME = """ - - - - - %s - - - - - - - - - - - - - -""" - -# merge radius server name -CE_MERGE_RADIUS_SERVER_NAME = """ - - - - - %s - - - %s - %s - %s - %s - %s - - - - - - -""" - -# delete radius server name -CE_DELETE_RADIUS_SERVER_NAME = """ - - - - - %s - - - %s - %s - %s - %s - %s - - - - - - -""" - -# get hwtacacs server config ipv4 -CE_GET_HWTACACS_SERVER_CFG_IPV4 = """ - - - - - %s - - - - - - - - - - - - - -""" - -# merge hwtacacs server config ipv4 -CE_MERGE_HWTACACS_SERVER_CFG_IPV4 = """ - - - - - %s - - - %s - %s - %s - %s - %s - - - - - - -""" - -# delete hwtacacs server config ipv4 -CE_DELETE_HWTACACS_SERVER_CFG_IPV4 = """ - - - - - %s - - - %s - %s - %s - %s - %s - - - - - - -""" - -# get hwtacacs server config ipv6 -CE_GET_HWTACACS_SERVER_CFG_IPV6 = """ - - - - - %s - - - - - - - - - - - - -""" - -# merge hwtacacs server config ipv6 -CE_MERGE_HWTACACS_SERVER_CFG_IPV6 = """ - - - - - %s - - - %s - %s - %s - %s - - - - - - -""" - -# delete hwtacacs server config ipv6 -CE_DELETE_HWTACACS_SERVER_CFG_IPV6 = """ - - - - - %s - - - %s - %s - %s - %s - - - - - - -""" - -# get hwtacacs host server config -CE_GET_HWTACACS_HOST_SERVER_CFG = """ - - - - - %s - - - - - - - - - - - - - -""" - -# merge hwtacacs host server config -CE_MERGE_HWTACACS_HOST_SERVER_CFG = """ - - - - - %s - - - %s - %s - %s - %s - %s - - - - - - -""" - -# delete hwtacacs host server config -CE_DELETE_HWTACACS_HOST_SERVER_CFG = """ - - - - - %s - - - %s - %s - %s - %s - %s - - - - - - -""" - - -class AaaServerHost(object): - """ Manages aaa server host configuration """ - - def netconf_get_config(self, **kwargs): - """ Get configure by netconf """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = get_nc_config(module, conf_str) - - return xml_str - - def netconf_set_config(self, **kwargs): - """ Set configure by netconf """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - recv_xml = set_nc_config(module, conf_str) - - return recv_xml - - def get_local_user_info(self, **kwargs): - """ Get local user information """ - - module = kwargs["module"] - local_user_name = module.params['local_user_name'] - local_service_type = module.params['local_service_type'] - local_ftp_dir = module.params['local_ftp_dir'] - local_user_level = module.params['local_user_level'] - local_user_group = module.params['local_user_group'] - state = module.params['state'] - - result = dict() - result["local_user_info"] = [] - need_cfg = False - - conf_str = CE_GET_LOCAL_USER_INFO_HEADER - - if local_service_type: - if local_service_type == "none": - conf_str += "" - conf_str += "" - conf_str += "" - conf_str += "" - conf_str += "" - conf_str += "" - elif local_service_type == "dot1x": - conf_str += "" - else: - option = local_service_type.split(" ") - for tmp in option: - if tmp == "dot1x": - module.fail_json( - msg='Error: Do not input dot1x with other service type.') - elif tmp == "none": - module.fail_json( - msg='Error: Do not input none with other service type.') - elif tmp == "ftp": - conf_str += "" - elif tmp == "snmp": - conf_str += "" - elif tmp == "ssh": - conf_str += "" - elif tmp == "telnet": - conf_str += "" - elif tmp == "terminal": - conf_str += "" - else: - module.fail_json( - msg='Error: Do not support the type [%s].' % tmp) - - if local_ftp_dir: - conf_str += "" - - if local_user_level: - conf_str += "" - - if local_user_group: - conf_str += "" - - conf_str += CE_GET_LOCAL_USER_INFO_TAIL - - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - local_user_info = root.findall("aaa/lam/users/user") - if local_user_info: - for tmp in local_user_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["userName", "password", "userLevel", "ftpDir", "userGroupName", - "serviceTerminal", "serviceTelnet", "serviceFtp", "serviceSsh", - "serviceSnmp", "serviceDot1x"]: - tmp_dict[site.tag] = site.text - - result["local_user_info"].append(tmp_dict) - - if state == "present": - need_cfg = True - else: - if result["local_user_info"]: - for tmp in result["local_user_info"]: - if "userName" in tmp.keys(): - if tmp["userName"] == local_user_name: - - if not local_service_type and not local_user_level \ - and not local_ftp_dir and not local_user_group: - - need_cfg = True - - if local_service_type: - if local_service_type == "none": - if tmp.get("serviceTerminal") == "true" or \ - tmp.get("serviceTelnet") == "true" or \ - tmp.get("serviceFtp") == "true" or \ - tmp.get("serviceSsh") == "true" or \ - tmp.get("serviceSnmp") == "true" or \ - tmp.get("serviceDot1x") == "true": - need_cfg = True - elif local_service_type == "dot1x": - if tmp.get("serviceDot1x") == "true": - need_cfg = True - elif tmp == "ftp": - if tmp.get("serviceFtp") == "true": - need_cfg = True - elif tmp == "snmp": - if tmp.get("serviceSnmp") == "true": - need_cfg = True - elif tmp == "ssh": - if tmp.get("serviceSsh") == "true": - need_cfg = True - elif tmp == "telnet": - if tmp.get("serviceTelnet") == "true": - need_cfg = True - elif tmp == "terminal": - if tmp.get("serviceTerminal") == "true": - need_cfg = True - - if local_user_level: - if tmp.get("userLevel") == local_user_level: - need_cfg = True - - if local_ftp_dir: - if tmp.get("ftpDir") == local_ftp_dir: - need_cfg = True - - if local_user_group: - if tmp.get("userGroupName") == local_user_group: - need_cfg = True - - break - - result["need_cfg"] = need_cfg - return result - - def merge_local_user_info(self, **kwargs): - """ Merge local user information by netconf """ - - module = kwargs["module"] - local_user_name = module.params['local_user_name'] - local_password = module.params['local_password'] - local_service_type = module.params['local_service_type'] - local_ftp_dir = module.params['local_ftp_dir'] - local_user_level = module.params['local_user_level'] - local_user_group = module.params['local_user_group'] - state = module.params['state'] - - cmds = [] - - conf_str = CE_MERGE_LOCAL_USER_INFO_HEADER % local_user_name - - if local_password: - conf_str += "%s" % local_password - - if state == "present": - cmd = "local-user %s password cipher %s" % ( - local_user_name, local_password) - cmds.append(cmd) - - if local_service_type: - if local_service_type == "none": - conf_str += "false" - conf_str += "false" - conf_str += "false" - conf_str += "false" - conf_str += "false" - conf_str += "false" - - cmd = "local-user %s service-type none" % local_user_name - cmds.append(cmd) - - elif local_service_type == "dot1x": - if state == "present": - conf_str += "true" - cmd = "local-user %s service-type dot1x" % local_user_name - else: - conf_str += "false" - cmd = "undo local-user %s service-type" % local_user_name - - cmds.append(cmd) - - else: - option = local_service_type.split(" ") - for tmp in option: - if tmp == "dot1x": - module.fail_json( - msg='Error: Do not input dot1x with other service type.') - if tmp == "none": - module.fail_json( - msg='Error: Do not input none with other service type.') - - if state == "present": - if tmp == "ftp": - conf_str += "true" - cmd = "local-user %s service-type ftp" % local_user_name - elif tmp == "snmp": - conf_str += "true" - cmd = "local-user %s service-type snmp" % local_user_name - elif tmp == "ssh": - conf_str += "true" - cmd = "local-user %s service-type ssh" % local_user_name - elif tmp == "telnet": - conf_str += "true" - cmd = "local-user %s service-type telnet" % local_user_name - elif tmp == "terminal": - conf_str += "true" - cmd = "local-user %s service-type terminal" % local_user_name - - cmds.append(cmd) - - else: - if tmp == "ftp": - conf_str += "false" - elif tmp == "snmp": - conf_str += "false" - elif tmp == "ssh": - conf_str += "false" - elif tmp == "telnet": - conf_str += "false" - elif tmp == "terminal": - conf_str += "false" - - if state == "absent": - cmd = "undo local-user %s service-type" % local_user_name - cmds.append(cmd) - - if local_ftp_dir: - if state == "present": - conf_str += "%s" % local_ftp_dir - cmd = "local-user %s ftp-directory %s" % ( - local_user_name, local_ftp_dir) - cmds.append(cmd) - else: - conf_str += "" - cmd = "undo local-user %s ftp-directory" % local_user_name - cmds.append(cmd) - - if local_user_level: - if state == "present": - conf_str += "%s" % local_user_level - cmd = "local-user %s level %s" % ( - local_user_name, local_user_level) - cmds.append(cmd) - else: - conf_str += "" - cmd = "undo local-user %s level" % local_user_name - cmds.append(cmd) - - if local_user_group: - if state == "present": - conf_str += "%s" % local_user_group - cmd = "local-user %s user-group %s" % ( - local_user_name, local_user_group) - cmds.append(cmd) - else: - conf_str += "" - cmd = "undo local-user %s user-group" % local_user_name - cmds.append(cmd) - - conf_str += CE_MERGE_LOCAL_USER_INFO_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge local user info failed.') - - return cmds - - def delete_local_user_info(self, **kwargs): - """ Delete local user information by netconf """ - - module = kwargs["module"] - local_user_name = module.params['local_user_name'] - conf_str = CE_DELETE_LOCAL_USER_INFO_HEADER % local_user_name - conf_str += CE_DELETE_LOCAL_USER_INFO_TAIL - - cmds = [] - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete local user info failed.') - - cmd = "undo local-user %s" % local_user_name - cmds.append(cmd) - - return cmds - - def get_radius_server_cfg_ipv4(self, **kwargs): - """ Get radius server configure ipv4 """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_ip = module.params['radius_server_ip'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - radius_vpn_name = module.params['radius_vpn_name'] - state = module.params['state'] - - result = dict() - result["radius_server_ip_v4"] = [] - need_cfg = False - - conf_str = CE_GET_RADIUS_SERVER_CFG_IPV4 % radius_group_name - - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - radius_server_ip_v4 = root.findall( - "radius/rdsTemplates/rdsTemplate/rdsServerIPV4s/rdsServerIPV4") - if radius_server_ip_v4: - for tmp in radius_server_ip_v4: - tmp_dict = dict() - for site in tmp: - if site.tag in ["serverType", "serverIPAddress", "serverPort", "serverMode", "vpnName"]: - tmp_dict[site.tag] = site.text - - result["radius_server_ip_v4"].append(tmp_dict) - - if result["radius_server_ip_v4"]: - cfg = dict() - config_list = list() - if radius_server_type: - cfg["serverType"] = radius_server_type.lower() - if radius_server_ip: - cfg["serverIPAddress"] = radius_server_ip.lower() - if radius_server_port: - cfg["serverPort"] = radius_server_port.lower() - if radius_server_mode: - cfg["serverMode"] = radius_server_mode.lower() - if radius_vpn_name: - cfg["vpnName"] = radius_vpn_name.lower() - - for tmp in result["radius_server_ip_v4"]: - exist_cfg = dict() - if radius_server_type: - exist_cfg["serverType"] = tmp.get("serverType").lower() - if radius_server_ip: - exist_cfg["serverIPAddress"] = tmp.get("serverIPAddress").lower() - if radius_server_port: - exist_cfg["serverPort"] = tmp.get("serverPort").lower() - if radius_server_mode: - exist_cfg["serverMode"] = tmp.get("serverMode").lower() - if radius_vpn_name: - exist_cfg["vpnName"] = tmp.get("vpnName").lower() - config_list.append(exist_cfg) - if cfg in config_list: - if state == "present": - need_cfg = False - else: - need_cfg = True - else: - if state == "present": - need_cfg = True - else: - need_cfg = False - result["need_cfg"] = need_cfg - return result - - def merge_radius_server_cfg_ipv4(self, **kwargs): - """ Merge radius server configure ipv4 """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_ip = module.params['radius_server_ip'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - radius_vpn_name = module.params['radius_vpn_name'] - - conf_str = CE_MERGE_RADIUS_SERVER_CFG_IPV4 % ( - radius_group_name, radius_server_type, - radius_server_ip, radius_server_port, - radius_server_mode, radius_vpn_name) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Merge radius server config ipv4 failed.') - - cmds = [] - - cmd = "radius server group %s" % radius_group_name - cmds.append(cmd) - - if radius_server_type == "Authentication": - cmd = "radius server authentication %s %s" % ( - radius_server_ip, radius_server_port) - - if radius_vpn_name and radius_vpn_name != "_public_": - cmd += " vpn-instance %s" % radius_vpn_name - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - else: - cmd = "radius server accounting %s %s" % ( - radius_server_ip, radius_server_port) - - if radius_vpn_name and radius_vpn_name != "_public_": - cmd += " vpn-instance %s" % radius_vpn_name - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def delete_radius_server_cfg_ipv4(self, **kwargs): - """ Delete radius server configure ipv4 """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_ip = module.params['radius_server_ip'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - radius_vpn_name = module.params['radius_vpn_name'] - - conf_str = CE_DELETE_RADIUS_SERVER_CFG_IPV4 % ( - radius_group_name, radius_server_type, - radius_server_ip, radius_server_port, - radius_server_mode, radius_vpn_name) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Create radius server config ipv4 failed.') - - cmds = [] - - cmd = "radius server group %s" % radius_group_name - cmds.append(cmd) - - if radius_server_type == "Authentication": - cmd = "undo radius server authentication %s %s" % ( - radius_server_ip, radius_server_port) - - if radius_vpn_name and radius_vpn_name != "_public_": - cmd += " vpn-instance %s" % radius_vpn_name - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - else: - cmd = "undo radius server accounting %s %s" % ( - radius_server_ip, radius_server_port) - - if radius_vpn_name and radius_vpn_name != "_public_": - cmd += " vpn-instance %s" % radius_vpn_name - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def get_radius_server_cfg_ipv6(self, **kwargs): - """ Get radius server configure ipv6 """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_ipv6 = module.params['radius_server_ipv6'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - state = module.params['state'] - - result = dict() - result["radius_server_ip_v6"] = [] - need_cfg = False - - conf_str = CE_GET_RADIUS_SERVER_CFG_IPV6 % radius_group_name - - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - radius_server_ip_v6 = root.findall( - "radius/rdsTemplates/rdsTemplate/rdsServerIPV6s/rdsServerIPV6") - if radius_server_ip_v6: - for tmp in radius_server_ip_v6: - tmp_dict = dict() - for site in tmp: - if site.tag in ["serverType", "serverIPAddress", "serverPort", "serverMode"]: - tmp_dict[site.tag] = site.text - - result["radius_server_ip_v6"].append(tmp_dict) - - if result["radius_server_ip_v6"]: - cfg = dict() - config_list = list() - if radius_server_type: - cfg["serverType"] = radius_server_type.lower() - if radius_server_ipv6: - cfg["serverIPAddress"] = radius_server_ipv6.lower() - if radius_server_port: - cfg["serverPort"] = radius_server_port.lower() - if radius_server_mode: - cfg["serverMode"] = radius_server_mode.lower() - - for tmp in result["radius_server_ip_v6"]: - exist_cfg = dict() - if radius_server_type: - exist_cfg["serverType"] = tmp.get("serverType").lower() - if radius_server_ipv6: - exist_cfg["serverIPAddress"] = tmp.get("serverIPAddress").lower() - if radius_server_port: - exist_cfg["serverPort"] = tmp.get("serverPort").lower() - if radius_server_mode: - exist_cfg["serverMode"] = tmp.get("serverMode").lower() - config_list.append(exist_cfg) - if cfg in config_list: - if state == "present": - need_cfg = False - else: - need_cfg = True - else: - if state == "present": - need_cfg = True - else: - need_cfg = False - - result["need_cfg"] = need_cfg - return result - - def merge_radius_server_cfg_ipv6(self, **kwargs): - """ Merge radius server configure ipv6 """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_ipv6 = module.params['radius_server_ipv6'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - - conf_str = CE_MERGE_RADIUS_SERVER_CFG_IPV6 % ( - radius_group_name, radius_server_type, - radius_server_ipv6, radius_server_port, - radius_server_mode) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Merge radius server config ipv6 failed.') - - cmds = [] - - cmd = "radius server group %s" % radius_group_name - cmds.append(cmd) - - if radius_server_type == "Authentication": - cmd = "radius server authentication %s %s" % ( - radius_server_ipv6, radius_server_port) - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - else: - cmd = "radius server accounting %s %s" % ( - radius_server_ipv6, radius_server_port) - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def delete_radius_server_cfg_ipv6(self, **kwargs): - """ Delete radius server configure ipv6 """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_ipv6 = module.params['radius_server_ipv6'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - - conf_str = CE_DELETE_RADIUS_SERVER_CFG_IPV6 % ( - radius_group_name, radius_server_type, - radius_server_ipv6, radius_server_port, - radius_server_mode) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Create radius server config ipv6 failed.') - - cmds = [] - - cmd = "radius server group %s" % radius_group_name - cmds.append(cmd) - - if radius_server_type == "Authentication": - cmd = "undo radius server authentication %s %s" % ( - radius_server_ipv6, radius_server_port) - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - else: - cmd = "undo radius server accounting %s %s" % ( - radius_server_ipv6, radius_server_port) - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def get_radius_server_name(self, **kwargs): - """ Get radius server name """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_name = module.params['radius_server_name'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - radius_vpn_name = module.params['radius_vpn_name'] - state = module.params['state'] - - result = dict() - result["radius_server_name_cfg"] = [] - need_cfg = False - - conf_str = CE_GET_RADIUS_SERVER_NAME % radius_group_name - - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - radius_server_name_cfg = root.findall( - "radius/rdsTemplates/rdsTemplate/rdsServerNames/rdsServerName") - if radius_server_name_cfg: - for tmp in radius_server_name_cfg: - tmp_dict = dict() - for site in tmp: - if site.tag in ["serverType", "serverName", "serverPort", "serverMode", "vpnName"]: - tmp_dict[site.tag] = site.text - - result["radius_server_name_cfg"].append(tmp_dict) - - if result["radius_server_name_cfg"]: - cfg = dict() - config_list = list() - if radius_server_type: - cfg["serverType"] = radius_server_type.lower() - if radius_server_name: - cfg["serverName"] = radius_server_name.lower() - if radius_server_port: - cfg["serverPort"] = radius_server_port.lower() - if radius_server_mode: - cfg["serverMode"] = radius_server_mode.lower() - if radius_vpn_name: - cfg["vpnName"] = radius_vpn_name.lower() - - for tmp in result["radius_server_name_cfg"]: - exist_cfg = dict() - if radius_server_type: - exist_cfg["serverType"] = tmp.get("serverType").lower() - if radius_server_name: - exist_cfg["serverName"] = tmp.get("serverName").lower() - if radius_server_port: - exist_cfg["serverPort"] = tmp.get("serverPort").lower() - if radius_server_mode: - exist_cfg["serverMode"] = tmp.get("serverMode").lower() - if radius_vpn_name: - exist_cfg["vpnName"] = tmp.get("vpnName").lower() - config_list.append(exist_cfg) - if cfg in config_list: - if state == "present": - need_cfg = False - else: - need_cfg = True - else: - if state == "present": - need_cfg = True - else: - need_cfg = False - result["need_cfg"] = need_cfg - return result - - def merge_radius_server_name(self, **kwargs): - """ Merge radius server name """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_name = module.params['radius_server_name'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - radius_vpn_name = module.params['radius_vpn_name'] - - conf_str = CE_MERGE_RADIUS_SERVER_NAME % ( - radius_group_name, radius_server_type, - radius_server_name, radius_server_port, - radius_server_mode, radius_vpn_name) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge radius server name failed.') - - cmds = [] - - cmd = "radius server group %s" % radius_group_name - cmds.append(cmd) - - if radius_server_type == "Authentication": - cmd = "radius server authentication hostname %s %s" % ( - radius_server_name, radius_server_port) - - if radius_vpn_name and radius_vpn_name != "_public_": - cmd += " vpn-instance %s" % radius_vpn_name - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - else: - cmd = "radius server accounting hostname %s %s" % ( - radius_server_name, radius_server_port) - - if radius_vpn_name and radius_vpn_name != "_public_": - cmd += " vpn-instance %s" % radius_vpn_name - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def delete_radius_server_name(self, **kwargs): - """ Delete radius server name """ - - module = kwargs["module"] - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_name = module.params['radius_server_name'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - radius_vpn_name = module.params['radius_vpn_name'] - - conf_str = CE_DELETE_RADIUS_SERVER_NAME % ( - radius_group_name, radius_server_type, - radius_server_name, radius_server_port, - radius_server_mode, radius_vpn_name) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: delete radius server name failed.') - - cmds = [] - - cmd = "radius server group %s" % radius_group_name - cmds.append(cmd) - - if radius_server_type == "Authentication": - cmd = "undo radius server authentication hostname %s %s" % ( - radius_server_name, radius_server_port) - - if radius_vpn_name and radius_vpn_name != "_public_": - cmd += " vpn-instance %s" % radius_vpn_name - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - else: - cmd = "undo radius server accounting hostname %s %s" % ( - radius_server_name, radius_server_port) - - if radius_vpn_name and radius_vpn_name != "_public_": - cmd += " vpn-instance %s" % radius_vpn_name - - if radius_server_mode == "Secondary-server": - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def get_hwtacacs_server_cfg_ipv4(self, **kwargs): - """ Get hwtacacs server configure ipv4 """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_ip = module.params["hwtacacs_server_ip"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = module.params[ - "hwtacacs_is_secondary_server"] - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - hwtacacs_is_public_net = module.params["hwtacacs_is_public_net"] - state = module.params["state"] - - result = dict() - result["hwtacacs_server_cfg_ipv4"] = [] - need_cfg = False - - conf_str = CE_GET_HWTACACS_SERVER_CFG_IPV4 % hwtacacs_template - - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - hwtacacs_server_cfg_ipv4 = root.findall( - "hwtacacs/hwTacTempCfgs/hwTacTempCfg/hwTacSrvCfgs/hwTacSrvCfg") - if hwtacacs_server_cfg_ipv4: - for tmp in hwtacacs_server_cfg_ipv4: - tmp_dict = dict() - for site in tmp: - if site.tag in ["serverIpAddress", "serverType", "isSecondaryServer", "isPublicNet", "vpnName"]: - tmp_dict[site.tag] = site.text - - result["hwtacacs_server_cfg_ipv4"].append(tmp_dict) - - if result["hwtacacs_server_cfg_ipv4"]: - cfg = dict() - config_list = list() - - if hwtacacs_server_ip: - cfg["serverIpAddress"] = hwtacacs_server_ip.lower() - if hwtacacs_server_type: - cfg["serverType"] = hwtacacs_server_type.lower() - if hwtacacs_is_secondary_server: - cfg["isSecondaryServer"] = str(hwtacacs_is_secondary_server).lower() - if hwtacacs_is_public_net: - cfg["isPublicNet"] = str(hwtacacs_is_public_net).lower() - if hwtacacs_vpn_name: - cfg["vpnName"] = hwtacacs_vpn_name.lower() - - for tmp in result["hwtacacs_server_cfg_ipv4"]: - exist_cfg = dict() - if hwtacacs_server_ip: - exist_cfg["serverIpAddress"] = tmp.get("serverIpAddress").lower() - if hwtacacs_server_type: - exist_cfg["serverType"] = tmp.get("serverType").lower() - if hwtacacs_is_secondary_server: - exist_cfg["isSecondaryServer"] = tmp.get("isSecondaryServer").lower() - if hwtacacs_is_public_net: - exist_cfg["isPublicNet"] = tmp.get("isPublicNet").lower() - if hwtacacs_vpn_name: - exist_cfg["vpnName"] = tmp.get("vpnName").lower() - config_list.append(exist_cfg) - if cfg in config_list: - if state == "present": - need_cfg = False - else: - need_cfg = True - else: - if state == "present": - need_cfg = True - else: - need_cfg = False - result["need_cfg"] = need_cfg - return result - - def merge_hwtacacs_server_cfg_ipv4(self, **kwargs): - """ Merge hwtacacs server configure ipv4 """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_ip = module.params["hwtacacs_server_ip"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = module.params[ - "hwtacacs_is_secondary_server"] - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - hwtacacs_is_public_net = module.params["hwtacacs_is_public_net"] - - conf_str = CE_MERGE_HWTACACS_SERVER_CFG_IPV4 % ( - hwtacacs_template, hwtacacs_server_ip, - hwtacacs_server_type, str(hwtacacs_is_secondary_server).lower(), - hwtacacs_vpn_name, str(hwtacacs_is_public_net).lower()) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Merge hwtacacs server config ipv4 failed.') - - cmds = [] - - cmd = "hwtacacs server template %s" % hwtacacs_template - cmds.append(cmd) - - if hwtacacs_server_type == "Authentication": - cmd = "hwtacacs server authentication %s" % hwtacacs_server_ip - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Authorization": - cmd = "hwtacacs server authorization %s" % hwtacacs_server_ip - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Accounting": - cmd = "hwtacacs server accounting %s" % hwtacacs_server_ip - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Common": - cmd = "hwtacacs server %s" % hwtacacs_server_ip - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def delete_hwtacacs_server_cfg_ipv4(self, **kwargs): - """ Delete hwtacacs server configure ipv4 """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_ip = module.params["hwtacacs_server_ip"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = module.params[ - "hwtacacs_is_secondary_server"] - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - hwtacacs_is_public_net = module.params["hwtacacs_is_public_net"] - - conf_str = CE_DELETE_HWTACACS_SERVER_CFG_IPV4 % ( - hwtacacs_template, hwtacacs_server_ip, - hwtacacs_server_type, str(hwtacacs_is_secondary_server).lower(), - hwtacacs_vpn_name, str(hwtacacs_is_public_net).lower()) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Delete hwtacacs server config ipv4 failed.') - - cmds = [] - - cmd = "hwtacacs server template %s" % hwtacacs_template - cmds.append(cmd) - - if hwtacacs_server_type == "Authentication": - cmd = "undo hwtacacs server authentication %s" % hwtacacs_server_ip - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Authorization": - cmd = "undo hwtacacs server authorization %s" % hwtacacs_server_ip - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Accounting": - cmd = "undo hwtacacs server accounting %s" % hwtacacs_server_ip - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Common": - cmd = "undo hwtacacs server %s" % hwtacacs_server_ip - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def get_hwtacacs_server_cfg_ipv6(self, **kwargs): - """ Get hwtacacs server configure ipv6 """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_ipv6 = module.params["hwtacacs_server_ipv6"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = module.params[ - "hwtacacs_is_secondary_server"] - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - state = module.params["state"] - - result = dict() - result["hwtacacs_server_cfg_ipv6"] = [] - need_cfg = False - - conf_str = CE_GET_HWTACACS_SERVER_CFG_IPV6 % hwtacacs_template - - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - hwtacacs_server_cfg_ipv6 = root.findall( - "hwtacacs/hwTacTempCfgs/hwTacTempCfg/hwTacIpv6SrvCfgs/hwTacIpv6SrvCfg") - if hwtacacs_server_cfg_ipv6: - for tmp in hwtacacs_server_cfg_ipv6: - tmp_dict = dict() - for site in tmp: - if site.tag in ["serverIpAddress", "serverType", "isSecondaryServer", "vpnName"]: - tmp_dict[site.tag] = site.text - - result["hwtacacs_server_cfg_ipv6"].append(tmp_dict) - - if result["hwtacacs_server_cfg_ipv6"]: - cfg = dict() - config_list = list() - - if hwtacacs_server_ipv6: - cfg["serverIpAddress"] = hwtacacs_server_ipv6.lower() - if hwtacacs_server_type: - cfg["serverType"] = hwtacacs_server_type.lower() - if hwtacacs_is_secondary_server: - cfg["isSecondaryServer"] = str(hwtacacs_is_secondary_server).lower() - if hwtacacs_vpn_name: - cfg["vpnName"] = hwtacacs_vpn_name.lower() - - for tmp in result["hwtacacs_server_cfg_ipv6"]: - exist_cfg = dict() - if hwtacacs_server_ipv6: - exist_cfg["serverIpAddress"] = tmp.get("serverIpAddress").lower() - if hwtacacs_server_type: - exist_cfg["serverType"] = tmp.get("serverType").lower() - if hwtacacs_is_secondary_server: - exist_cfg["isSecondaryServer"] = tmp.get("isSecondaryServer").lower() - if hwtacacs_vpn_name: - exist_cfg["vpnName"] = tmp.get("vpnName").lower() - config_list.append(exist_cfg) - if cfg in config_list: - if state == "present": - need_cfg = False - else: - need_cfg = True - else: - if state == "present": - need_cfg = True - else: - need_cfg = False - result["need_cfg"] = need_cfg - return result - - def merge_hwtacacs_server_cfg_ipv6(self, **kwargs): - """ Merge hwtacacs server configure ipv6 """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_ipv6 = module.params["hwtacacs_server_ipv6"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = module.params[ - "hwtacacs_is_secondary_server"] - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - - conf_str = CE_MERGE_HWTACACS_SERVER_CFG_IPV6 % ( - hwtacacs_template, hwtacacs_server_ipv6, - hwtacacs_server_type, str(hwtacacs_is_secondary_server).lower(), - hwtacacs_vpn_name) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Merge hwtacacs server config ipv6 failed.') - - cmds = [] - - cmd = "hwtacacs server template %s" % hwtacacs_template - cmds.append(cmd) - - if hwtacacs_server_type == "Authentication": - cmd = "hwtacacs server authentication %s" % hwtacacs_server_ipv6 - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Authorization": - cmd = "hwtacacs server authorization %s" % hwtacacs_server_ipv6 - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Accounting": - cmd = "hwtacacs server accounting %s" % hwtacacs_server_ipv6 - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Common": - cmd = "hwtacacs server %s" % hwtacacs_server_ipv6 - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_secondary_server: - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def delete_hwtacacs_server_cfg_ipv6(self, **kwargs): - """ Delete hwtacacs server configure ipv6 """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_ipv6 = module.params["hwtacacs_server_ipv6"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = module.params[ - "hwtacacs_is_secondary_server"] - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - - conf_str = CE_DELETE_HWTACACS_SERVER_CFG_IPV6 % ( - hwtacacs_template, hwtacacs_server_ipv6, - hwtacacs_server_type, str(hwtacacs_is_secondary_server).lower(), - hwtacacs_vpn_name) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Delete hwtacacs server config ipv6 failed.') - - cmds = [] - - cmd = "hwtacacs server template %s" % hwtacacs_template - cmds.append(cmd) - - if hwtacacs_server_type == "Authentication": - cmd = "undo hwtacacs server authentication %s" % hwtacacs_server_ipv6 - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Authorization": - cmd = "undo hwtacacs server authorization %s" % hwtacacs_server_ipv6 - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Accounting": - cmd = "undo hwtacacs server accounting %s" % hwtacacs_server_ipv6 - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Common": - cmd = "undo hwtacacs server %s" % hwtacacs_server_ipv6 - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_secondary_server: - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def get_hwtacacs_host_server_cfg(self, **kwargs): - """ Get hwtacacs host server configure """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_host_name = module.params["hwtacacs_server_host_name"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = "true" if module.params[ - "hwtacacs_is_secondary_server"] is True else "false" - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - hwtacacs_is_public_net = "true" if module.params[ - "hwtacacs_is_public_net"] is True else "false" - state = module.params["state"] - - result = dict() - result["hwtacacs_server_name_cfg"] = [] - need_cfg = False - - conf_str = CE_GET_HWTACACS_HOST_SERVER_CFG % hwtacacs_template - - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - hwtacacs_server_name_cfg = root.findall( - "hwtacacs/hwTacTempCfgs/hwTacTempCfg/hwTacHostSrvCfgs/hwTacHostSrvCfg") - if hwtacacs_server_name_cfg: - for tmp in hwtacacs_server_name_cfg: - tmp_dict = dict() - for site in tmp: - if site.tag in ["serverHostName", "serverType", "isSecondaryServer", "isPublicNet", "vpnName"]: - tmp_dict[site.tag] = site.text - - result["hwtacacs_server_name_cfg"].append(tmp_dict) - - if result["hwtacacs_server_name_cfg"]: - cfg = dict() - config_list = list() - - if hwtacacs_server_host_name: - cfg["serverHostName"] = hwtacacs_server_host_name.lower() - if hwtacacs_server_type: - cfg["serverType"] = hwtacacs_server_type.lower() - if hwtacacs_is_secondary_server: - cfg["isSecondaryServer"] = str(hwtacacs_is_secondary_server).lower() - if hwtacacs_is_public_net: - cfg["isPublicNet"] = str(hwtacacs_is_public_net).lower() - if hwtacacs_vpn_name: - cfg["vpnName"] = hwtacacs_vpn_name.lower() - - for tmp in result["hwtacacs_server_name_cfg"]: - exist_cfg = dict() - if hwtacacs_server_host_name: - exist_cfg["serverHostName"] = tmp.get("serverHostName").lower() - if hwtacacs_server_type: - exist_cfg["serverType"] = tmp.get("serverType").lower() - if hwtacacs_is_secondary_server: - exist_cfg["isSecondaryServer"] = tmp.get("isSecondaryServer").lower() - if hwtacacs_is_public_net: - exist_cfg["isPublicNet"] = tmp.get("isPublicNet").lower() - if hwtacacs_vpn_name: - exist_cfg["vpnName"] = tmp.get("vpnName").lower() - config_list.append(exist_cfg) - if cfg in config_list: - if state == "present": - need_cfg = False - else: - need_cfg = True - else: - if state == "present": - need_cfg = True - else: - need_cfg = False - result["need_cfg"] = need_cfg - return result - - def merge_hwtacacs_host_server_cfg(self, **kwargs): - """ Merge hwtacacs host server configure """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_host_name = module.params["hwtacacs_server_host_name"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = module.params[ - "hwtacacs_is_secondary_server"] - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - hwtacacs_is_public_net = module.params["hwtacacs_is_public_net"] - - conf_str = CE_MERGE_HWTACACS_HOST_SERVER_CFG % ( - hwtacacs_template, hwtacacs_server_host_name, - hwtacacs_server_type, str(hwtacacs_is_secondary_server).lower(), - hwtacacs_vpn_name, str(hwtacacs_is_public_net).lower()) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Merge hwtacacs host server config failed.') - - cmds = [] - - if hwtacacs_server_type == "Authentication": - cmd = "hwtacacs server authentication host %s" % hwtacacs_server_host_name - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Authorization": - cmd = "hwtacacs server authorization host %s" % hwtacacs_server_host_name - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Accounting": - cmd = "hwtacacs server accounting host %s" % hwtacacs_server_host_name - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Common": - cmd = "hwtacacs server host host-name %s" % hwtacacs_server_host_name - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - cmds.append(cmd) - return cmds - - def delete_hwtacacs_host_server_cfg(self, **kwargs): - """ Delete hwtacacs host server configure """ - - module = kwargs["module"] - hwtacacs_template = module.params["hwtacacs_template"] - hwtacacs_server_host_name = module.params["hwtacacs_server_host_name"] - hwtacacs_server_type = module.params["hwtacacs_server_type"] - hwtacacs_is_secondary_server = module.params[ - "hwtacacs_is_secondary_server"] - hwtacacs_vpn_name = module.params["hwtacacs_vpn_name"] - hwtacacs_is_public_net = module.params["hwtacacs_is_public_net"] - - conf_str = CE_DELETE_HWTACACS_HOST_SERVER_CFG % ( - hwtacacs_template, hwtacacs_server_host_name, - hwtacacs_server_type, str(hwtacacs_is_secondary_server).lower(), - hwtacacs_vpn_name, str(hwtacacs_is_public_net).lower()) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Delete hwtacacs host server config failed.') - - cmds = [] - - if hwtacacs_server_type == "Authentication": - cmd = "undo hwtacacs server authentication host %s" % hwtacacs_server_host_name - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Authorization": - cmd = "undo hwtacacs server authorization host %s" % hwtacacs_server_host_name - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Accounting": - cmd = "undo hwtacacs server accounting host %s" % hwtacacs_server_host_name - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - elif hwtacacs_server_type == "Common": - cmd = "undo hwtacacs server host %s" % hwtacacs_server_host_name - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - cmd += " vpn-instance %s" % hwtacacs_vpn_name - if hwtacacs_is_public_net: - cmd += " public-net" - if hwtacacs_is_secondary_server: - cmd += " secondary" - - cmds.append(cmd) - return cmds - - -def check_name(**kwargs): - """ Check invalid name """ - - module = kwargs["module"] - name = kwargs["name"] - invalid_char = kwargs["invalid_char"] - - for item in invalid_char: - if item in name: - module.fail_json( - msg='Error: Invalid char %s is in the name %s ' % (item, name)) - - -def check_module_argument(**kwargs): - """ Check module argument """ - - module = kwargs["module"] - - # local para - local_user_name = module.params['local_user_name'] - local_password = module.params['local_password'] - local_ftp_dir = module.params['local_ftp_dir'] - local_user_level = module.params['local_user_level'] - local_user_group = module.params['local_user_group'] - - # radius para - radius_group_name = module.params['radius_group_name'] - radius_server_ip = module.params['radius_server_ip'] - radius_server_port = module.params['radius_server_port'] - radius_vpn_name = module.params['radius_vpn_name'] - radius_server_name = module.params['radius_server_name'] - - # hwtacacs para - hwtacacs_template = module.params['hwtacacs_template'] - hwtacacs_server_ip = module.params['hwtacacs_server_ip'] - hwtacacs_vpn_name = module.params['hwtacacs_vpn_name'] - hwtacacs_server_host_name = module.params['hwtacacs_server_host_name'] - - if local_user_name: - if len(local_user_name) > 253: - module.fail_json( - msg='Error: The local_user_name %s is large than 253.' % local_user_name) - check_name(module=module, name=local_user_name, - invalid_char=INVALID_USER_NAME_CHAR) - - if local_password and len(local_password) > 255: - module.fail_json( - msg='Error: The local_password %s is large than 255.' % local_password) - - if local_user_level: - if int(local_user_level) > 15 or int(local_user_level) < 0: - module.fail_json( - msg='Error: The local_user_level %s is out of [0 - 15].' % local_user_level) - - if local_ftp_dir: - if len(local_ftp_dir) > 255: - module.fail_json( - msg='Error: The local_ftp_dir %s is large than 255.' % local_ftp_dir) - - if local_user_group: - if len(local_user_group) > 32 or len(local_user_group) < 1: - module.fail_json( - msg='Error: The local_user_group %s is out of [1 - 32].' % local_user_group) - - if radius_group_name and len(radius_group_name) > 32: - module.fail_json( - msg='Error: The radius_group_name %s is large than 32.' % radius_group_name) - - if radius_server_ip and not check_ip_addr(radius_server_ip): - module.fail_json( - msg='Error: The radius_server_ip %s is invalid.' % radius_server_ip) - - if radius_server_port and not radius_server_port.isdigit(): - module.fail_json( - msg='Error: The radius_server_port %s is invalid.' % radius_server_port) - - if radius_vpn_name: - if len(radius_vpn_name) > 31: - module.fail_json( - msg='Error: The radius_vpn_name %s is large than 31.' % radius_vpn_name) - if ' ' in radius_vpn_name: - module.fail_json( - msg='Error: The radius_vpn_name %s include space.' % radius_vpn_name) - - if radius_server_name: - if len(radius_server_name) > 255: - module.fail_json( - msg='Error: The radius_server_name %s is large than 255.' % radius_server_name) - if ' ' in radius_server_name: - module.fail_json( - msg='Error: The radius_server_name %s include space.' % radius_server_name) - - if hwtacacs_template and len(hwtacacs_template) > 32: - module.fail_json( - msg='Error: The hwtacacs_template %s is large than 32.' % hwtacacs_template) - - if hwtacacs_server_ip and not check_ip_addr(hwtacacs_server_ip): - module.fail_json( - msg='Error: The hwtacacs_server_ip %s is invalid.' % hwtacacs_server_ip) - - if hwtacacs_vpn_name: - if len(hwtacacs_vpn_name) > 31: - module.fail_json( - msg='Error: The hwtacacs_vpn_name %s is large than 31.' % hwtacacs_vpn_name) - if ' ' in hwtacacs_vpn_name: - module.fail_json( - msg='Error: The hwtacacs_vpn_name %s include space.' % hwtacacs_vpn_name) - - if hwtacacs_server_host_name: - if len(hwtacacs_server_host_name) > 255: - module.fail_json( - msg='Error: The hwtacacs_server_host_name %s is large than 255.' % hwtacacs_server_host_name) - if ' ' in hwtacacs_server_host_name: - module.fail_json( - msg='Error: The hwtacacs_server_host_name %s include space.' % hwtacacs_server_host_name) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - local_user_name=dict(type='str'), - local_password=dict(type='str', no_log=True), - local_service_type=dict(type='str'), - local_ftp_dir=dict(type='str'), - local_user_level=dict(type='str'), - local_user_group=dict(type='str'), - radius_group_name=dict(type='str'), - radius_server_type=dict(choices=['Authentication', 'Accounting']), - radius_server_ip=dict(type='str'), - radius_server_ipv6=dict(type='str'), - radius_server_port=dict(type='str'), - radius_server_mode=dict( - choices=['Secondary-server', 'Primary-server']), - radius_vpn_name=dict(type='str'), - radius_server_name=dict(type='str'), - hwtacacs_template=dict(type='str'), - hwtacacs_server_ip=dict(type='str'), - hwtacacs_server_ipv6=dict(type='str'), - hwtacacs_server_type=dict( - choices=['Authentication', 'Authorization', 'Accounting', 'Common']), - hwtacacs_is_secondary_server=dict( - required=False, default=False, type='bool'), - hwtacacs_vpn_name=dict(type='str'), - hwtacacs_is_public_net=dict( - required=False, default=False, type='bool'), - hwtacacs_server_host_name=dict(type='str') - ) - - argument_spec.update(ce_argument_spec) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - check_module_argument(module=module) - - changed = False - proposed = dict() - existing = dict() - end_state = dict() - updates = [] - - # common para - state = module.params['state'] - - # local para - local_user_name = module.params['local_user_name'] - local_password = module.params['local_password'] - local_service_type = module.params['local_service_type'] - local_ftp_dir = module.params['local_ftp_dir'] - local_user_level = module.params['local_user_level'] - local_user_group = module.params['local_user_group'] - - # radius para - radius_group_name = module.params['radius_group_name'] - radius_server_type = module.params['radius_server_type'] - radius_server_ip = module.params['radius_server_ip'] - radius_server_ipv6 = module.params['radius_server_ipv6'] - radius_server_port = module.params['radius_server_port'] - radius_server_mode = module.params['radius_server_mode'] - radius_vpn_name = module.params['radius_vpn_name'] - radius_server_name = module.params['radius_server_name'] - - # hwtacacs para - hwtacacs_template = module.params['hwtacacs_template'] - hwtacacs_server_ip = module.params['hwtacacs_server_ip'] - hwtacacs_server_ipv6 = module.params['hwtacacs_server_ipv6'] - hwtacacs_server_type = module.params['hwtacacs_server_type'] - hwtacacs_is_secondary_server = module.params[ - 'hwtacacs_is_secondary_server'] - hwtacacs_vpn_name = module.params['hwtacacs_vpn_name'] - hwtacacs_is_public_net = module.params['hwtacacs_is_public_net'] - hwtacacs_server_host_name = module.params['hwtacacs_server_host_name'] - - ce_aaa_server_host = AaaServerHost() - - if not ce_aaa_server_host: - module.fail_json(msg='Error: Construct ce_aaa_server failed.') - - # get proposed - proposed["state"] = state - if local_user_name: - proposed["local_user_name"] = local_user_name - if local_password: - proposed["local_password"] = "******" - if local_service_type: - proposed["local_service_type"] = local_service_type - if local_ftp_dir: - proposed["local_ftp_dir"] = local_ftp_dir - if local_user_level: - proposed["local_user_level"] = local_user_level - if local_user_group: - proposed["local_user_group"] = local_user_group - if radius_group_name: - proposed["radius_group_name"] = radius_group_name - if radius_server_type: - proposed["radius_server_type"] = radius_server_type - if radius_server_ip: - proposed["radius_server_ip"] = radius_server_ip - if radius_server_ipv6: - proposed["radius_server_ipv6"] = radius_server_ipv6 - if radius_server_port: - proposed["radius_server_port"] = radius_server_port - if radius_server_mode: - proposed["radius_server_mode"] = radius_server_mode - if radius_vpn_name: - proposed["radius_vpn_name"] = radius_vpn_name - if radius_server_name: - proposed["radius_server_name"] = radius_server_name - if hwtacacs_template: - proposed["hwtacacs_template"] = hwtacacs_template - if hwtacacs_server_ip: - proposed["hwtacacs_server_ip"] = hwtacacs_server_ip - if hwtacacs_server_ipv6: - proposed["hwtacacs_server_ipv6"] = hwtacacs_server_ipv6 - if hwtacacs_server_type: - proposed["hwtacacs_server_type"] = hwtacacs_server_type - proposed["hwtacacs_is_secondary_server"] = hwtacacs_is_secondary_server - if hwtacacs_vpn_name: - proposed["hwtacacs_vpn_name"] = hwtacacs_vpn_name - proposed["hwtacacs_is_public_net"] = hwtacacs_is_public_net - if hwtacacs_server_host_name: - proposed["hwtacacs_server_host_name"] = hwtacacs_server_host_name - - if local_user_name: - - if state == "present" and not local_password: - module.fail_json( - msg='Error: Please input local_password when config local user.') - - local_user_result = ce_aaa_server_host.get_local_user_info( - module=module) - existing["local user name"] = local_user_result["local_user_info"] - - if state == "present": - # present local user - if local_user_result["need_cfg"]: - cmd = ce_aaa_server_host.merge_local_user_info(module=module) - - changed = True - updates.append(cmd) - - else: - # absent local user - if local_user_result["need_cfg"]: - if not local_service_type and not local_ftp_dir and not local_user_level and not local_user_group: - cmd = ce_aaa_server_host.delete_local_user_info( - module=module) - else: - cmd = ce_aaa_server_host.merge_local_user_info( - module=module) - - changed = True - updates.append(cmd) - - local_user_result = ce_aaa_server_host.get_local_user_info( - module=module) - end_state["local user name"] = local_user_result["local_user_info"] - - if radius_group_name: - - if not radius_server_ip and not radius_server_ipv6 and not radius_server_name: - module.fail_json( - msg='Error: Please input radius_server_ip or radius_server_ipv6 or radius_server_name.') - - if radius_server_ip and radius_server_ipv6: - module.fail_json( - msg='Error: Please do not input radius_server_ip and radius_server_ipv6 at the same time.') - - if not radius_server_type or not radius_server_port or not radius_server_mode or not radius_vpn_name: - module.fail_json( - msg='Error: Please input radius_server_type radius_server_port radius_server_mode radius_vpn_name.') - - if radius_server_ip: - rds_server_ipv4_result = ce_aaa_server_host.get_radius_server_cfg_ipv4( - module=module) - if radius_server_ipv6: - rds_server_ipv6_result = ce_aaa_server_host.get_radius_server_cfg_ipv6( - module=module) - if radius_server_name: - rds_server_name_result = ce_aaa_server_host.get_radius_server_name( - module=module) - - if radius_server_ip and rds_server_ipv4_result["radius_server_ip_v4"]: - existing["radius server ipv4"] = rds_server_ipv4_result[ - "radius_server_ip_v4"] - if radius_server_ipv6 and rds_server_ipv6_result["radius_server_ip_v6"]: - existing["radius server ipv6"] = rds_server_ipv6_result[ - "radius_server_ip_v6"] - if radius_server_name and rds_server_name_result["radius_server_name_cfg"]: - existing["radius server name cfg"] = rds_server_name_result[ - "radius_server_name_cfg"] - - if state == "present": - if radius_server_ip and rds_server_ipv4_result["need_cfg"]: - cmd = ce_aaa_server_host.merge_radius_server_cfg_ipv4( - module=module) - changed = True - updates.append(cmd) - - if radius_server_ipv6 and rds_server_ipv6_result["need_cfg"]: - cmd = ce_aaa_server_host.merge_radius_server_cfg_ipv6( - module=module) - changed = True - updates.append(cmd) - - if radius_server_name and rds_server_name_result["need_cfg"]: - cmd = ce_aaa_server_host.merge_radius_server_name( - module=module) - changed = True - updates.append(cmd) - else: - if radius_server_ip and rds_server_ipv4_result["need_cfg"]: - cmd = ce_aaa_server_host.delete_radius_server_cfg_ipv4( - module=module) - changed = True - updates.append(cmd) - - if radius_server_ipv6 and rds_server_ipv6_result["need_cfg"]: - cmd = ce_aaa_server_host.delete_radius_server_cfg_ipv6( - module=module) - changed = True - updates.append(cmd) - - if radius_server_name and rds_server_name_result["need_cfg"]: - cmd = ce_aaa_server_host.delete_radius_server_name( - module=module) - changed = True - updates.append(cmd) - - if radius_server_ip: - rds_server_ipv4_result = ce_aaa_server_host.get_radius_server_cfg_ipv4( - module=module) - if radius_server_ipv6: - rds_server_ipv6_result = ce_aaa_server_host.get_radius_server_cfg_ipv6( - module=module) - if radius_server_name: - rds_server_name_result = ce_aaa_server_host.get_radius_server_name( - module=module) - - if radius_server_ip and rds_server_ipv4_result["radius_server_ip_v4"]: - end_state["radius server ipv4"] = rds_server_ipv4_result[ - "radius_server_ip_v4"] - if radius_server_ipv6 and rds_server_ipv6_result["radius_server_ip_v6"]: - end_state["radius server ipv6"] = rds_server_ipv6_result[ - "radius_server_ip_v6"] - if radius_server_name and rds_server_name_result["radius_server_name_cfg"]: - end_state["radius server name cfg"] = rds_server_name_result[ - "radius_server_name_cfg"] - - if hwtacacs_template: - - if not hwtacacs_server_ip and not hwtacacs_server_ipv6 and not hwtacacs_server_host_name: - module.fail_json( - msg='Error: Please input hwtacacs_server_ip or hwtacacs_server_ipv6 or hwtacacs_server_host_name.') - - if not hwtacacs_server_type or not hwtacacs_vpn_name: - module.fail_json( - msg='Error: Please input hwtacacs_server_type hwtacacs_vpn_name.') - - if hwtacacs_server_ip and hwtacacs_server_ipv6: - module.fail_json( - msg='Error: Please do not set hwtacacs_server_ip and hwtacacs_server_ipv6 at the same time.') - - if hwtacacs_vpn_name and hwtacacs_vpn_name != "_public_": - if hwtacacs_is_public_net: - module.fail_json( - msg='Error: Please do not set vpn and public net at the same time.') - - if hwtacacs_server_ip: - hwtacacs_server_ipv4_result = ce_aaa_server_host.get_hwtacacs_server_cfg_ipv4( - module=module) - if hwtacacs_server_ipv6: - hwtacacs_server_ipv6_result = ce_aaa_server_host.get_hwtacacs_server_cfg_ipv6( - module=module) - if hwtacacs_server_host_name: - hwtacacs_host_name_result = ce_aaa_server_host.get_hwtacacs_host_server_cfg( - module=module) - - if hwtacacs_server_ip and hwtacacs_server_ipv4_result["hwtacacs_server_cfg_ipv4"]: - existing["hwtacacs server cfg ipv4"] = hwtacacs_server_ipv4_result[ - "hwtacacs_server_cfg_ipv4"] - if hwtacacs_server_ipv6 and hwtacacs_server_ipv6_result["hwtacacs_server_cfg_ipv6"]: - existing["hwtacacs server cfg ipv6"] = hwtacacs_server_ipv6_result[ - "hwtacacs_server_cfg_ipv6"] - if hwtacacs_server_host_name and hwtacacs_host_name_result["hwtacacs_server_name_cfg"]: - existing["hwtacacs server name cfg"] = hwtacacs_host_name_result[ - "hwtacacs_server_name_cfg"] - - if state == "present": - if hwtacacs_server_ip and hwtacacs_server_ipv4_result["need_cfg"]: - cmd = ce_aaa_server_host.merge_hwtacacs_server_cfg_ipv4( - module=module) - changed = True - updates.append(cmd) - - if hwtacacs_server_ipv6 and hwtacacs_server_ipv6_result["need_cfg"]: - cmd = ce_aaa_server_host.merge_hwtacacs_server_cfg_ipv6( - module=module) - changed = True - updates.append(cmd) - - if hwtacacs_server_host_name and hwtacacs_host_name_result["need_cfg"]: - cmd = ce_aaa_server_host.merge_hwtacacs_host_server_cfg( - module=module) - changed = True - updates.append(cmd) - - else: - if hwtacacs_server_ip and hwtacacs_server_ipv4_result["need_cfg"]: - cmd = ce_aaa_server_host.delete_hwtacacs_server_cfg_ipv4( - module=module) - changed = True - updates.append(cmd) - - if hwtacacs_server_ipv6 and hwtacacs_server_ipv6_result["need_cfg"]: - cmd = ce_aaa_server_host.delete_hwtacacs_server_cfg_ipv6( - module=module) - changed = True - updates.append(cmd) - - if hwtacacs_server_host_name and hwtacacs_host_name_result["need_cfg"]: - cmd = ce_aaa_server_host.delete_hwtacacs_host_server_cfg( - module=module) - changed = True - updates.append(cmd) - - if hwtacacs_server_ip: - hwtacacs_server_ipv4_result = ce_aaa_server_host.get_hwtacacs_server_cfg_ipv4( - module=module) - if hwtacacs_server_ipv6: - hwtacacs_server_ipv6_result = ce_aaa_server_host.get_hwtacacs_server_cfg_ipv6( - module=module) - if hwtacacs_server_host_name: - hwtacacs_host_name_result = ce_aaa_server_host.get_hwtacacs_host_server_cfg( - module=module) - - if hwtacacs_server_ip and hwtacacs_server_ipv4_result["hwtacacs_server_cfg_ipv4"]: - end_state["hwtacacs server cfg ipv4"] = hwtacacs_server_ipv4_result[ - "hwtacacs_server_cfg_ipv4"] - if hwtacacs_server_ipv6 and hwtacacs_server_ipv6_result["hwtacacs_server_cfg_ipv6"]: - end_state["hwtacacs server cfg ipv6"] = hwtacacs_server_ipv6_result[ - "hwtacacs_server_cfg_ipv6"] - if hwtacacs_server_host_name and hwtacacs_host_name_result["hwtacacs_server_name_cfg"]: - end_state["hwtacacs server name cfg"] = hwtacacs_host_name_result[ - "hwtacacs_server_name_cfg"] - - results = dict() - results['proposed'] = proposed - results['existing'] = existing - results['changed'] = changed - results['end_state'] = end_state - results['updates'] = updates - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_acl.py b/plugins/modules/network/cloudengine/ce_acl.py deleted file mode 100644 index 4b82418bb2..0000000000 --- a/plugins/modules/network/cloudengine/ce_acl.py +++ /dev/null @@ -1,1004 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_acl -short_description: Manages base ACL configuration on HUAWEI CloudEngine switches. -description: - - Manages base ACL configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent','delete_acl'] - acl_name: - description: - - ACL number or name. - For a numbered rule group, the value ranging from 2000 to 2999 indicates a basic ACL. - For a named rule group, the value is a string of 1 to 32 case-sensitive characters starting - with a letter, spaces not supported. - required: true - acl_num: - description: - - ACL number. - The value is an integer ranging from 2000 to 2999. - acl_step: - description: - - ACL step. - The value is an integer ranging from 1 to 20. The default value is 5. - acl_description: - description: - - ACL description. - The value is a string of 1 to 127 characters. - rule_name: - description: - - Name of a basic ACL rule. - The value is a string of 1 to 32 characters. - The value is case-insensitive, and cannot contain spaces or begin with an underscore (_). - rule_id: - description: - - ID of a basic ACL rule in configuration mode. - The value is an integer ranging from 0 to 4294967294. - rule_action: - description: - - Matching mode of basic ACL rules. - choices: ['permit','deny'] - source_ip: - description: - - Source IP address. - The value is a string of 0 to 255 characters.The default value is 0.0.0.0. - The value is in dotted decimal notation. - src_mask: - description: - - Mask of a source IP address. - The value is an integer ranging from 1 to 32. - frag_type: - description: - - Type of packet fragmentation. - choices: ['fragment', 'clear_fragment'] - vrf_name: - description: - - VPN instance name. - The value is a string of 1 to 31 characters.The default value is _public_. - time_range: - description: - - Name of a time range in which an ACL rule takes effect. - The value is a string of 1 to 32 characters. - The value is case-insensitive, and cannot contain spaces. The name must start with an uppercase - or lowercase letter. In addition, the word "all" cannot be specified as a time range name. - rule_description: - description: - - Description about an ACL rule. - The value is a string of 1 to 127 characters. - log_flag: - description: - - Flag of logging matched data packets. - type: bool - default: 'no' -''' - -EXAMPLES = ''' - -- name: CloudEngine acl test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config ACL" - ce_acl: - state: present - acl_name: 2200 - provider: "{{ cli }}" - - - name: "Undo ACL" - ce_acl: - state: delete_acl - acl_name: 2200 - provider: "{{ cli }}" - - - name: "Config ACL base rule" - ce_acl: - state: present - acl_name: 2200 - rule_name: test_rule - rule_id: 111 - rule_action: permit - source_ip: 10.10.10.10 - src_mask: 24 - frag_type: fragment - time_range: wdz_acl_time - provider: "{{ cli }}" - - - name: "undo ACL base rule" - ce_acl: - state: absent - acl_name: 2200 - rule_name: test_rule - rule_id: 111 - rule_action: permit - source_ip: 10.10.10.10 - src_mask: 24 - frag_type: fragment - time_range: wdz_acl_time - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"acl_name": "test", "state": "delete_acl"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"aclNumOrName": "test", "aclType": "Basic"} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {} -updates: - description: command sent to the device - returned: always - type: list - sample: ["undo acl name test"] -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - -# get acl -CE_GET_ACL_HEADER = """ - - - - - -""" -CE_GET_ACL_TAIL = """ - - - - -""" -# merge acl -CE_MERGE_ACL_HEADER = """ - - - - - %s -""" -CE_MERGE_ACL_TAIL = """ - - - - -""" -# delete acl -CE_DELETE_ACL_HEADER = """ - - - - - %s -""" -CE_DELETE_ACL_TAIL = """ - - - - -""" - -# get acl base rule -CE_GET_ACL_BASE_RULE_HEADER = """ - - - - - %s - - - -""" -CE_GET_ACL_BASE_RULE_TAIL = """ - - - - - - -""" -# merge acl base rule -CE_MERGE_ACL_BASE_RULE_HEADER = """ - - - - - %s - - - %s -""" -CE_MERGE_ACL_BASE_RULE_TAIL = """ - - - - - - -""" -# delete acl base rule -CE_DELETE_ACL_BASE_RULE_HEADER = """ - - - - - %s - - - %s -""" -CE_DELETE_ACL_BASE_RULE_TAIL = """ - - - - - - -""" - - -class BaseAcl(object): - """ Manages base acl configuration """ - - def __init__(self, **kwargs): - """ Class init """ - - # argument spec - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # module args - self.state = self.module.params['state'] - self.acl_name = self.module.params['acl_name'] or None - self.acl_num = self.module.params['acl_num'] or None - self.acl_type = None - self.acl_step = self.module.params['acl_step'] or None - self.acl_description = self.module.params['acl_description'] or None - self.rule_name = self.module.params['rule_name'] or None - self.rule_id = self.module.params['rule_id'] or None - self.rule_action = self.module.params['rule_action'] or None - self.source_ip = self.module.params['source_ip'] or None - self.src_mask = self.module.params['src_mask'] or None - self.src_wild = None - self.frag_type = self.module.params['frag_type'] or None - self.vrf_name = self.module.params['vrf_name'] or None - self.time_range = self.module.params['time_range'] or None - self.rule_description = self.module.params['rule_description'] or None - self.log_flag = self.module.params['log_flag'] - - # cur config - self.cur_acl_cfg = dict() - self.cur_base_rule_cfg = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def netconf_get_config(self, conf_str): - """ Get configure by netconf """ - - xml_str = get_nc_config(self.module, conf_str) - - return xml_str - - def netconf_set_config(self, conf_str): - """ Set configure by netconf """ - - xml_str = set_nc_config(self.module, conf_str) - - return xml_str - - def get_wildcard_mask(self): - """ convert mask length to ip address wildcard mask, i.e. 24 to 0.0.0.255 """ - - mask_int = ["255"] * 4 - value = int(self.src_mask) - - if value > 32: - self.module.fail_json(msg='Error: IPv4 ipaddress mask length is invalid.') - if value < 8: - mask_int[0] = str(int(~(0xFF << (8 - value % 8)) & 0xFF)) - if value >= 8: - mask_int[0] = '0' - mask_int[1] = str(int(~(0xFF << (16 - (value % 16))) & 0xFF)) - if value >= 16: - mask_int[1] = '0' - mask_int[2] = str(int(~(0xFF << (24 - (value % 24))) & 0xFF)) - if value >= 24: - mask_int[2] = '0' - mask_int[3] = str(int(~(0xFF << (32 - (value % 32))) & 0xFF)) - if value == 32: - mask_int[3] = '0' - - return '.'.join(mask_int) - - def check_acl_args(self): - """ Check acl invalid args """ - - need_cfg = False - find_flag = False - self.cur_acl_cfg["acl_info"] = [] - - if self.acl_name: - - if self.acl_name.isdigit(): - if int(self.acl_name) < 2000 or int(self.acl_name) > 2999: - self.module.fail_json( - msg='Error: The value of acl_name is out of [2000-2999] for base ACL.') - - if self.acl_num: - self.module.fail_json( - msg='Error: The acl_name is digit, so should not input acl_num at the same time.') - else: - - self.acl_type = "Basic" - - if len(self.acl_name) < 1 or len(self.acl_name) > 32: - self.module.fail_json( - msg='Error: The len of acl_name is out of [1 - 32].') - - if self.state == "present": - if not self.acl_num and not self.acl_type and not self.rule_name: - self.module.fail_json( - msg='Error: Please input acl_num or acl_type when config ACL.') - - if self.acl_num: - if self.acl_num.isdigit(): - if int(self.acl_num) < 2000 or int(self.acl_num) > 2999: - self.module.fail_json( - msg='Error: The value of acl_name is out of [2000-2999] for base ACL.') - else: - self.module.fail_json( - msg='Error: The acl_num is not digit.') - - if self.acl_step: - if self.acl_step.isdigit(): - if int(self.acl_step) < 1 or int(self.acl_step) > 20: - self.module.fail_json( - msg='Error: The value of acl_step is out of [1 - 20].') - else: - self.module.fail_json( - msg='Error: The acl_step is not digit.') - - if self.acl_description: - if len(self.acl_description) < 1 or len(self.acl_description) > 127: - self.module.fail_json( - msg='Error: The len of acl_description is out of [1 - 127].') - - conf_str = CE_GET_ACL_HEADER - - if self.acl_type: - conf_str += "" - if self.acl_num or self.acl_name.isdigit(): - conf_str += "" - if self.acl_step: - conf_str += "" - if self.acl_description: - conf_str += "" - - conf_str += CE_GET_ACL_TAIL - recv_xml = self.netconf_get_config(conf_str=conf_str) - - if "" in recv_xml: - find_flag = False - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # parse acl - acl_info = root.findall( - "acl/aclGroups/aclGroup") - if acl_info: - for tmp in acl_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["aclNumOrName", "aclType", "aclNumber", "aclStep", "aclDescription"]: - tmp_dict[site.tag] = site.text - - self.cur_acl_cfg["acl_info"].append(tmp_dict) - - if self.cur_acl_cfg["acl_info"]: - find_list = list() - for tmp in self.cur_acl_cfg["acl_info"]: - cur_cfg_dict = dict() - exist_cfg_dict = dict() - if self.acl_name: - if self.acl_name.isdigit() and tmp.get("aclNumber"): - cur_cfg_dict["aclNumber"] = self.acl_name - exist_cfg_dict["aclNumber"] = tmp.get("aclNumber") - else: - cur_cfg_dict["aclNumOrName"] = self.acl_name - exist_cfg_dict["aclNumOrName"] = tmp.get("aclNumOrName") - if self.acl_type: - cur_cfg_dict["aclType"] = self.acl_type - exist_cfg_dict["aclType"] = tmp.get("aclType") - if self.acl_num: - cur_cfg_dict["aclNumber"] = self.acl_num - exist_cfg_dict["aclNumber"] = tmp.get("aclNumber") - if self.acl_step: - cur_cfg_dict["aclStep"] = self.acl_step - exist_cfg_dict["aclStep"] = tmp.get("aclStep") - if self.acl_description: - cur_cfg_dict["aclDescription"] = self.acl_description - exist_cfg_dict["aclDescription"] = tmp.get("aclDescription") - - if cur_cfg_dict == exist_cfg_dict: - find_bool = True - else: - find_bool = False - find_list.append(find_bool) - - for mem in find_list: - if mem: - find_flag = True - break - else: - find_flag = False - - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - elif self.state == "delete_acl": - need_cfg = bool(find_flag) - else: - need_cfg = False - - self.cur_acl_cfg["need_cfg"] = need_cfg - - def check_base_rule_args(self): - """ Check base rule invalid args """ - - need_cfg = False - find_flag = False - self.cur_base_rule_cfg["base_rule_info"] = [] - - if self.acl_name: - - if self.state == "absent": - if not self.rule_name: - self.module.fail_json( - msg='Error: Please input rule_name when state is absent.') - - # config rule - if self.rule_name: - if len(self.rule_name) < 1 or len(self.rule_name) > 32: - self.module.fail_json( - msg='Error: The len of rule_name is out of [1 - 32].') - - if self.state != "delete_acl" and not self.rule_id: - self.module.fail_json( - msg='Error: Please input rule_id.') - - if self.rule_id: - if self.rule_id.isdigit(): - if int(self.rule_id) < 0 or int(self.rule_id) > 4294967294: - self.module.fail_json( - msg='Error: The value of rule_id is out of [0 - 4294967294].') - else: - self.module.fail_json( - msg='Error: The rule_id is not digit.') - - if self.source_ip: - if not check_ip_addr(self.source_ip): - self.module.fail_json( - msg='Error: The source_ip %s is invalid.' % self.source_ip) - if not self.src_mask: - self.module.fail_json( - msg='Error: Please input src_mask.') - - if self.src_mask: - if self.src_mask.isdigit(): - if int(self.src_mask) < 1 or int(self.src_mask) > 32: - self.module.fail_json( - msg='Error: The src_mask is out of [1 - 32].') - self.src_wild = self.get_wildcard_mask() - else: - self.module.fail_json( - msg='Error: The src_mask is not digit.') - - if self.vrf_name: - if len(self.vrf_name) < 1 or len(self.vrf_name) > 31: - self.module.fail_json( - msg='Error: The len of vrf_name is out of [1 - 31].') - - if self.time_range: - if len(self.time_range) < 1 or len(self.time_range) > 32: - self.module.fail_json( - msg='Error: The len of time_range is out of [1 - 32].') - - if self.rule_description: - if len(self.rule_description) < 1 or len(self.rule_description) > 127: - self.module.fail_json( - msg='Error: The len of rule_description is out of [1 - 127].') - - if self.state != "delete_acl" and not self.rule_id: - self.module.fail_json( - msg='Error: Please input rule_id.') - - conf_str = CE_GET_ACL_BASE_RULE_HEADER % self.acl_name - - if self.rule_id: - conf_str += "" - if self.rule_action: - conf_str += "" - if self.source_ip: - conf_str += "" - if self.src_wild: - conf_str += "" - if self.frag_type: - conf_str += "" - if self.vrf_name: - conf_str += "" - if self.time_range: - conf_str += "" - if self.rule_description: - conf_str += "" - conf_str += "" - - conf_str += CE_GET_ACL_BASE_RULE_TAIL - recv_xml = self.netconf_get_config(conf_str=conf_str) - - if "" in recv_xml: - find_flag = False - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - - # parse base rule - base_rule_info = root.findall( - "acl/aclGroups/aclGroup/aclRuleBas4s/aclRuleBas4") - if base_rule_info: - for tmp in base_rule_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["aclRuleName", "aclRuleID", "aclAction", "aclSourceIp", "aclSrcWild", - "aclFragType", "vrfName", "aclTimeName", "aclRuleDescription", - "aclLogFlag"]: - tmp_dict[site.tag] = site.text - - self.cur_base_rule_cfg[ - "base_rule_info"].append(tmp_dict) - - if self.cur_base_rule_cfg["base_rule_info"]: - for tmp in self.cur_base_rule_cfg["base_rule_info"]: - find_flag = True - - if self.rule_name and tmp.get("aclRuleName") != self.rule_name: - find_flag = False - if self.rule_id and tmp.get("aclRuleID") != self.rule_id: - find_flag = False - if self.rule_action and tmp.get("aclAction") != self.rule_action: - find_flag = False - if self.source_ip: - tmp_src_ip = self.source_ip.split(".") - tmp_src_wild = self.src_wild.split(".") - tmp_addr_item = [] - for idx in range(4): - item1 = 255 - int(tmp_src_wild[idx]) - item2 = item1 & int(tmp_src_ip[idx]) - tmp_addr_item.append(item2) - tmp_addr = "%s.%s.%s.%s" % (tmp_addr_item[0], tmp_addr_item[1], - tmp_addr_item[2], tmp_addr_item[3]) - if tmp_addr != tmp.get("aclSourceIp"): - find_flag = False - if self.src_wild and tmp.get("aclSrcWild") != self.src_wild: - find_flag = False - frag_type = "clear_fragment" if tmp.get("aclFragType") is None else tmp.get("aclFragType") - if self.frag_type and frag_type != self.frag_type: - find_flag = False - if self.vrf_name and tmp.get("vrfName") != self.vrf_name: - find_flag = False - if self.time_range and tmp.get("aclTimeName") != self.time_range: - find_flag = False - if self.rule_description and tmp.get("aclRuleDescription") != self.rule_description: - find_flag = False - if tmp.get("aclLogFlag") != str(self.log_flag).lower(): - find_flag = False - - if find_flag: - break - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - elif self.state == "absent": - need_cfg = bool(find_flag) - else: - need_cfg = False - - self.cur_base_rule_cfg["need_cfg"] = need_cfg - - def get_proposed(self): - """ Get proposed state """ - - self.proposed["state"] = self.state - - if self.acl_name: - self.proposed["acl_name"] = self.acl_name - if self.acl_num: - self.proposed["acl_num"] = self.acl_num - if self.acl_step: - self.proposed["acl_step"] = self.acl_step - if self.acl_description: - self.proposed["acl_description"] = self.acl_description - if self.rule_name: - self.proposed["rule_name"] = self.rule_name - if self.rule_id: - self.proposed["rule_id"] = self.rule_id - if self.rule_action: - self.proposed["rule_action"] = self.rule_action - if self.source_ip: - self.proposed["source_ip"] = self.source_ip - if self.src_mask: - self.proposed["src_mask"] = self.src_mask - if self.frag_type: - self.proposed["frag_type"] = self.frag_type - if self.vrf_name: - self.proposed["vrf_name"] = self.vrf_name - if self.time_range: - self.proposed["time_range"] = self.time_range - if self.rule_description: - self.proposed["rule_description"] = self.rule_description - if self.log_flag: - self.proposed["log_flag"] = self.log_flag - - def get_existing(self): - """ Get existing state """ - - self.existing["acl_info"] = self.cur_acl_cfg["acl_info"] - self.existing["base_rule_info"] = self.cur_base_rule_cfg[ - "base_rule_info"] - - def get_end_state(self): - """ Get end state """ - - self.check_acl_args() - self.end_state["acl_info"] = self.cur_acl_cfg["acl_info"] - - self.check_base_rule_args() - self.end_state["base_rule_info"] = self.cur_base_rule_cfg[ - "base_rule_info"] - - def merge_acl(self): - """ Merge acl operation """ - - conf_str = CE_MERGE_ACL_HEADER % self.acl_name - - if self.acl_type: - conf_str += "%s" % self.acl_type - if self.acl_num: - conf_str += "%s" % self.acl_num - if self.acl_step: - conf_str += "%s" % self.acl_step - if self.acl_description: - conf_str += "%s" % self.acl_description - - conf_str += CE_MERGE_ACL_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge acl failed.') - - if self.acl_name.isdigit(): - cmd = "acl number %s" % self.acl_name - else: - if self.acl_type and not self.acl_num: - cmd = "acl name %s %s" % (self.acl_name, self.acl_type.lower()) - elif self.acl_type and self.acl_num: - cmd = "acl name %s number %s" % (self.acl_name, self.acl_num) - elif not self.acl_type and self.acl_num: - cmd = "acl name %s number %s" % (self.acl_name, self.acl_num) - self.updates_cmd.append(cmd) - - if self.acl_description: - cmd = "description %s" % self.acl_description - self.updates_cmd.append(cmd) - - if self.acl_step: - cmd = "step %s" % self.acl_step - self.updates_cmd.append(cmd) - - self.changed = True - - def delete_acl(self): - """ Delete acl operation """ - - conf_str = CE_DELETE_ACL_HEADER % self.acl_name - - if self.acl_type: - conf_str += "%s" % self.acl_type - if self.acl_num: - conf_str += "%s" % self.acl_num - if self.acl_step: - conf_str += "%s" % self.acl_step - if self.acl_description: - conf_str += "%s" % self.acl_description - - conf_str += CE_DELETE_ACL_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Delete acl failed.') - - if self.acl_description: - cmd = "undo description" - self.updates_cmd.append(cmd) - - if self.acl_step: - cmd = "undo step" - self.updates_cmd.append(cmd) - - if self.acl_name.isdigit(): - cmd = "undo acl number %s" % self.acl_name - else: - cmd = "undo acl name %s" % self.acl_name - self.updates_cmd.append(cmd) - - self.changed = True - - def merge_base_rule(self): - """ Merge base rule operation """ - - conf_str = CE_MERGE_ACL_BASE_RULE_HEADER % ( - self.acl_name, self.rule_name) - - if self.rule_id: - conf_str += "%s" % self.rule_id - if self.rule_action: - conf_str += "%s" % self.rule_action - if self.source_ip: - conf_str += "%s" % self.source_ip - if self.src_wild: - conf_str += "%s" % self.src_wild - if self.frag_type: - conf_str += "%s" % self.frag_type - if self.vrf_name: - conf_str += "%s" % self.vrf_name - if self.time_range: - conf_str += "%s" % self.time_range - if self.rule_description: - conf_str += "%s" % self.rule_description - conf_str += "%s" % str(self.log_flag).lower() - - conf_str += CE_MERGE_ACL_BASE_RULE_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge acl base rule failed.') - - if self.rule_action: - cmd = "rule" - if self.rule_id: - cmd += " %s" % self.rule_id - cmd += " %s" % self.rule_action - if self.frag_type == "fragment": - cmd += " fragment-type fragment" - if self.source_ip and self.src_wild: - cmd += " source %s %s" % (self.source_ip, self.src_wild) - if self.time_range: - cmd += " time-range %s" % self.time_range - if self.vrf_name: - cmd += " vpn-instance %s" % self.vrf_name - if self.log_flag: - cmd += " logging" - self.updates_cmd.append(cmd) - - if self.rule_description: - cmd = "rule %s description %s" % ( - self.rule_id, self.rule_description) - self.updates_cmd.append(cmd) - - self.changed = True - - def delete_base_rule(self): - """ Delete base rule operation """ - - conf_str = CE_DELETE_ACL_BASE_RULE_HEADER % ( - self.acl_name, self.rule_name) - - if self.rule_id: - conf_str += "%s" % self.rule_id - if self.rule_action: - conf_str += "%s" % self.rule_action - if self.source_ip: - conf_str += "%s" % self.source_ip - if self.src_wild: - conf_str += "%s" % self.src_wild - if self.frag_type: - conf_str += "%s" % self.frag_type - if self.vrf_name: - conf_str += "%s" % self.vrf_name - if self.time_range: - conf_str += "%s" % self.time_range - if self.rule_description: - conf_str += "%s" % self.rule_description - conf_str += "%s" % str(self.log_flag).lower() - - conf_str += CE_DELETE_ACL_BASE_RULE_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Delete acl base rule failed.') - - if self.rule_description: - if self.acl_name.isdigit(): - cmd = "acl number %s" % self.acl_name - else: - cmd = "acl name %s" % self.acl_name - self.updates_cmd.append(cmd) - - cmd = "undo rule %s description" % self.rule_id - self.updates_cmd.append(cmd) - - if self.rule_id: - if self.acl_name.isdigit(): - cmd = "acl number %s" % self.acl_name - else: - cmd = "acl name %s" % self.acl_name - self.updates_cmd.append(cmd) - - cmd = "undo rule %s" % self.rule_id - self.updates_cmd.append(cmd) - elif self.rule_action: - if self.acl_name.isdigit(): - cmd = "acl number %s" % self.acl_name - else: - cmd = "acl name %s" % self.acl_name - self.updates_cmd.append(cmd) - - cmd = "undo rule" - cmd += " %s" % self.rule_action - if self.frag_type == "fragment": - cmd += " fragment-type fragment" - if self.source_ip and self.src_wild: - cmd += " source %s %s" % (self.source_ip, self.src_wild) - if self.time_range: - cmd += " time-range %s" % self.time_range - if self.vrf_name: - cmd += " vpn-instance %s" % self.vrf_name - if self.log_flag: - cmd += " logging" - self.updates_cmd.append(cmd) - - self.changed = True - - def work(self): - """ Main work function """ - - self.check_acl_args() - self.check_base_rule_args() - self.get_proposed() - self.get_existing() - - if self.state == "present": - if self.cur_acl_cfg["need_cfg"]: - self.merge_acl() - if self.cur_base_rule_cfg["need_cfg"]: - self.merge_base_rule() - - elif self.state == "absent": - if self.cur_base_rule_cfg["need_cfg"]: - self.delete_base_rule() - - elif self.state == "delete_acl": - if self.cur_acl_cfg["need_cfg"]: - self.delete_acl() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent', - 'delete_acl'], default='present'), - acl_name=dict(type='str', required=True), - acl_num=dict(type='str'), - acl_step=dict(type='str'), - acl_description=dict(type='str'), - rule_name=dict(type='str'), - rule_id=dict(type='str'), - rule_action=dict(choices=['permit', 'deny']), - source_ip=dict(type='str'), - src_mask=dict(type='str'), - frag_type=dict(choices=['fragment', 'clear_fragment']), - vrf_name=dict(type='str'), - time_range=dict(type='str'), - rule_description=dict(type='str'), - log_flag=dict(required=False, default=False, type='bool') - ) - - argument_spec.update(ce_argument_spec) - module = BaseAcl(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_acl_advance.py b/plugins/modules/network/cloudengine/ce_acl_advance.py deleted file mode 100644 index 869963bda0..0000000000 --- a/plugins/modules/network/cloudengine/ce_acl_advance.py +++ /dev/null @@ -1,1750 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_acl_advance -short_description: Manages advanced ACL configuration on HUAWEI CloudEngine switches. -description: - - Manages advanced ACL configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - required: false - default: present - choices: ['present','absent','delete_acl'] - acl_name: - description: - - ACL number or name. - For a numbered rule group, the value ranging from 3000 to 3999 indicates a advance ACL. - For a named rule group, the value is a string of 1 to 32 case-sensitive characters starting - with a letter, spaces not supported. - required: true - acl_num: - description: - - ACL number. - The value is an integer ranging from 3000 to 3999. - acl_step: - description: - - ACL step. - The value is an integer ranging from 1 to 20. The default value is 5. - acl_description: - description: - - ACL description. - The value is a string of 1 to 127 characters. - rule_name: - description: - - Name of a basic ACL rule. - The value is a string of 1 to 32 characters. - rule_id: - description: - - ID of a basic ACL rule in configuration mode. - The value is an integer ranging from 0 to 4294967294. - rule_action: - description: - - Matching mode of basic ACL rules. - choices: ['permit','deny'] - protocol: - description: - - Protocol type. - choices: ['ip', 'icmp', 'igmp', 'ipinip', 'tcp', 'udp', 'gre', 'ospf'] - source_ip: - description: - - Source IP address. - The value is a string of 0 to 255 characters.The default value is 0.0.0.0. - The value is in dotted decimal notation. - src_mask: - description: - - Source IP address mask. - The value is an integer ranging from 1 to 32. - src_pool_name: - description: - - Name of a source pool. - The value is a string of 1 to 32 characters. - dest_ip: - description: - - Destination IP address. - The value is a string of 0 to 255 characters.The default value is 0.0.0.0. - The value is in dotted decimal notation. - dest_mask: - description: - - Destination IP address mask. - The value is an integer ranging from 1 to 32. - dest_pool_name: - description: - - Name of a destination pool. - The value is a string of 1 to 32 characters. - src_port_op: - description: - - Range type of the source port. - choices: ['lt','eq', 'gt', 'range'] - src_port_begin: - description: - - Start port number of the source port. - The value is an integer ranging from 0 to 65535. - src_port_end: - description: - - End port number of the source port. - The value is an integer ranging from 0 to 65535. - src_port_pool_name: - description: - - Name of a source port pool. - The value is a string of 1 to 32 characters. - dest_port_op: - description: - - Range type of the destination port. - choices: ['lt','eq', 'gt', 'range'] - dest_port_begin: - description: - - Start port number of the destination port. - The value is an integer ranging from 0 to 65535. - dest_port_end: - description: - - End port number of the destination port. - The value is an integer ranging from 0 to 65535. - dest_port_pool_name: - description: - - Name of a destination port pool. - The value is a string of 1 to 32 characters. - frag_type: - description: - - Type of packet fragmentation. - choices: ['fragment', 'clear_fragment'] - precedence: - description: - - Data packets can be filtered based on the priority field. - The value is an integer ranging from 0 to 7. - tos: - description: - - ToS value on which data packet filtering is based. - The value is an integer ranging from 0 to 15. - dscp: - description: - - Differentiated Services Code Point. - The value is an integer ranging from 0 to 63. - icmp_name: - description: - - ICMP name. - choices: ['unconfiged', 'echo', 'echo-reply', 'fragmentneed-DFset', 'host-redirect', - 'host-tos-redirect', 'host-unreachable', 'information-reply', 'information-request', - 'net-redirect', 'net-tos-redirect', 'net-unreachable', 'parameter-problem', - 'port-unreachable', 'protocol-unreachable', 'reassembly-timeout', 'source-quench', - 'source-route-failed', 'timestamp-reply', 'timestamp-request', 'ttl-exceeded', - 'address-mask-reply', 'address-mask-request', 'custom'] - icmp_type: - description: - - ICMP type. This parameter is available only when the packet protocol is ICMP. - The value is an integer ranging from 0 to 255. - icmp_code: - description: - - ICMP message code. Data packets can be filtered based on the ICMP message code. - The value is an integer ranging from 0 to 255. - ttl_expired: - description: - - Whether TTL Expired is matched, with the TTL value of 1. - type: bool - default: 'no' - vrf_name: - description: - - VPN instance name. - The value is a string of 1 to 31 characters.The default value is _public_. - syn_flag: - description: - - TCP flag value. - The value is an integer ranging from 0 to 63. - tcp_flag_mask: - description: - - TCP flag mask value. - The value is an integer ranging from 0 to 63. - established: - description: - - Match established connections. - type: bool - default: 'no' - time_range: - description: - - Name of a time range in which an ACL rule takes effect. - rule_description: - description: - - Description about an ACL rule. - igmp_type: - description: - - Internet Group Management Protocol. - choices: ['host-query', 'mrouter-adver', 'mrouter-solic', 'mrouter-termi', 'mtrace-resp', 'mtrace-route', - 'v1host-report', 'v2host-report', 'v2leave-group', 'v3host-report'] - log_flag: - description: - - Flag of logging matched data packets. - type: bool - default: 'no' -''' - -EXAMPLES = ''' - -- name: CloudEngine advance acl test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config ACL" - ce_acl_advance: - state: present - acl_name: 3200 - provider: "{{ cli }}" - - - name: "Undo ACL" - ce_acl_advance: - state: delete_acl - acl_name: 3200 - provider: "{{ cli }}" - - - name: "Config ACL advance rule" - ce_acl_advance: - state: present - acl_name: test - rule_name: test_rule - rule_id: 111 - rule_action: permit - protocol: tcp - source_ip: 10.10.10.10 - src_mask: 24 - frag_type: fragment - provider: "{{ cli }}" - - - name: "Undo ACL advance rule" - ce_acl_advance: - state: absent - acl_name: test - rule_name: test_rule - rule_id: 111 - rule_action: permit - protocol: tcp - source_ip: 10.10.10.10 - src_mask: 24 - frag_type: fragment - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"acl_name": "test", "state": "delete_acl"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"aclNumOrName": "test", "aclType": "Advance"} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {} -updates: - description: command sent to the device - returned: always - type: list - sample: ["undo acl name test"] -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - - -# get acl -CE_GET_ACL_HEADER = """ - - - - - -""" -CE_GET_ACL_TAIL = """ - - - - -""" -# merge acl -CE_MERGE_ACL_HEADER = """ - - - - - %s -""" -CE_MERGE_ACL_TAIL = """ - - - - -""" -# delete acl -CE_DELETE_ACL_HEADER = """ - - - - - %s -""" -CE_DELETE_ACL_TAIL = """ - - - - -""" - -# get acl advance rule -CE_GET_ACL_ADVANCE_RULE_HEADER = """ - - - - - %s - - - -""" -CE_GET_ACL_ADVANCE_RULE_TAIL = """ - - - - - - -""" -# merge acl advance rule -CE_MERGE_ACL_ADVANCE_RULE_HEADER = """ - - - - - %s - - - %s -""" -CE_MERGE_ACL_ADVANCE_RULE_TAIL = """ - - - - - - -""" -# delete acl advance rule -CE_DELETE_ACL_ADVANCE_RULE_HEADER = """ - - - - - %s - - - %s -""" -CE_DELETE_ACL_ADVANCE_RULE_TAIL = """ - - - - - - -""" - - -PROTOCOL_NUM = {"ip": "0", - "icmp": "1", - "igmp": "2", - "ipinip": "4", - "tcp": "6", - "udp": "17", - "gre": "47", - "ospf": "89"} - -IGMP_TYPE_NUM = {"host-query": "17", - "mrouter-adver": "48", - "mrouter-solic": "49", - "mrouter-termi": "50", - "mtrace-resp": "30", - "mtrace-route": "31", - "v1host-report": "18", - "v2host-report": "22", - "v2leave-group": "23", - "v3host-report": "34"} - - -def get_wildcard_mask(mask): - """ convert mask length to ip address wildcard mask, i.e. 24 to 0.0.0.255 """ - - mask_int = ["255"] * 4 - value = int(mask) - - if value > 32: - return None - if value < 8: - mask_int[0] = str(int(~(0xFF << (8 - value % 8)) & 0xFF)) - if value >= 8: - mask_int[0] = '0' - mask_int[1] = str(int(~(0xFF << (16 - (value % 16))) & 0xFF)) - if value >= 16: - mask_int[1] = '0' - mask_int[2] = str(int(~(0xFF << (24 - (value % 24))) & 0xFF)) - if value >= 24: - mask_int[2] = '0' - mask_int[3] = str(int(~(0xFF << (32 - (value % 32))) & 0xFF)) - if value == 32: - mask_int[3] = '0' - - return '.'.join(mask_int) - - -class AdvanceAcl(object): - """ Manages advance acl configuration """ - - def __init__(self, **kwargs): - """ Class init """ - - # argument spec - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # module args - self.state = self.module.params['state'] - self.acl_name = self.module.params['acl_name'] or None - self.acl_num = self.module.params['acl_num'] or None - self.acl_type = None - self.acl_step = self.module.params['acl_step'] or None - self.acl_description = self.module.params['acl_description'] or None - self.rule_name = self.module.params['rule_name'] or None - self.rule_id = self.module.params['rule_id'] or None - self.rule_action = self.module.params['rule_action'] or None - self.protocol = self.module.params['protocol'] or None - self.protocol_num = None - self.source_ip = self.module.params['source_ip'] or None - self.src_mask = self.module.params['src_mask'] or None - self.src_wild = None - self.src_pool_name = self.module.params['src_pool_name'] or None - self.dest_ip = self.module.params['dest_ip'] or None - self.dest_mask = self.module.params['dest_mask'] or None - self.dest_wild = None - self.dest_pool_name = self.module.params['dest_pool_name'] or None - self.src_port_op = self.module.params['src_port_op'] or None - self.src_port_begin = self.module.params['src_port_begin'] or None - self.src_port_end = self.module.params['src_port_end'] or None - self.src_port_pool_name = self.module.params[ - 'src_port_pool_name'] or None - self.dest_port_op = self.module.params['dest_port_op'] or None - self.dest_port_begin = self.module.params['dest_port_begin'] or None - self.dest_port_end = self.module.params['dest_port_end'] or None - self.dest_port_pool_name = self.module.params[ - 'dest_port_pool_name'] or None - self.frag_type = self.module.params['frag_type'] or None - self.precedence = self.module.params['precedence'] or None - self.tos = self.module.params['tos'] or None - self.dscp = self.module.params['dscp'] or None - self.icmp_name = self.module.params['icmp_name'] or None - self.icmp_type = self.module.params['icmp_type'] or None - self.icmp_code = self.module.params['icmp_code'] or None - self.ttl_expired = self.module.params['ttl_expired'] - self.vrf_name = self.module.params['vrf_name'] or None - self.syn_flag = self.module.params['syn_flag'] or None - self.tcp_flag_mask = self.module.params['tcp_flag_mask'] or None - self.established = self.module.params['established'] - self.time_range = self.module.params['time_range'] or None - self.rule_description = self.module.params['rule_description'] or None - self.igmp_type = self.module.params['igmp_type'] or None - self.igmp_type_num = None - self.log_flag = self.module.params['log_flag'] - - self.precedence_name = dict() - self.precedence_name["0"] = "routine" - self.precedence_name["1"] = "priority" - self.precedence_name["2"] = "immediate" - self.precedence_name["3"] = "flash" - self.precedence_name["4"] = "flash-override" - self.precedence_name["5"] = "critical" - self.precedence_name["6"] = "internet" - self.precedence_name["7"] = "network" - - # cur config - self.cur_acl_cfg = dict() - self.cur_advance_rule_cfg = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def netconf_get_config(self, conf_str): - """ Get configure by netconf """ - - xml_str = get_nc_config(self.module, conf_str) - - return xml_str - - def netconf_set_config(self, conf_str): - """ Set configure by netconf """ - - xml_str = set_nc_config(self.module, conf_str) - - return xml_str - - def get_protocol_num(self): - """ Get protocol num by name """ - - if self.protocol: - self.protocol_num = PROTOCOL_NUM.get(self.protocol) - - def get_igmp_type_num(self): - """ Get igmp type num by type """ - - if self.igmp_type: - self.igmp_type_num = IGMP_TYPE_NUM.get(self.igmp_type) - - def check_acl_args(self): - """ Check acl invalid args """ - - need_cfg = False - find_flag = False - self.cur_acl_cfg["acl_info"] = [] - - if self.acl_name: - - if self.acl_name.isdigit(): - if int(self.acl_name) < 3000 or int(self.acl_name) > 3999: - self.module.fail_json( - msg='Error: The value of acl_name is out of [3000-3999] for advance ACL.') - - if self.acl_num: - self.module.fail_json( - msg='Error: The acl_name is digit, so should not input acl_num at the same time.') - else: - - self.acl_type = "Advance" - - if len(self.acl_name) < 1 or len(self.acl_name) > 32: - self.module.fail_json( - msg='Error: The len of acl_name is out of [1 - 32].') - - if self.state == "present": - if not self.acl_num and not self.acl_type and not self.rule_name: - self.module.fail_json( - msg='Error: Please input acl_num or acl_type when config ACL.') - - if self.acl_num: - if self.acl_num.isdigit(): - if int(self.acl_num) < 3000 or int(self.acl_num) > 3999: - self.module.fail_json( - msg='Error: The value of acl_name is out of [3000-3999] for advance ACL.') - else: - self.module.fail_json( - msg='Error: The acl_num is not digit.') - - if self.acl_step: - if self.acl_step.isdigit(): - if int(self.acl_step) < 1 or int(self.acl_step) > 20: - self.module.fail_json( - msg='Error: The value of acl_step is out of [1 - 20].') - else: - self.module.fail_json( - msg='Error: The acl_step is not digit.') - - if self.acl_description: - if len(self.acl_description) < 1 or len(self.acl_description) > 127: - self.module.fail_json( - msg='Error: The len of acl_description is out of [1 - 127].') - - conf_str = CE_GET_ACL_HEADER - - if self.acl_type: - conf_str += "" - if self.acl_num or self.acl_name.isdigit(): - conf_str += "" - if self.acl_step: - conf_str += "" - if self.acl_description: - conf_str += "" - - conf_str += CE_GET_ACL_TAIL - recv_xml = self.netconf_get_config(conf_str=conf_str) - - if "" in recv_xml: - find_flag = False - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - - # parse acl - acl_info = root.findall( - "acl/aclGroups/aclGroup") - if acl_info: - for tmp in acl_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["aclNumOrName", "aclType", "aclNumber", "aclStep", "aclDescription"]: - tmp_dict[site.tag] = site.text - - self.cur_acl_cfg["acl_info"].append(tmp_dict) - - if self.cur_acl_cfg["acl_info"]: - find_list = list() - for tmp in self.cur_acl_cfg["acl_info"]: - cur_cfg_dict = dict() - exist_cfg_dict = dict() - - if self.acl_name: - if self.acl_name.isdigit() and tmp.get("aclNumber"): - cur_cfg_dict["aclNumber"] = self.acl_name - exist_cfg_dict["aclNumber"] = tmp.get("aclNumber") - else: - cur_cfg_dict["aclNumOrName"] = self.acl_name - exist_cfg_dict["aclNumOrName"] = tmp.get("aclNumOrName") - if self.acl_type: - cur_cfg_dict["aclType"] = self.acl_type - exist_cfg_dict["aclType"] = tmp.get("aclType") - if self.acl_num: - cur_cfg_dict["aclNumber"] = self.acl_num - exist_cfg_dict["aclNumber"] = tmp.get("aclNumber") - if self.acl_step: - cur_cfg_dict["aclStep"] = self.acl_step - exist_cfg_dict["aclStep"] = tmp.get("aclStep") - if self.acl_description: - cur_cfg_dict["aclDescription"] = self.acl_description - exist_cfg_dict["aclDescription"] = tmp.get("aclDescription") - - if cur_cfg_dict == exist_cfg_dict: - find_bool = True - else: - find_bool = False - find_list.append(find_bool) - for mem in find_list: - if mem: - find_flag = True - break - else: - find_flag = False - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - elif self.state == "delete_acl": - need_cfg = bool(find_flag) - else: - need_cfg = False - - self.cur_acl_cfg["need_cfg"] = need_cfg - - def check_advance_rule_args(self): - """ Check advance rule invalid args """ - - need_cfg = False - find_flag = False - self.cur_advance_rule_cfg["adv_rule_info"] = [] - - if self.acl_name: - - if self.state == "absent": - if not self.rule_name: - self.module.fail_json( - msg='Error: Please input rule_name when state is absent.') - - # config rule - if self.rule_name: - if len(self.rule_name) < 1 or len(self.rule_name) > 32: - self.module.fail_json( - msg='Error: The len of rule_name is out of [1 - 32].') - - if self.state != "delete_acl" and not self.rule_id: - self.module.fail_json( - msg='Error: Please input rule_id.') - - if self.rule_id: - if self.rule_id.isdigit(): - if int(self.rule_id) < 0 or int(self.rule_id) > 4294967294: - self.module.fail_json( - msg='Error: The value of rule_id is out of [0 - 4294967294].') - else: - self.module.fail_json( - msg='Error: The rule_id is not digit.') - - if self.rule_action and not self.protocol: - self.module.fail_json( - msg='Error: The rule_action and the protocol must input at the same time.') - - if not self.rule_action and self.protocol: - self.module.fail_json( - msg='Error: The rule_action and the protocol must input at the same time.') - - if self.protocol: - self.get_protocol_num() - - if self.source_ip: - if not check_ip_addr(self.source_ip): - self.module.fail_json( - msg='Error: The source_ip %s is invalid.' % self.source_ip) - if not self.src_mask: - self.module.fail_json( - msg='Error: Please input src_mask.') - - if self.src_mask: - if self.src_mask.isdigit(): - if int(self.src_mask) < 1 or int(self.src_mask) > 32: - self.module.fail_json( - msg='Error: The value of src_mask is out of [1 - 32].') - self.src_wild = get_wildcard_mask(self.src_mask) - else: - self.module.fail_json( - msg='Error: The src_mask is not digit.') - - if self.src_pool_name: - if len(self.src_pool_name) < 1 or len(self.src_pool_name) > 32: - self.module.fail_json( - msg='Error: The len of src_pool_name is out of [1 - 32].') - - if self.dest_ip: - if not check_ip_addr(self.dest_ip): - self.module.fail_json( - msg='Error: The dest_ip %s is invalid.' % self.dest_ip) - if not self.dest_mask: - self.module.fail_json( - msg='Error: Please input dest_mask.') - - if self.dest_mask: - if self.dest_mask.isdigit(): - if int(self.dest_mask) < 1 or int(self.dest_mask) > 32: - self.module.fail_json( - msg='Error: The value of dest_mask is out of [1 - 32].') - self.dest_wild = get_wildcard_mask(self.dest_mask) - else: - self.module.fail_json( - msg='Error: The dest_mask is not digit.') - - if self.dest_pool_name: - if len(self.dest_pool_name) < 1 or len(self.dest_pool_name) > 32: - self.module.fail_json( - msg='Error: The len of dest_pool_name is out of [1 - 32].') - - if self.src_port_op: - if self.src_port_op == "lt": - if not self.src_port_end: - self.module.fail_json( - msg='Error: The src_port_end must input.') - if self.src_port_begin: - self.module.fail_json( - msg='Error: The src_port_begin should not input.') - if self.src_port_op == "eq" or self.src_port_op == "gt": - if not self.src_port_begin: - self.module.fail_json( - msg='Error: The src_port_begin must input.') - if self.src_port_end: - self.module.fail_json( - msg='Error: The src_port_end should not input.') - if self.src_port_op == "range": - if not self.src_port_begin or not self.src_port_end: - self.module.fail_json( - msg='Error: The src_port_begin and src_port_end must input.') - - if self.src_port_begin: - if self.src_port_begin.isdigit(): - if int(self.src_port_begin) < 0 or int(self.src_port_begin) > 65535: - self.module.fail_json( - msg='Error: The value of src_port_begin is out of [0 - 65535].') - else: - self.module.fail_json( - msg='Error: The src_port_begin is not digit.') - - if self.src_port_end: - if self.src_port_end.isdigit(): - if int(self.src_port_end) < 0 or int(self.src_port_end) > 65535: - self.module.fail_json( - msg='Error: The value of src_port_end is out of [0 - 65535].') - else: - self.module.fail_json( - msg='Error: The src_port_end is not digit.') - - if self.src_port_pool_name: - if len(self.src_port_pool_name) < 1 or len(self.src_port_pool_name) > 32: - self.module.fail_json( - msg='Error: The len of src_port_pool_name is out of [1 - 32].') - - if self.dest_port_op: - if self.dest_port_op == "lt": - if not self.dest_port_end: - self.module.fail_json( - msg='Error: The dest_port_end must input.') - if self.dest_port_begin: - self.module.fail_json( - msg='Error: The dest_port_begin should not input.') - if self.dest_port_op == "eq" or self.dest_port_op == "gt": - if not self.dest_port_begin: - self.module.fail_json( - msg='Error: The dest_port_begin must input.') - if self.dest_port_end: - self.module.fail_json( - msg='Error: The dest_port_end should not input.') - if self.dest_port_op == "range": - if not self.dest_port_begin or not self.dest_port_end: - self.module.fail_json( - msg='Error: The dest_port_begin and dest_port_end must input.') - - if self.dest_port_begin: - if self.dest_port_begin.isdigit(): - if int(self.dest_port_begin) < 0 or int(self.dest_port_begin) > 65535: - self.module.fail_json( - msg='Error: The value of dest_port_begin is out of [0 - 65535].') - else: - self.module.fail_json( - msg='Error: The dest_port_begin is not digit.') - - if self.dest_port_end: - if self.dest_port_end.isdigit(): - if int(self.dest_port_end) < 0 or int(self.dest_port_end) > 65535: - self.module.fail_json( - msg='Error: The value of dest_port_end is out of [0 - 65535].') - else: - self.module.fail_json( - msg='Error: The dest_port_end is not digit.') - - if self.dest_port_pool_name: - if len(self.dest_port_pool_name) < 1 or len(self.dest_port_pool_name) > 32: - self.module.fail_json( - msg='Error: The len of dest_port_pool_name is out of [1 - 32].') - - if self.precedence: - if self.precedence.isdigit(): - if int(self.precedence) < 0 or int(self.precedence) > 7: - self.module.fail_json( - msg='Error: The value of precedence is out of [0 - 7].') - else: - self.module.fail_json( - msg='Error: The precedence is not digit.') - - if self.tos: - if self.tos.isdigit(): - if int(self.tos) < 0 or int(self.tos) > 15: - self.module.fail_json( - msg='Error: The value of tos is out of [0 - 15].') - else: - self.module.fail_json( - msg='Error: The tos is not digit.') - - if self.dscp: - if self.dscp.isdigit(): - if int(self.dscp) < 0 or int(self.dscp) > 63: - self.module.fail_json( - msg='Error: The value of dscp is out of [0 - 63].') - else: - self.module.fail_json( - msg='Error: The dscp is not digit.') - - if self.icmp_type: - if self.icmp_type.isdigit(): - if int(self.icmp_type) < 0 or int(self.icmp_type) > 255: - self.module.fail_json( - msg='Error: The value of icmp_type is out of [0 - 255].') - else: - self.module.fail_json( - msg='Error: The icmp_type is not digit.') - - if self.icmp_code: - if self.icmp_code.isdigit(): - if int(self.icmp_code) < 0 or int(self.icmp_code) > 255: - self.module.fail_json( - msg='Error: The value of icmp_code is out of [0 - 255].') - else: - self.module.fail_json( - msg='Error: The icmp_code is not digit.') - - if self.vrf_name: - if len(self.vrf_name) < 1 or len(self.vrf_name) > 31: - self.module.fail_json( - msg='Error: The len of vrf_name is out of [1 - 31].') - - if self.syn_flag: - if self.syn_flag.isdigit(): - if int(self.syn_flag) < 0 or int(self.syn_flag) > 63: - self.module.fail_json( - msg='Error: The value of syn_flag is out of [0 - 63].') - else: - self.module.fail_json( - msg='Error: The syn_flag is not digit.') - - if self.tcp_flag_mask: - if self.tcp_flag_mask.isdigit(): - if int(self.tcp_flag_mask) < 0 or int(self.tcp_flag_mask) > 63: - self.module.fail_json( - msg='Error: The value of tcp_flag_mask is out of [0 - 63].') - else: - self.module.fail_json( - msg='Error: The tcp_flag_mask is not digit.') - - if self.time_range: - if len(self.time_range) < 1 or len(self.time_range) > 32: - self.module.fail_json( - msg='Error: The len of time_range is out of [1 - 32].') - - if self.rule_description: - if len(self.rule_description) < 1 or len(self.rule_description) > 127: - self.module.fail_json( - msg='Error: The len of rule_description is out of [1 - 127].') - - if self.igmp_type: - self.get_igmp_type_num() - - conf_str = CE_GET_ACL_ADVANCE_RULE_HEADER % self.acl_name - - if self.rule_id: - conf_str += "" - if self.rule_action: - conf_str += "" - if self.protocol: - conf_str += "" - if self.source_ip: - conf_str += "" - if self.src_wild: - conf_str += "" - if self.src_pool_name: - conf_str += "" - if self.dest_ip: - conf_str += "" - if self.dest_wild: - conf_str += "" - if self.dest_pool_name: - conf_str += "" - if self.src_port_op: - conf_str += "" - if self.src_port_begin: - conf_str += "" - if self.src_port_end: - conf_str += "" - if self.src_port_pool_name: - conf_str += "" - if self.dest_port_op: - conf_str += "" - if self.dest_port_begin: - conf_str += "" - if self.dest_port_end: - conf_str += "" - if self.dest_port_pool_name: - conf_str += "" - if self.frag_type: - conf_str += "" - if self.precedence: - conf_str += "" - if self.tos: - conf_str += "" - if self.dscp: - conf_str += "" - if self.icmp_name: - conf_str += "" - if self.icmp_type: - conf_str += "" - if self.icmp_code: - conf_str += "" - conf_str += "" - if self.vrf_name: - conf_str += "" - if self.syn_flag: - conf_str += "" - if self.tcp_flag_mask: - conf_str += "" - conf_str += "" - if self.time_range: - conf_str += "" - if self.rule_description: - conf_str += "" - if self.igmp_type: - conf_str += "" - conf_str += "" - - conf_str += CE_GET_ACL_ADVANCE_RULE_TAIL - recv_xml = self.netconf_get_config(conf_str=conf_str) - - if "" in recv_xml: - find_flag = False - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - - # parse advance rule - adv_rule_info = root.findall( - "acl/aclGroups/aclGroup/aclRuleAdv4s/aclRuleAdv4") - if adv_rule_info: - for tmp in adv_rule_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["aclRuleName", "aclRuleID", "aclAction", "aclProtocol", "aclSourceIp", - "aclSrcWild", "aclSPoolName", "aclDestIp", "aclDestWild", - "aclDPoolName", "aclSrcPortOp", "aclSrcPortBegin", "aclSrcPortEnd", - "aclSPortPoolName", "aclDestPortOp", "aclDestPortB", "aclDestPortE", - "aclDPortPoolName", "aclFragType", "aclPrecedence", "aclTos", - "aclDscp", "aclIcmpName", "aclIcmpType", "aclIcmpCode", "aclTtlExpired", - "vrfName", "aclSynFlag", "aclTcpFlagMask", "aclEstablished", - "aclTimeName", "aclRuleDescription", "aclIgmpType", "aclLogFlag"]: - tmp_dict[site.tag] = site.text - - self.cur_advance_rule_cfg[ - "adv_rule_info"].append(tmp_dict) - - if self.cur_advance_rule_cfg["adv_rule_info"]: - for tmp in self.cur_advance_rule_cfg["adv_rule_info"]: - find_flag = True - - if self.rule_name and tmp.get("aclRuleName") != self.rule_name: - find_flag = False - if self.rule_id and tmp.get("aclRuleID") != self.rule_id: - find_flag = False - if self.rule_action and tmp.get("aclAction") != self.rule_action: - find_flag = False - if self.protocol and tmp.get("aclProtocol") != self.protocol_num: - find_flag = False - if self.source_ip: - tmp_src_ip = self.source_ip.split(".") - tmp_src_wild = self.src_wild.split(".") - tmp_addr_item = [] - for idx in range(4): - item1 = 255 - int(tmp_src_wild[idx]) - item2 = item1 & int(tmp_src_ip[idx]) - tmp_addr_item.append(item2) - tmp_addr = "%s.%s.%s.%s" % (tmp_addr_item[0], tmp_addr_item[1], - tmp_addr_item[2], tmp_addr_item[3]) - if tmp_addr != tmp.get("aclSourceIp"): - find_flag = False - if self.src_wild and tmp.get("aclSrcWild") != self.src_wild: - find_flag = False - if self.src_pool_name and tmp.get("aclSPoolName") != self.src_pool_name: - find_flag = False - if self.dest_ip: - tmp_src_ip = self.dest_ip.split(".") - tmp_src_wild = self.dest_wild.split(".") - tmp_addr_item = [] - for idx in range(4): - item1 = 255 - int(tmp_src_wild[idx]) - item2 = item1 & int(tmp_src_ip[idx]) - tmp_addr_item.append(item2) - tmp_addr = "%s.%s.%s.%s" % (tmp_addr_item[0], tmp_addr_item[1], - tmp_addr_item[2], tmp_addr_item[3]) - if tmp_addr != tmp.get("aclDestIp"): - find_flag = False - if self.dest_wild and tmp.get("aclDestWild") != self.dest_wild: - find_flag = False - if self.dest_pool_name and tmp.get("aclDPoolName") != self.dest_pool_name: - find_flag = False - if self.src_port_op and tmp.get("aclSrcPortOp") != self.src_port_op: - find_flag = False - if self.src_port_begin and tmp.get("aclSrcPortBegin") != self.src_port_begin: - find_flag = False - if self.src_port_end and tmp.get("aclSrcPortEnd") != self.src_port_end: - find_flag = False - if self.src_port_pool_name and tmp.get("aclSPortPoolName") != self.src_port_pool_name: - find_flag = False - if self.dest_port_op and tmp.get("aclDestPortOp") != self.dest_port_op: - find_flag = False - if self.dest_port_begin and tmp.get("aclDestPortB") != self.dest_port_begin: - find_flag = False - if self.dest_port_end and tmp.get("aclDestPortE") != self.dest_port_end: - find_flag = False - if self.dest_port_pool_name and tmp.get("aclDPortPoolName") != self.dest_port_pool_name: - find_flag = False - frag_type = "clear_fragment" if tmp.get("aclFragType") is None else tmp.get("aclFragType") - if self.frag_type and frag_type != self.frag_type: - find_flag = False - if self.precedence and tmp.get("aclPrecedence") != self.precedence: - find_flag = False - if self.tos and tmp.get("aclTos") != self.tos: - find_flag = False - if self.dscp and tmp.get("aclDscp") != self.dscp: - find_flag = False - if self.icmp_name and tmp.get("aclIcmpName") != self.icmp_name: - find_flag = False - if self.icmp_type and tmp.get("aclIcmpType") != self.icmp_type: - find_flag = False - if self.icmp_code and tmp.get("aclIcmpCode") != self.icmp_code: - find_flag = False - if tmp.get("aclTtlExpired").lower() != str(self.ttl_expired).lower(): - find_flag = False - if self.vrf_name and tmp.get("vrfName") != self.vrf_name: - find_flag = False - if self.syn_flag and tmp.get("aclSynFlag") != self.syn_flag: - find_flag = False - if self.tcp_flag_mask and tmp.get("aclTcpFlagMask") != self.tcp_flag_mask: - find_flag = False - if self.protocol == "tcp" and \ - tmp.get("aclEstablished").lower() != str(self.established).lower(): - find_flag = False - if self.time_range and tmp.get("aclTimeName") != self.time_range: - find_flag = False - if self.rule_description and tmp.get("aclRuleDescription") != self.rule_description: - find_flag = False - if self.igmp_type and tmp.get("aclIgmpType") != self.igmp_type_num: - find_flag = False - if tmp.get("aclLogFlag").lower() != str(self.log_flag).lower(): - find_flag = False - - if find_flag: - break - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - elif self.state == "absent": - need_cfg = bool(find_flag) - else: - need_cfg = False - - self.cur_advance_rule_cfg["need_cfg"] = need_cfg - - def get_proposed(self): - """ Get proposed state """ - - self.proposed["state"] = self.state - - if self.acl_name: - self.proposed["acl_name"] = self.acl_name - if self.acl_num: - self.proposed["acl_num"] = self.acl_num - if self.acl_step: - self.proposed["acl_step"] = self.acl_step - if self.acl_description: - self.proposed["acl_description"] = self.acl_description - if self.rule_name: - self.proposed["rule_name"] = self.rule_name - if self.rule_id: - self.proposed["rule_id"] = self.rule_id - if self.rule_action: - self.proposed["rule_action"] = self.rule_action - if self.protocol: - self.proposed["protocol"] = self.protocol - if self.source_ip: - self.proposed["source_ip"] = self.source_ip - if self.src_mask: - self.proposed["src_mask"] = self.src_mask - if self.src_pool_name: - self.proposed["src_pool_name"] = self.src_pool_name - if self.dest_ip: - self.proposed["dest_ip"] = self.dest_ip - if self.dest_mask: - self.proposed["dest_mask"] = self.dest_mask - if self.dest_pool_name: - self.proposed["dest_pool_name"] = self.dest_pool_name - if self.src_port_op: - self.proposed["src_port_op"] = self.src_port_op - if self.src_port_begin: - self.proposed["src_port_begin"] = self.src_port_begin - if self.src_port_end: - self.proposed["src_port_end"] = self.src_port_end - if self.src_port_pool_name: - self.proposed["src_port_pool_name"] = self.src_port_pool_name - if self.dest_port_op: - self.proposed["dest_port_op"] = self.dest_port_op - if self.dest_port_begin: - self.proposed["dest_port_begin"] = self.dest_port_begin - if self.dest_port_end: - self.proposed["dest_port_end"] = self.dest_port_end - if self.dest_port_pool_name: - self.proposed["dest_port_pool_name"] = self.dest_port_pool_name - if self.frag_type: - self.proposed["frag_type"] = self.frag_type - if self.precedence: - self.proposed["precedence"] = self.precedence - if self.tos: - self.proposed["tos"] = self.tos - if self.dscp: - self.proposed["dscp"] = self.dscp - if self.icmp_name: - self.proposed["icmp_name"] = self.icmp_name - if self.icmp_type: - self.proposed["icmp_type"] = self.icmp_type - if self.icmp_code: - self.proposed["icmp_code"] = self.icmp_code - if self.ttl_expired: - self.proposed["ttl_expired"] = self.ttl_expired - if self.vrf_name: - self.proposed["vrf_name"] = self.vrf_name - if self.syn_flag: - self.proposed["syn_flag"] = self.syn_flag - if self.tcp_flag_mask: - self.proposed["tcp_flag_mask"] = self.tcp_flag_mask - self.proposed["established"] = self.established - if self.time_range: - self.proposed["time_range"] = self.time_range - if self.rule_description: - self.proposed["rule_description"] = self.rule_description - if self.igmp_type: - self.proposed["igmp_type"] = self.igmp_type - self.proposed["log_flag"] = self.log_flag - - def get_existing(self): - """ Get existing state """ - - self.existing["acl_info"] = self.cur_acl_cfg["acl_info"] - self.existing["adv_rule_info"] = self.cur_advance_rule_cfg[ - "adv_rule_info"] - - def get_end_state(self): - """ Get end state """ - - self.check_acl_args() - self.end_state["acl_info"] = self.cur_acl_cfg["acl_info"] - - self.check_advance_rule_args() - self.end_state["adv_rule_info"] = self.cur_advance_rule_cfg[ - "adv_rule_info"] - if self.end_state == self.existing: - self.changed = False - self.updates_cmd = list() - - def merge_acl(self): - """ Merge acl operation """ - - conf_str = CE_MERGE_ACL_HEADER % self.acl_name - - if self.acl_type: - conf_str += "%s" % self.acl_type - if self.acl_num: - conf_str += "%s" % self.acl_num - if self.acl_step: - conf_str += "%s" % self.acl_step - if self.acl_description: - conf_str += "%s" % self.acl_description - - conf_str += CE_MERGE_ACL_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge acl failed.') - - if self.acl_name.isdigit(): - cmd = "acl number %s" % self.acl_name - else: - if self.acl_type and not self.acl_num: - cmd = "acl name %s %s" % (self.acl_name, self.acl_type.lower()) - elif self.acl_type and self.acl_num: - cmd = "acl name %s number %s" % (self.acl_name, self.acl_num) - elif not self.acl_type and self.acl_num: - cmd = "acl name %s number %s" % (self.acl_name, self.acl_num) - self.updates_cmd.append(cmd) - - if self.acl_description: - cmd = "description %s" % self.acl_description - self.updates_cmd.append(cmd) - - if self.acl_step: - cmd = "step %s" % self.acl_step - self.updates_cmd.append(cmd) - - self.changed = True - - def delete_acl(self): - """ Delete acl operation """ - - conf_str = CE_DELETE_ACL_HEADER % self.acl_name - - if self.acl_type: - conf_str += "%s" % self.acl_type - if self.acl_num: - conf_str += "%s" % self.acl_num - if self.acl_step: - conf_str += "%s" % self.acl_step - if self.acl_description: - conf_str += "%s" % self.acl_description - - conf_str += CE_DELETE_ACL_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Delete acl failed.') - - if self.acl_description: - cmd = "undo description" - self.updates_cmd.append(cmd) - - if self.acl_step: - cmd = "undo step" - self.updates_cmd.append(cmd) - - if self.acl_name.isdigit(): - cmd = "undo acl number %s" % self.acl_name - else: - cmd = "undo acl name %s" % self.acl_name - self.updates_cmd.append(cmd) - - self.changed = True - - def merge_adv_rule(self): - """ Merge advance rule operation """ - - conf_str = CE_MERGE_ACL_ADVANCE_RULE_HEADER % ( - self.acl_name, self.rule_name) - - if self.rule_id: - conf_str += "%s" % self.rule_id - if self.rule_action: - conf_str += "%s" % self.rule_action - if self.protocol: - conf_str += "%s" % self.protocol_num - if self.source_ip: - conf_str += "%s" % self.source_ip - if self.src_wild: - conf_str += "%s" % self.src_wild - if self.src_pool_name: - conf_str += "%s" % self.src_pool_name - if self.dest_ip: - conf_str += "%s" % self.dest_ip - if self.dest_wild: - conf_str += "%s" % self.dest_wild - if self.dest_pool_name: - conf_str += "%s" % self.dest_pool_name - if self.src_port_op: - conf_str += "%s" % self.src_port_op - if self.src_port_begin: - conf_str += "%s" % self.src_port_begin - if self.src_port_end: - conf_str += "%s" % self.src_port_end - if self.src_port_pool_name: - conf_str += "%s" % self.src_port_pool_name - if self.dest_port_op: - conf_str += "%s" % self.dest_port_op - if self.dest_port_begin: - conf_str += "%s" % self.dest_port_begin - if self.dest_port_end: - conf_str += "%s" % self.dest_port_end - if self.dest_port_pool_name: - conf_str += "%s" % self.dest_port_pool_name - if self.frag_type: - conf_str += "%s" % self.frag_type - if self.precedence: - conf_str += "%s" % self.precedence - if self.tos: - conf_str += "%s" % self.tos - if self.dscp: - conf_str += "%s" % self.dscp - if self.icmp_name: - conf_str += "%s" % self.icmp_name - if self.icmp_type: - conf_str += "%s" % self.icmp_type - if self.icmp_code: - conf_str += "%s" % self.icmp_code - conf_str += "%s" % str(self.ttl_expired).lower() - if self.vrf_name: - conf_str += "%s" % self.vrf_name - if self.syn_flag: - conf_str += "%s" % self.syn_flag - if self.tcp_flag_mask: - conf_str += "%s" % self.tcp_flag_mask - if self.protocol == "tcp": - conf_str += "%s" % str(self.established).lower() - if self.time_range: - conf_str += "%s" % self.time_range - if self.rule_description: - conf_str += "%s" % self.rule_description - if self.igmp_type: - conf_str += "%s" % self.igmp_type_num - conf_str += "%s" % str(self.log_flag).lower() - - conf_str += CE_MERGE_ACL_ADVANCE_RULE_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge acl base rule failed.') - - if self.rule_action and self.protocol: - cmd = "rule" - if self.rule_id: - cmd += " %s" % self.rule_id - cmd += " %s" % self.rule_action - cmd += " %s" % self.protocol - if self.dscp: - cmd += " dscp %s" % self.dscp - if self.tos: - cmd += " tos %s" % self.tos - if self.source_ip and self.src_wild: - cmd += " source %s %s" % (self.source_ip, self.src_wild) - if self.src_pool_name: - cmd += " source-pool %s" % self.src_pool_name - if self.src_port_op: - cmd += " source-port" - if self.src_port_op == "lt": - cmd += " lt %s" % self.src_port_end - elif self.src_port_op == "eq": - cmd += " eq %s" % self.src_port_begin - elif self.src_port_op == "gt": - cmd += " gt %s" % self.src_port_begin - elif self.src_port_op == "range": - cmd += " range %s %s" % (self.src_port_begin, - self.src_port_end) - if self.src_port_pool_name: - cmd += " source-port-pool %s" % self.src_port_pool_name - if self.dest_ip and self.dest_wild: - cmd += " destination %s %s" % (self.dest_ip, self.dest_wild) - if self.dest_pool_name: - cmd += " destination-pool %s" % self.dest_pool_name - if self.dest_port_op: - cmd += " destination-port" - if self.dest_port_op == "lt": - cmd += " lt %s" % self.dest_port_end - elif self.dest_port_op == "eq": - cmd += " eq %s" % self.dest_port_begin - elif self.dest_port_op == "gt": - cmd += " gt %s" % self.dest_port_begin - elif self.dest_port_op == "range": - cmd += " range %s %s" % (self.dest_port_begin, - self.dest_port_end) - if self.dest_port_pool_name: - cmd += " destination-port-pool %s" % self.dest_port_pool_name - if self.frag_type == "fragment": - cmd += " fragment-type fragment" - if self.precedence: - cmd += " precedence %s" % self.precedence_name[self.precedence] - - if self.protocol == "icmp": - if self.icmp_name: - cmd += " icmp-type %s" % self.icmp_name - elif self.icmp_type and self.icmp_code: - cmd += " icmp-type %s %s" % (self.icmp_type, self.icmp_code) - elif self.icmp_type: - cmd += " icmp-type %s" % self.icmp_type - if self.protocol == "tcp": - if self.syn_flag: - cmd += " tcp-flag %s" % self.syn_flag - if self.tcp_flag_mask: - cmd += " mask %s" % self.tcp_flag_mask - if self.established: - cmd += " established" - if self.protocol == "igmp": - if self.igmp_type: - cmd += " igmp-type %s" % self.igmp_type - if self.time_range: - cmd += " time-range %s" % self.time_range - if self.vrf_name: - cmd += " vpn-instance %s" % self.vrf_name - if self.ttl_expired: - cmd += " ttl-expired" - if self.log_flag: - cmd += " logging" - self.updates_cmd.append(cmd) - - if self.rule_description: - cmd = "rule %s description %s" % ( - self.rule_id, self.rule_description) - self.updates_cmd.append(cmd) - - self.changed = True - - def delete_adv_rule(self): - """ Delete advance rule operation """ - - conf_str = CE_DELETE_ACL_ADVANCE_RULE_HEADER % ( - self.acl_name, self.rule_name) - - if self.rule_id: - conf_str += "%s" % self.rule_id - if self.rule_action: - conf_str += "%s" % self.rule_action - if self.protocol: - conf_str += "%s" % self.protocol_num - if self.source_ip: - conf_str += "%s" % self.source_ip - if self.src_wild: - conf_str += "%s" % self.src_wild - if self.src_pool_name: - conf_str += "%s" % self.src_pool_name - if self.dest_ip: - conf_str += "%s" % self.dest_ip - if self.dest_wild: - conf_str += "%s" % self.dest_wild - if self.dest_pool_name: - conf_str += "%s" % self.dest_pool_name - if self.src_port_op: - conf_str += "%s" % self.src_port_op - if self.src_port_begin: - conf_str += "%s" % self.src_port_begin - if self.src_port_end: - conf_str += "%s" % self.src_port_end - if self.src_port_pool_name: - conf_str += "%s" % self.src_port_pool_name - if self.dest_port_op: - conf_str += "%s" % self.dest_port_op - if self.dest_port_begin: - conf_str += "%s" % self.dest_port_begin - if self.dest_port_end: - conf_str += "%s" % self.dest_port_end - if self.dest_port_pool_name: - conf_str += "%s" % self.dest_port_pool_name - if self.frag_type: - conf_str += "%s" % self.frag_type - if self.precedence: - conf_str += "%s" % self.precedence - if self.tos: - conf_str += "%s" % self.tos - if self.dscp: - conf_str += "%s" % self.dscp - if self.icmp_name: - conf_str += "%s" % self.icmp_name - if self.icmp_type: - conf_str += "%s" % self.icmp_type - if self.icmp_code: - conf_str += "%s" % self.icmp_code - conf_str += "%s" % str(self.ttl_expired).lower() - if self.vrf_name: - conf_str += "%s" % self.vrf_name - if self.syn_flag: - conf_str += "%s" % self.syn_flag - if self.tcp_flag_mask: - conf_str += "%s" % self.tcp_flag_mask - if self.protocol == "tcp": - conf_str += "%s" % str(self.established).lower() - if self.time_range: - conf_str += "%s" % self.time_range - if self.rule_description: - conf_str += "%s" % self.rule_description - if self.igmp_type: - conf_str += "%s" % self.igmp_type - conf_str += "%s" % str(self.log_flag).lower() - - conf_str += CE_DELETE_ACL_ADVANCE_RULE_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Delete acl base rule failed.') - - if self.rule_description: - if self.acl_name.isdigit(): - cmd = "acl number %s" % self.acl_name - else: - cmd = "acl name %s" % self.acl_name - self.updates_cmd.append(cmd) - - cmd = "undo rule %s description" % self.rule_id - self.updates_cmd.append(cmd) - - if self.rule_id: - if self.acl_name.isdigit(): - cmd = "acl number %s" % self.acl_name - else: - cmd = "acl name %s" % self.acl_name - self.updates_cmd.append(cmd) - - cmd = "undo rule %s" % self.rule_id - self.updates_cmd.append(cmd) - elif self.rule_action and self.protocol: - if self.acl_name.isdigit(): - cmd = "acl number %s" % self.acl_name - else: - cmd = "acl name %s" % self.acl_name - self.updates_cmd.append(cmd) - - cmd = "undo rule" - cmd += " %s" % self.rule_action - cmd += " %s" % self.protocol - if self.dscp: - cmd += " dscp %s" % self.dscp - if self.tos: - cmd += " tos %s" % self.tos - if self.source_ip and self.src_mask: - cmd += " source %s %s" % (self.source_ip, self.src_mask) - if self.src_pool_name: - cmd += " source-pool %s" % self.src_pool_name - if self.src_port_op: - cmd += " source-port" - if self.src_port_op == "lt": - cmd += " lt %s" % self.src_port_end - elif self.src_port_op == "eq": - cmd += " eq %s" % self.src_port_begin - elif self.src_port_op == "gt": - cmd += " gt %s" % self.src_port_begin - elif self.src_port_op == "range": - cmd += " range %s %s" % (self.src_port_begin, - self.src_port_end) - if self.src_port_pool_name: - cmd += " source-port-pool %s" % self.src_port_pool_name - if self.dest_ip and self.dest_mask: - cmd += " destination %s %s" % (self.dest_ip, self.dest_mask) - if self.dest_pool_name: - cmd += " destination-pool %s" % self.dest_pool_name - if self.dest_port_op: - cmd += " destination-port" - if self.dest_port_op == "lt": - cmd += " lt %s" % self.dest_port_end - elif self.dest_port_op == "eq": - cmd += " eq %s" % self.dest_port_begin - elif self.dest_port_op == "gt": - cmd += " gt %s" % self.dest_port_begin - elif self.dest_port_op == "range": - cmd += " range %s %s" % (self.dest_port_begin, - self.dest_port_end) - if self.dest_port_pool_name: - cmd += " destination-port-pool %s" % self.dest_port_pool_name - if self.frag_type == "fragment": - cmd += " fragment-type fragment" - if self.precedence: - cmd += " precedence %s" % self.precedence_name[self.precedence] - if self.time_range: - cmd += " time-range %s" % self.time_range - if self.vrf_name: - cmd += " vpn-instance %s" % self.vrf_name - if self.ttl_expired: - cmd += " ttl-expired" - if self.log_flag: - cmd += " logging" - self.updates_cmd.append(cmd) - - self.changed = True - - def work(self): - """ Main work function """ - - self.check_acl_args() - self.check_advance_rule_args() - self.get_proposed() - self.get_existing() - - if self.state == "present": - if self.cur_acl_cfg["need_cfg"]: - self.merge_acl() - if self.cur_advance_rule_cfg["need_cfg"]: - self.merge_adv_rule() - - elif self.state == "absent": - if self.cur_advance_rule_cfg["need_cfg"]: - self.delete_adv_rule() - - elif self.state == "delete_acl": - if self.cur_acl_cfg["need_cfg"]: - self.delete_acl() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent', - 'delete_acl'], default='present'), - acl_name=dict(type='str', required=True), - acl_num=dict(type='str'), - acl_step=dict(type='str'), - acl_description=dict(type='str'), - rule_name=dict(type='str'), - rule_id=dict(type='str'), - rule_action=dict(choices=['permit', 'deny']), - protocol=dict(choices=['ip', 'icmp', 'igmp', - 'ipinip', 'tcp', 'udp', 'gre', 'ospf']), - source_ip=dict(type='str'), - src_mask=dict(type='str'), - src_pool_name=dict(type='str'), - dest_ip=dict(type='str'), - dest_mask=dict(type='str'), - dest_pool_name=dict(type='str'), - src_port_op=dict(choices=['lt', 'eq', 'gt', 'range']), - src_port_begin=dict(type='str'), - src_port_end=dict(type='str'), - src_port_pool_name=dict(type='str'), - dest_port_op=dict(choices=['lt', 'eq', 'gt', 'range']), - dest_port_begin=dict(type='str'), - dest_port_end=dict(type='str'), - dest_port_pool_name=dict(type='str'), - frag_type=dict(choices=['fragment', 'clear_fragment']), - precedence=dict(type='str'), - tos=dict(type='str'), - dscp=dict(type='str'), - icmp_name=dict(choices=['unconfiged', 'echo', 'echo-reply', 'fragmentneed-DFset', 'host-redirect', - 'host-tos-redirect', 'host-unreachable', 'information-reply', 'information-request', - 'net-redirect', 'net-tos-redirect', 'net-unreachable', 'parameter-problem', - 'port-unreachable', 'protocol-unreachable', 'reassembly-timeout', 'source-quench', - 'source-route-failed', 'timestamp-reply', 'timestamp-request', 'ttl-exceeded', - 'address-mask-reply', 'address-mask-request', 'custom']), - icmp_type=dict(type='str'), - icmp_code=dict(type='str'), - ttl_expired=dict(required=False, default=False, type='bool'), - vrf_name=dict(type='str'), - syn_flag=dict(type='str'), - tcp_flag_mask=dict(type='str'), - established=dict(required=False, default=False, type='bool'), - time_range=dict(type='str'), - rule_description=dict(type='str'), - igmp_type=dict(choices=['host-query', 'mrouter-adver', 'mrouter-solic', 'mrouter-termi', 'mtrace-resp', - 'mtrace-route', 'v1host-report', 'v2host-report', 'v2leave-group', 'v3host-report']), - log_flag=dict(required=False, default=False, type='bool') - ) - - argument_spec.update(ce_argument_spec) - module = AdvanceAcl(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_acl_interface.py b/plugins/modules/network/cloudengine/ce_acl_interface.py deleted file mode 100644 index deb9c22f74..0000000000 --- a/plugins/modules/network/cloudengine/ce_acl_interface.py +++ /dev/null @@ -1,327 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_acl_interface -short_description: Manages applying ACLs to interfaces on HUAWEI CloudEngine switches. -description: - - Manages applying ACLs to interfaces on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - acl_name: - description: - - ACL number or name. - For a numbered rule group, the value ranging from 2000 to 4999. - For a named rule group, the value is a string of 1 to 32 case-sensitive characters starting - with a letter, spaces not supported. - required: true - interface: - description: - - Interface name. - Only support interface full name, such as "40GE2/0/1". - required: true - direction: - description: - - Direction ACL to be applied in on the interface. - required: true - choices: ['inbound', 'outbound'] - state: - description: - - Determines whether the config should be present or not on the device. - required: false - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' - -- name: CloudEngine acl interface test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Apply acl to interface" - ce_acl_interface: - state: present - acl_name: 2000 - interface: 40GE1/0/1 - direction: outbound - provider: "{{ cli }}" - - - name: "Undo acl from interface" - ce_acl_interface: - state: absent - acl_name: 2000 - interface: 40GE1/0/1 - direction: outbound - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"acl_name": "2000", - "direction": "outbound", - "interface": "40GE2/0/1", - "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"acl interface": "traffic-filter acl lb inbound"} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"acl interface": ["traffic-filter acl lb inbound", "traffic-filter acl 2000 outbound"]} -updates: - description: command sent to the device - returned: always - type: list - sample: ["interface 40ge2/0/1", - "traffic-filter acl 2000 outbound"] -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_config, exec_command, cli_err_msg -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec - - -class AclInterface(object): - """ Manages acl interface configuration """ - - def __init__(self, **kwargs): - """ Class init """ - - # argument spec - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # config - self.cur_cfg = dict() - self.cur_cfg["acl interface"] = [] - - # module args - self.state = self.module.params['state'] - self.acl_name = self.module.params['acl_name'] - self.interface = self.module.params['interface'] - self.direction = self.module.params['direction'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def check_args(self): - """ Check args """ - - if self.acl_name: - if self.acl_name.isdigit(): - if int(self.acl_name) < 2000 or int(self.acl_name) > 4999: - self.module.fail_json( - msg='Error: The value of acl_name is out of [2000 - 4999].') - else: - if len(self.acl_name) < 1 or len(self.acl_name) > 32: - self.module.fail_json( - msg='Error: The len of acl_name is out of [1 - 32].') - - if self.interface: - cmd = "display current-configuration | ignore-case section include interface %s" % self.interface - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - result = str(out).strip() - if result: - tmp = result.split('\n') - if "display" in tmp[0]: - tmp.pop(0) - if not tmp: - self.module.fail_json( - msg='Error: The interface %s is not in the device.' % self.interface) - - def get_proposed(self): - """ Get proposed config """ - - self.proposed["state"] = self.state - - if self.acl_name: - self.proposed["acl_name"] = self.acl_name - - if self.interface: - self.proposed["interface"] = self.interface - - if self.direction: - self.proposed["direction"] = self.direction - - def get_existing(self): - """ Get existing config """ - - cmd = "display current-configuration | ignore-case section include interface %s | include traffic-filter" % self.interface - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - result = str(out).strip() - end = [] - if result: - tmp = result.split('\n') - if "display" in tmp[0]: - tmp.pop(0) - for item in tmp: - end.append(item.strip()) - self.cur_cfg["acl interface"] = end - self.existing["acl interface"] = end - - def get_end_state(self): - """ Get config end state """ - - cmd = "display current-configuration | ignore-case section include interface %s | include traffic-filter" % self.interface - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - result = str(out).strip() - end = [] - if result: - tmp = result.split('\n') - if "display" in tmp[0]: - tmp.pop(0) - for item in tmp: - end.append(item.strip()) - self.end_state["acl interface"] = end - - def load_config(self, config): - """Sends configuration commands to the remote device""" - - rc, out, err = exec_command(self.module, 'mmi-mode enable') - if rc != 0: - self.module.fail_json(msg='unable to set mmi-mode enable', output=err) - rc, out, err = exec_command(self.module, 'system-view immediately') - if rc != 0: - self.module.fail_json(msg='unable to enter system-view', output=err) - - for cmd in config: - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - if "unrecognized command found" in err.lower(): - self.module.fail_json(msg="Error:The parameter is incorrect or the interface does not support this parameter.") - else: - self.module.fail_json(msg=cli_err_msg(cmd.strip(), err)) - - exec_command(self.module, 'return') - - def cli_load_config(self, commands): - """ Cli method to load config """ - - if not self.module.check_mode: - self.load_config(commands) - - def work(self): - """ Work function """ - - self.check_args() - self.get_proposed() - self.get_existing() - - cmds = list() - tmp_cmd = "traffic-filter acl %s %s" % (self.acl_name, self.direction) - undo_tmp_cmd = "undo traffic-filter acl %s %s" % ( - self.acl_name, self.direction) - - if self.state == "present": - if tmp_cmd not in self.cur_cfg["acl interface"]: - interface_cmd = "interface %s" % self.interface.lower() - cmds.append(interface_cmd) - cmds.append(tmp_cmd) - - self.cli_load_config(cmds) - - self.changed = True - self.updates_cmd.append(interface_cmd) - self.updates_cmd.append(tmp_cmd) - - else: - if tmp_cmd in self.cur_cfg["acl interface"]: - interface_cmd = "interface %s" % self.interface - cmds.append(interface_cmd) - cmds.append(undo_tmp_cmd) - self.cli_load_config(cmds) - - self.changed = True - self.updates_cmd.append(interface_cmd) - self.updates_cmd.append(undo_tmp_cmd) - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - acl_name=dict(type='str', required=True), - interface=dict(type='str', required=True), - direction=dict(choices=['inbound', 'outbound'], required=True) - ) - - argument_spec.update(ce_argument_spec) - module = AclInterface(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_bfd_global.py b/plugins/modules/network/cloudengine/ce_bfd_global.py deleted file mode 100644 index 924fdbddd0..0000000000 --- a/plugins/modules/network/cloudengine/ce_bfd_global.py +++ /dev/null @@ -1,558 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_bfd_global -short_description: Manages BFD global configuration on HUAWEI CloudEngine devices. -description: - - Manages BFD global configuration on HUAWEI CloudEngine devices. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - bfd_enable: - description: - - Enables the global Bidirectional Forwarding Detection (BFD) function. - choices: ['enable', 'disable'] - default_ip: - description: - - Specifies the default multicast IP address. - The value ranges from 224.0.0.107 to 224.0.0.250. - tos_exp_dynamic: - description: - - Indicates the priority of BFD control packets for dynamic BFD sessions. - The value is an integer ranging from 0 to 7. - The default priority is 7, which is the highest priority of BFD control packets. - tos_exp_static: - description: - - Indicates the priority of BFD control packets for static BFD sessions. - The value is an integer ranging from 0 to 7. - The default priority is 7, which is the highest priority of BFD control packets. - damp_init_wait_time: - description: - - Specifies an initial flapping suppression time for a BFD session. - The value is an integer ranging from 1 to 3600000, in milliseconds. - The default value is 2000. - damp_max_wait_time: - description: - - Specifies a maximum flapping suppression time for a BFD session. - The value is an integer ranging from 1 to 3600000, in milliseconds. - The default value is 15000. - damp_second_wait_time: - description: - - Specifies a secondary flapping suppression time for a BFD session. - The value is an integer ranging from 1 to 3600000, in milliseconds. - The default value is 5000. - delay_up_time: - description: - - Specifies the delay before a BFD session becomes Up. - The value is an integer ranging from 1 to 600, in seconds. - The default value is 0, indicating that a BFD session immediately becomes Up. - state: - description: - - Determines whether the config should be present or not on the device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' -- name: bfd global module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Enable the global BFD function - ce_bfd_global: - bfd_enable: enable - provider: '{{ cli }}' - - - name: Set the default multicast IP address to 224.0.0.150 - ce_bfd_global: - bfd_enable: enable - default_ip: 224.0.0.150 - state: present - provider: '{{ cli }}' - - - name: Set the priority of BFD control packets for dynamic and static BFD sessions - ce_bfd_global: - bfd_enable: enable - tos_exp_dynamic: 5 - tos_exp_static: 6 - state: present - provider: '{{ cli }}' - - - name: Disable the global BFD function - ce_bfd_global: - bfd_enable: disable - provider: '{{ cli }}' -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: { - "bfd_enalbe": "enable", - "damp_init_wait_time": null, - "damp_max_wait_time": null, - "damp_second_wait_time": null, - "default_ip": null, - "delayUpTimer": null, - "state": "present", - "tos_exp_dynamic": null, - "tos_exp_static": null - } -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: { - "global": { - "bfdEnable": "false", - "dampInitWaitTime": "2000", - "dampMaxWaitTime": "12000", - "dampSecondWaitTime": "5000", - "defaultIp": "224.0.0.184", - "delayUpTimer": null, - "tosExp": "7", - "tosExpStatic": "7" - } - } -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: { - "global": { - "bfdEnable": "true", - "dampInitWaitTime": "2000", - "dampMaxWaitTime": "12000", - "dampSecondWaitTime": "5000", - "defaultIp": "224.0.0.184", - "delayUpTimer": null, - "tosExp": "7", - "tosExpStatic": "7" - } - } -updates: - description: commands sent to the device - returned: always - type: list - sample: [ "bfd" ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import sys -import socket -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - -CE_NC_GET_BFD = """ - - - %s - - -""" - -CE_NC_GET_BFD_GLB = """ - - - - - - - - - - -""" - - -def check_default_ip(ipaddr): - """check the default multicast IP address""" - - # The value ranges from 224.0.0.107 to 224.0.0.250 - if not check_ip_addr(ipaddr): - return False - - if ipaddr.count(".") != 3: - return False - - ips = ipaddr.split(".") - if ips[0] != "224" or ips[1] != "0" or ips[2] != "0": - return False - - if not ips[3].isdigit() or int(ips[3]) < 107 or int(ips[3]) > 250: - return False - - return True - - -class BfdGlobal(object): - """Manages BFD Global""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.bfd_enable = self.module.params['bfd_enable'] - self.default_ip = self.module.params['default_ip'] - self.tos_exp_dynamic = self.module.params['tos_exp_dynamic'] - self.tos_exp_static = self.module.params['tos_exp_static'] - self.damp_init_wait_time = self.module.params['damp_init_wait_time'] - self.damp_max_wait_time = self.module.params['damp_max_wait_time'] - self.damp_second_wait_time = self.module.params['damp_second_wait_time'] - self.delay_up_time = self.module.params['delay_up_time'] - self.state = self.module.params['state'] - - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.changed = False - self.bfd_dict = dict() - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - - required_together = [('damp_init_wait_time', 'damp_max_wait_time', 'damp_second_wait_time')] - self.module = AnsibleModule(argument_spec=self.spec, - required_together=required_together, - supports_check_mode=True) - - def get_bfd_dict(self): - """bfd config dict""" - - bfd_dict = dict() - bfd_dict["global"] = dict() - conf_str = CE_NC_GET_BFD % CE_NC_GET_BFD_GLB - - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return bfd_dict - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # get bfd global info - glb = root.find("bfd/bfdSchGlobal") - if glb: - for attr in glb: - if attr.text is not None: - bfd_dict["global"][attr.tag] = attr.text - - return bfd_dict - - def config_global(self): - """configures bfd global params""" - - xml_str = "" - damp_chg = False - - # bfd_enable - if self.bfd_enable: - if bool(self.bfd_dict["global"].get("bfdEnable", "false") == "true") != bool(self.bfd_enable == "enable"): - if self.bfd_enable == "enable": - xml_str = "true" - self.updates_cmd.append("bfd") - else: - xml_str = "false" - self.updates_cmd.append("undo bfd") - - # get bfd end state - bfd_state = "disable" - if self.bfd_enable: - bfd_state = self.bfd_enable - elif self.bfd_dict["global"].get("bfdEnable", "false") == "true": - bfd_state = "enable" - - # default_ip - if self.default_ip: - if bfd_state == "enable": - if self.state == "present" and self.default_ip != self.bfd_dict["global"].get("defaultIp"): - xml_str += "%s" % self.default_ip - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("default-ip-address %s" % self.default_ip) - elif self.state == "absent" and self.default_ip == self.bfd_dict["global"].get("defaultIp"): - xml_str += "" - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("undo default-ip-address") - - # tos_exp_dynamic - if self.tos_exp_dynamic is not None: - if bfd_state == "enable": - if self.state == "present" and self.tos_exp_dynamic != int(self.bfd_dict["global"].get("tosExp", "7")): - xml_str += "%s" % self.tos_exp_dynamic - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("tos-exp %s dynamic" % self.tos_exp_dynamic) - elif self.state == "absent" and self.tos_exp_dynamic == int(self.bfd_dict["global"].get("tosExp", "7")): - xml_str += "" - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("undo tos-exp dynamic") - - # tos_exp_static - if self.tos_exp_static is not None: - if bfd_state == "enable": - if self.state == "present" \ - and self.tos_exp_static != int(self.bfd_dict["global"].get("tosExpStatic", "7")): - xml_str += "%s" % self.tos_exp_static - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("tos-exp %s static" % self.tos_exp_static) - elif self.state == "absent" \ - and self.tos_exp_static == int(self.bfd_dict["global"].get("tosExpStatic", "7")): - xml_str += "" - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("undo tos-exp static") - - # delay_up_time - if self.delay_up_time is not None: - if bfd_state == "enable": - delay_time = self.bfd_dict["global"].get("delayUpTimer", "0") - if not delay_time or not delay_time.isdigit(): - delay_time = "0" - if self.state == "present" \ - and self.delay_up_time != int(delay_time): - xml_str += "%s" % self.delay_up_time - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("delay-up %s" % self.delay_up_time) - elif self.state == "absent" \ - and self.delay_up_time == int(delay_time): - xml_str += "" - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("undo delay-up") - - # damp_init_wait_time damp_max_wait_time damp_second_wait_time - if self.damp_init_wait_time is not None and self.damp_second_wait_time is not None \ - and self.damp_second_wait_time is not None: - if bfd_state == "enable": - if self.state == "present": - if self.damp_max_wait_time != int(self.bfd_dict["global"].get("dampMaxWaitTime", "2000")): - xml_str += "%s" % self.damp_max_wait_time - damp_chg = True - if self.damp_init_wait_time != int(self.bfd_dict["global"].get("dampInitWaitTime", "12000")): - xml_str += "%s" % self.damp_init_wait_time - damp_chg = True - if self.damp_second_wait_time != int(self.bfd_dict["global"].get("dampSecondWaitTime", "5000")): - xml_str += "%s" % self.damp_second_wait_time - damp_chg = True - if damp_chg: - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("dampening timer-interval maximum %s initial %s secondary %s" % ( - self.damp_max_wait_time, self.damp_init_wait_time, self.damp_second_wait_time)) - else: - damp_chg = True - if self.damp_max_wait_time != int(self.bfd_dict["global"].get("dampMaxWaitTime", "2000")): - damp_chg = False - if self.damp_init_wait_time != int(self.bfd_dict["global"].get("dampInitWaitTime", "12000")): - damp_chg = False - if self.damp_second_wait_time != int(self.bfd_dict["global"].get("dampSecondWaitTime", "5000")): - damp_chg = False - - if damp_chg: - xml_str += "" - if "bfd" not in self.updates_cmd: - self.updates_cmd.append("bfd") - self.updates_cmd.append("undo dampening timer-interval maximum %s initial %s secondary %s" % ( - self.damp_max_wait_time, self.damp_init_wait_time, self.damp_second_wait_time)) - if xml_str: - return '' + xml_str + '' - else: - return "" - - def netconf_load_config(self, xml_str): - """load bfd config by netconf""" - - if not xml_str: - return - - xml_cfg = """ - - - %s - - """ % xml_str - set_nc_config(self.module, xml_cfg) - self.changed = True - - def check_params(self): - """Check all input params""" - - # check default_ip - if self.default_ip: - if not check_default_ip(self.default_ip): - self.module.fail_json(msg="Error: Default ip is invalid.") - - # check tos_exp_dynamic - if self.tos_exp_dynamic is not None: - if self.tos_exp_dynamic < 0 or self.tos_exp_dynamic > 7: - self.module.fail_json(msg="Error: Session tos_exp_dynamic is not ranges from 0 to 7.") - - # check tos_exp_static - if self.tos_exp_static is not None: - if self.tos_exp_static < 0 or self.tos_exp_static > 7: - self.module.fail_json(msg="Error: Session tos_exp_static is not ranges from 0 to 7.") - - # check damp_init_wait_time - if self.damp_init_wait_time is not None: - if self.damp_init_wait_time < 1 or self.damp_init_wait_time > 3600000: - self.module.fail_json(msg="Error: Session damp_init_wait_time is not ranges from 1 to 3600000.") - - # check damp_max_wait_time - if self.damp_max_wait_time is not None: - if self.damp_max_wait_time < 1 or self.damp_max_wait_time > 3600000: - self.module.fail_json(msg="Error: Session damp_max_wait_time is not ranges from 1 to 3600000.") - - # check damp_second_wait_time - if self.damp_second_wait_time is not None: - if self.damp_second_wait_time < 1 or self.damp_second_wait_time > 3600000: - self.module.fail_json(msg="Error: Session damp_second_wait_time is not ranges from 1 to 3600000.") - - # check delay_up_time - if self.delay_up_time is not None: - if self.delay_up_time < 1 or self.delay_up_time > 600: - self.module.fail_json(msg="Error: Session delay_up_time is not ranges from 1 to 600.") - - def get_proposed(self): - """get proposed info""" - - self.proposed["bfd_enalbe"] = self.bfd_enable - self.proposed["default_ip"] = self.default_ip - self.proposed["tos_exp_dynamic"] = self.tos_exp_dynamic - self.proposed["tos_exp_static"] = self.tos_exp_static - self.proposed["damp_init_wait_time"] = self.damp_init_wait_time - self.proposed["damp_max_wait_time"] = self.damp_max_wait_time - self.proposed["damp_second_wait_time"] = self.damp_second_wait_time - self.proposed["delay_up_time"] = self.delay_up_time - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.bfd_dict: - return - - self.existing["global"] = self.bfd_dict.get("global") - - def get_end_state(self): - """get end state info""" - - bfd_dict = self.get_bfd_dict() - if not bfd_dict: - return - - self.end_state["global"] = bfd_dict.get("global") - if self.existing == self.end_state: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.bfd_dict = self.get_bfd_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - xml_str = self.config_global() - - # update to device - if xml_str: - self.netconf_load_config(xml_str) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - bfd_enable=dict(required=False, type='str', choices=['enable', 'disable']), - default_ip=dict(required=False, type='str'), - tos_exp_dynamic=dict(required=False, type='int'), - tos_exp_static=dict(required=False, type='int'), - damp_init_wait_time=dict(required=False, type='int'), - damp_max_wait_time=dict(required=False, type='int'), - damp_second_wait_time=dict(required=False, type='int'), - delay_up_time=dict(required=False, type='int'), - state=dict(required=False, default='present', choices=['present', 'absent']) - ) - - argument_spec.update(ce_argument_spec) - module = BfdGlobal(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_bfd_session.py b/plugins/modules/network/cloudengine/ce_bfd_session.py deleted file mode 100644 index 34538f6235..0000000000 --- a/plugins/modules/network/cloudengine/ce_bfd_session.py +++ /dev/null @@ -1,658 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_bfd_session -short_description: Manages BFD session configuration on HUAWEI CloudEngine devices. -description: - - Manages BFD session configuration, creates a BFD session or deletes a specified BFD session - on HUAWEI CloudEngine devices. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - session_name: - description: - - Specifies the name of a BFD session. - The value is a string of 1 to 15 case-sensitive characters without spaces. - required: true - create_type: - description: - - BFD session creation mode, the currently created BFD session - only supports static or static auto-negotiation mode. - choices: ['static', 'auto'] - default: static - addr_type: - description: - - Specifies the peer IP address type. - choices: ['ipv4'] - out_if_name: - description: - - Specifies the type and number of the interface bound to the BFD session. - dest_addr: - description: - - Specifies the peer IP address bound to the BFD session. - src_addr: - description: - - Indicates the source IP address carried in BFD packets. - local_discr: - description: - - The BFD session local identifier does not need to be configured when the mode is auto. - remote_discr: - description: - - The BFD session remote identifier does not need to be configured when the mode is auto. - vrf_name: - description: - - Specifies the name of a Virtual Private Network (VPN) instance that is bound to a BFD session. - The value is a string of 1 to 31 case-sensitive characters, spaces not supported. - When double quotation marks are used around the string, spaces are allowed in the string. - The value _public_ is reserved and cannot be used as the VPN instance name. - use_default_ip: - description: - - Indicates the default multicast IP address that is bound to a BFD session. - By default, BFD uses the multicast IP address 224.0.0.184. - You can set the multicast IP address by running the default-ip-address command. - The value is a bool type. - type: bool - default: 'no' - state: - description: - - Determines whether the config should be present or not on the device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' -- name: bfd session module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Configuring Single-hop BFD for Detecting Faults on a Layer 2 Link - ce_bfd_session: - session_name: bfd_l2link - use_default_ip: true - out_if_name: 10GE1/0/1 - local_discr: 163 - remote_discr: 163 - provider: '{{ cli }}' - - - name: Configuring Single-Hop BFD on a VLANIF Interface - ce_bfd_session: - session_name: bfd_vlanif - dest_addr: 10.1.1.6 - out_if_name: Vlanif100 - local_discr: 163 - remote_discr: 163 - provider: '{{ cli }}' - - - name: Configuring Multi-Hop BFD - ce_bfd_session: - session_name: bfd_multi_hop - dest_addr: 10.1.1.1 - local_discr: 163 - remote_discr: 163 - provider: '{{ cli }}' -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "addr_type": null, - "create_type": null, - "dest_addr": null, - "out_if_name": "10GE1/0/1", - "session_name": "bfd_l2link", - "src_addr": null, - "state": "present", - "use_default_ip": true, - "vrf_name": null - } -existing: - description: k/v pairs of existing configuration - returned: always - type: dict - sample: { - "session": {} - } -end_state: - description: k/v pairs of configuration after module execution - returned: always - type: dict - sample: { - "session": { - "addrType": "IPV4", - "createType": "SESS_STATIC", - "destAddr": null, - "outIfName": "10GE1/0/1", - "sessName": "bfd_l2link", - "srcAddr": null, - "useDefaultIp": "true", - "vrfName": null - } - } -updates: - description: commands sent to the device - returned: always - type: list - sample: [ - "bfd bfd_l2link bind peer-ip default-ip interface 10ge1/0/1" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import sys -import socket -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - - -CE_NC_GET_BFD = """ - - - - - - - - - %s - - - - - - - - - - - - - -""" - - -def is_valid_ip_vpn(vpname): - """check ip vpn""" - - if not vpname: - return False - - if vpname == "_public_": - return False - - if len(vpname) < 1 or len(vpname) > 31: - return False - - return True - - -def check_default_ip(ipaddr): - """check the default multicast IP address""" - - # The value ranges from 224.0.0.107 to 224.0.0.250 - if not check_ip_addr(ipaddr): - return False - - if ipaddr.count(".") != 3: - return False - - ips = ipaddr.split(".") - if ips[0] != "224" or ips[1] != "0" or ips[2] != "0": - return False - - if not ips[3].isdigit() or int(ips[3]) < 107 or int(ips[3]) > 250: - return False - - return True - - -def get_interface_type(interface): - """get the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class BfdSession(object): - """Manages BFD Session""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.session_name = self.module.params['session_name'] - self.create_type = self.module.params['create_type'] - self.addr_type = self.module.params['addr_type'] - self.out_if_name = self.module.params['out_if_name'] - self.dest_addr = self.module.params['dest_addr'] - self.src_addr = self.module.params['src_addr'] - self.vrf_name = self.module.params['vrf_name'] - self.use_default_ip = self.module.params['use_default_ip'] - self.state = self.module.params['state'] - self.local_discr = self.module.params['local_discr'] - self.remote_discr = self.module.params['remote_discr'] - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.changed = False - self.bfd_dict = dict() - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - - mutually_exclusive = [('use_default_ip', 'dest_addr')] - self.module = AnsibleModule(argument_spec=self.spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def get_bfd_dict(self): - """bfd config dict""" - - bfd_dict = dict() - bfd_dict["global"] = dict() - bfd_dict["session"] = dict() - conf_str = CE_NC_GET_BFD % self.session_name - - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return bfd_dict - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # get bfd global info - glb = root.find("bfd/bfdSchGlobal") - if glb: - for attr in glb: - bfd_dict["global"][attr.tag] = attr.text - - # get bfd session info - sess = root.find("bfd/bfdCfgSessions/bfdCfgSession") - if sess: - for attr in sess: - bfd_dict["session"][attr.tag] = attr.text - - return bfd_dict - - def is_session_match(self): - """is bfd session match""" - - if not self.bfd_dict["session"] or not self.session_name: - return False - - session = self.bfd_dict["session"] - if self.session_name != session.get("sessName", ""): - return False - - if self.create_type and self.create_type.upper() not in session.get("createType", "").upper(): - return False - - if self.addr_type and self.addr_type != session.get("addrType").lower(): - return False - - if self.dest_addr and self.dest_addr != session.get("destAddr"): - return False - - if self.src_addr and self.src_addr != session.get("srcAddr"): - return False - - if self.out_if_name: - if not session.get("outIfName"): - return False - if self.out_if_name.replace(" ", "").lower() != session.get("outIfName").replace(" ", "").lower(): - return False - - if self.vrf_name and self.vrf_name != session.get("vrfName"): - return False - - if str(self.use_default_ip).lower() != session.get("useDefaultIp"): - return False - - if self.create_type == "static" and self.state == "present": - if str(self.local_discr).lower() != session.get("localDiscr", ""): - return False - if str(self.remote_discr).lower() != session.get("remoteDiscr", ""): - return False - - return True - - def config_session(self): - """configures bfd session""" - - xml_str = "" - cmd_list = list() - discr = list() - - if not self.session_name: - return xml_str - - if self.bfd_dict["global"].get("bfdEnable", "false") != "true": - self.module.fail_json(msg="Error: Please enable BFD globally first.") - - xml_str = "%s" % self.session_name - cmd_session = "bfd %s" % self.session_name - - if self.state == "present": - if not self.bfd_dict["session"]: - # Parameter check - if not self.dest_addr and not self.use_default_ip: - self.module.fail_json( - msg="Error: dest_addr or use_default_ip must be set when bfd session is creating.") - - # Creates a BFD session - if self.create_type == "auto": - xml_str += "SESS_%s" % self.create_type.upper() - else: - xml_str += "SESS_STATIC" - xml_str += "IP" - cmd_session += " bind" - if self.addr_type: - xml_str += "%s" % self.addr_type.upper() - else: - xml_str += "IPV4" - if self.dest_addr: - xml_str += "%s" % self.dest_addr - cmd_session += " peer-%s %s" % ("ipv6" if self.addr_type == "ipv6" else "ip", self.dest_addr) - if self.use_default_ip: - xml_str += "%s" % str(self.use_default_ip).lower() - cmd_session += " peer-ip default-ip" - if self.vrf_name: - xml_str += "%s" % self.vrf_name - cmd_session += " vpn-instance %s" % self.vrf_name - if self.out_if_name: - xml_str += "%s" % self.out_if_name - cmd_session += " interface %s" % self.out_if_name.lower() - if self.src_addr: - xml_str += "%s" % self.src_addr - cmd_session += " source-%s %s" % ("ipv6" if self.addr_type == "ipv6" else "ip", self.src_addr) - - if self.create_type == "auto": - cmd_session += " auto" - else: - xml_str += "%s" % self.local_discr - discr.append("discriminator local %s" % self.local_discr) - xml_str += "%s" % self.remote_discr - discr.append("discriminator remote %s" % self.remote_discr) - - elif not self.is_session_match(): - # Bfd session is not match - self.module.fail_json(msg="Error: The specified BFD configuration view has been created.") - else: - pass - else: # absent - if not self.bfd_dict["session"]: - self.module.fail_json(msg="Error: BFD session is not exist.") - if not self.is_session_match(): - self.module.fail_json(msg="Error: BFD session parameter is invalid.") - - if self.state == "present": - if xml_str.endswith(""): - # no config update - return "" - else: - cmd_list.insert(0, cmd_session) - cmd_list.extend(discr) - self.updates_cmd.extend(cmd_list) - return '' + xml_str\ - + '' - else: # absent - cmd_list.append("undo " + cmd_session) - self.updates_cmd.extend(cmd_list) - return '' + xml_str\ - + '' - - def netconf_load_config(self, xml_str): - """load bfd config by netconf""" - - if not xml_str: - return - - xml_cfg = """ - - - %s - - """ % xml_str - set_nc_config(self.module, xml_cfg) - self.changed = True - - def check_params(self): - """Check all input params""" - - # check session_name - if not self.session_name: - self.module.fail_json(msg="Error: Missing required arguments: session_name.") - - if self.session_name: - if len(self.session_name) < 1 or len(self.session_name) > 15: - self.module.fail_json(msg="Error: Session name is invalid.") - - # check local_discr - # check remote_discr - - if self.local_discr: - if self.local_discr < 1 or self.local_discr > 16384: - self.module.fail_json(msg="Error: Session local_discr is not ranges from 1 to 16384.") - if self.remote_discr: - if self.remote_discr < 1 or self.remote_discr > 4294967295: - self.module.fail_json(msg="Error: Session remote_discr is not ranges from 1 to 4294967295.") - - if self.state == "present" and self.create_type == "static": - if not self.local_discr: - self.module.fail_json(msg="Error: Missing required arguments: local_discr.") - if not self.remote_discr: - self.module.fail_json(msg="Error: Missing required arguments: remote_discr.") - - # check out_if_name - if self.out_if_name: - if not get_interface_type(self.out_if_name): - self.module.fail_json(msg="Error: Session out_if_name is invalid.") - - # check dest_addr - if self.dest_addr: - if not check_ip_addr(self.dest_addr): - self.module.fail_json(msg="Error: Session dest_addr is invalid.") - - # check src_addr - if self.src_addr: - if not check_ip_addr(self.src_addr): - self.module.fail_json(msg="Error: Session src_addr is invalid.") - - # check vrf_name - if self.vrf_name: - if not is_valid_ip_vpn(self.vrf_name): - self.module.fail_json(msg="Error: Session vrf_name is invalid.") - if not self.dest_addr: - self.module.fail_json(msg="Error: vrf_name and dest_addr must set at the same time.") - - # check use_default_ip - if self.use_default_ip and not self.out_if_name: - self.module.fail_json(msg="Error: use_default_ip and out_if_name must set at the same time.") - - def get_proposed(self): - """get proposed info""" - - # base config - self.proposed["session_name"] = self.session_name - self.proposed["create_type"] = self.create_type - self.proposed["addr_type"] = self.addr_type - self.proposed["out_if_name"] = self.out_if_name - self.proposed["dest_addr"] = self.dest_addr - self.proposed["src_addr"] = self.src_addr - self.proposed["vrf_name"] = self.vrf_name - self.proposed["use_default_ip"] = self.use_default_ip - self.proposed["state"] = self.state - self.proposed["local_discr"] = self.local_discr - self.proposed["remote_discr"] = self.remote_discr - - def get_existing(self): - """get existing info""" - - if not self.bfd_dict: - return - - self.existing["session"] = self.bfd_dict.get("session") - - def get_end_state(self): - """get end state info""" - - bfd_dict = self.get_bfd_dict() - if not bfd_dict: - return - - self.end_state["session"] = bfd_dict.get("session") - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.bfd_dict = self.get_bfd_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - xml_str = '' - if self.session_name: - xml_str += self.config_session() - - # update to device - if xml_str: - self.netconf_load_config(xml_str) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - session_name=dict(required=True, type='str'), - create_type=dict(required=False, default='static', type='str', choices=['static', 'auto']), - addr_type=dict(required=False, type='str', choices=['ipv4']), - out_if_name=dict(required=False, type='str'), - dest_addr=dict(required=False, type='str'), - src_addr=dict(required=False, type='str'), - vrf_name=dict(required=False, type='str'), - use_default_ip=dict(required=False, type='bool', default=False), - state=dict(required=False, default='present', choices=['present', 'absent']), - local_discr=dict(required=False, type='int'), - remote_discr=dict(required=False, type='int') - ) - - argument_spec.update(ce_argument_spec) - module = BfdSession(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_bfd_view.py b/plugins/modules/network/cloudengine/ce_bfd_view.py deleted file mode 100644 index 6f7fe79807..0000000000 --- a/plugins/modules/network/cloudengine/ce_bfd_view.py +++ /dev/null @@ -1,565 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_bfd_view -short_description: Manages BFD session view configuration on HUAWEI CloudEngine devices. -description: - - Manages BFD session view configuration on HUAWEI CloudEngine devices. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - session_name: - description: - - Specifies the name of a BFD session. - The value is a string of 1 to 15 case-sensitive characters without spaces. - required: true - local_discr: - description: - - Specifies the local discriminator of a BFD session. - The value is an integer that ranges from 1 to 16384. - remote_discr: - description: - - Specifies the remote discriminator of a BFD session. - The value is an integer that ranges from 1 to 4294967295. - min_tx_interval: - description: - - Specifies the minimum interval for receiving BFD packets. - The value is an integer that ranges from 50 to 1000, in milliseconds. - min_rx_interval: - description: - - Specifies the minimum interval for sending BFD packets. - The value is an integer that ranges from 50 to 1000, in milliseconds. - detect_multi: - description: - - Specifies the local detection multiplier of a BFD session. - The value is an integer that ranges from 3 to 50. - wtr_interval: - description: - - Specifies the WTR time of a BFD session. - The value is an integer that ranges from 1 to 60, in minutes. - The default value is 0. - tos_exp: - description: - - Specifies a priority for BFD control packets. - The value is an integer ranging from 0 to 7. - The default value is 7, which is the highest priority. - admin_down: - description: - - Enables the BFD session to enter the AdminDown state. - By default, a BFD session is enabled. - The default value is bool type. - type: bool - default: 'no' - description: - description: - - Specifies the description of a BFD session. - The value is a string of 1 to 51 case-sensitive characters with spaces. - state: - description: - - Determines whether the config should be present or not on the device. - default: present - choices: ['present', 'absent'] -extends_documentation_fragment: -- community.general.ce - -''' - -EXAMPLES = ''' -- name: bfd view module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Set the local discriminator of a BFD session to 80 and the remote discriminator to 800 - ce_bfd_view: - session_name: atob - local_discr: 80 - remote_discr: 800 - state: present - provider: '{{ cli }}' - - - name: Set the minimum interval for receiving BFD packets to 500 ms - ce_bfd_view: - session_name: atob - min_rx_interval: 500 - state: present - provider: '{{ cli }}' -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "admin_down": false, - "description": null, - "detect_multi": null, - "local_discr": 80, - "min_rx_interval": null, - "min_tx_interval": null, - "remote_discr": 800, - "session_name": "atob", - "state": "present", - "tos_exp": null, - "wtr_interval": null - } -existing: - description: k/v pairs of existing configuration - returned: always - type: dict - sample: { - "session": { - "adminDown": "false", - "createType": "SESS_STATIC", - "description": null, - "detectMulti": "3", - "localDiscr": null, - "minRxInt": null, - "minTxInt": null, - "remoteDiscr": null, - "sessName": "atob", - "tosExp": null, - "wtrTimerInt": null - } - } -end_state: - description: k/v pairs of configuration after module execution - returned: always - type: dict - sample: { - "session": { - "adminDown": "false", - "createType": "SESS_STATIC", - "description": null, - "detectMulti": "3", - "localDiscr": "80", - "minRxInt": null, - "minTxInt": null, - "remoteDiscr": "800", - "sessName": "atob", - "tosExp": null, - "wtrTimerInt": null - } - } -updates: - description: commands sent to the device - returned: always - type: list - sample: [ - "bfd atob", - "discriminator local 80", - "discriminator remote 800" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import sys -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_BFD = """ - - - %s - - -""" - -CE_NC_GET_BFD_GLB = """ - - - -""" - -CE_NC_GET_BFD_SESSION = """ - - - %s - - - - - - - - - - - - -""" - - -class BfdView(object): - """Manages BFD View""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.session_name = self.module.params['session_name'] - self.local_discr = self.module.params['local_discr'] - self.remote_discr = self.module.params['remote_discr'] - self.min_tx_interval = self.module.params['min_tx_interval'] - self.min_rx_interval = self.module.params['min_rx_interval'] - self.detect_multi = self.module.params['detect_multi'] - self.wtr_interval = self.module.params['wtr_interval'] - self.tos_exp = self.module.params['tos_exp'] - self.admin_down = self.module.params['admin_down'] - self.description = self.module.params['description'] - self.state = self.module.params['state'] - - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.changed = False - self.bfd_dict = dict() - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - - self.module = AnsibleModule(argument_spec=self.spec, - supports_check_mode=True) - - def get_bfd_dict(self): - """bfd config dict""" - - bfd_dict = dict() - bfd_dict["global"] = dict() - bfd_dict["session"] = dict() - conf_str = CE_NC_GET_BFD % (CE_NC_GET_BFD_GLB + (CE_NC_GET_BFD_SESSION % self.session_name)) - - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return bfd_dict - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # get bfd global info - glb = root.find("bfd/bfdSchGlobal") - if glb: - for attr in glb: - bfd_dict["global"][attr.tag] = attr.text - - # get bfd session info - sess = root.find("bfd/bfdCfgSessions/bfdCfgSession") - if sess: - for attr in sess: - bfd_dict["session"][attr.tag] = attr.text - - return bfd_dict - - def config_session(self): - """configures bfd session""" - - xml_str = "" - cmd_list = list() - cmd_session = "" - - if not self.session_name: - return xml_str - - if self.bfd_dict["global"].get("bfdEnable", "false") != "true": - self.module.fail_json(msg="Error: Please enable BFD globally first.") - - if not self.bfd_dict["session"]: - self.module.fail_json(msg="Error: BFD session is not exist.") - - session = self.bfd_dict["session"] - xml_str = "%s" % self.session_name - cmd_session = "bfd %s" % self.session_name - - # BFD session view - if self.local_discr is not None: - if self.state == "present" and str(self.local_discr) != session.get("localDiscr"): - xml_str += "%s" % self.local_discr - cmd_list.append("discriminator local %s" % self.local_discr) - elif self.state == "absent" and str(self.local_discr) == session.get("localDiscr"): - xml_str += "" - cmd_list.append("undo discriminator local") - - if self.remote_discr is not None: - if self.state == "present" and str(self.remote_discr) != session.get("remoteDiscr"): - xml_str += "%s" % self.remote_discr - cmd_list.append("discriminator remote %s" % self.remote_discr) - elif self.state == "absent" and str(self.remote_discr) == session.get("remoteDiscr"): - xml_str += "" - cmd_list.append("undo discriminator remote") - - if self.min_tx_interval is not None: - if self.state == "present" and str(self.min_tx_interval) != session.get("minTxInt"): - xml_str += "%s" % self.min_tx_interval - cmd_list.append("min-tx-interval %s" % self.min_tx_interval) - elif self.state == "absent" and str(self.min_tx_interval) == session.get("minTxInt"): - xml_str += "" - cmd_list.append("undo min-tx-interval") - - if self.min_rx_interval is not None: - if self.state == "present" and str(self.min_rx_interval) != session.get("minRxInt"): - xml_str += "%s" % self.min_rx_interval - cmd_list.append("min-rx-interval %s" % self.min_rx_interval) - elif self.state == "absent" and str(self.min_rx_interval) == session.get("minRxInt"): - xml_str += "" - cmd_list.append("undo min-rx-interval") - - if self.detect_multi is not None: - if self.state == "present" and str(self.detect_multi) != session.get("detectMulti"): - xml_str += " %s" % self.detect_multi - cmd_list.append("detect-multiplier %s" % self.detect_multi) - elif self.state == "absent" and str(self.detect_multi) == session.get("detectMulti"): - xml_str += " " - cmd_list.append("undo detect-multiplier") - - if self.wtr_interval is not None: - if self.state == "present" and str(self.wtr_interval) != session.get("wtrTimerInt"): - xml_str += " %s" % self.wtr_interval - cmd_list.append("wtr %s" % self.wtr_interval) - elif self.state == "absent" and str(self.wtr_interval) == session.get("wtrTimerInt"): - xml_str += " " - cmd_list.append("undo wtr") - - if self.tos_exp is not None: - if self.state == "present" and str(self.tos_exp) != session.get("tosExp"): - xml_str += " %s" % self.tos_exp - cmd_list.append("tos-exp %s" % self.tos_exp) - elif self.state == "absent" and str(self.tos_exp) == session.get("tosExp"): - xml_str += " " - cmd_list.append("undo tos-exp") - - if self.admin_down and session.get("adminDown", "false") == "false": - xml_str += " true" - cmd_list.append("shutdown") - elif not self.admin_down and session.get("adminDown", "false") == "true": - xml_str += " false" - cmd_list.append("undo shutdown") - - if self.description: - if self.state == "present" and self.description != session.get("description"): - xml_str += "%s" % self.description - cmd_list.append("description %s" % self.description) - elif self.state == "absent" and self.description == session.get("description"): - xml_str += "" - cmd_list.append("undo description") - - if xml_str.endswith(""): - # no config update - return "" - else: - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - return '' + xml_str\ - + '' - - def netconf_load_config(self, xml_str): - """load bfd config by netconf""" - - if not xml_str: - return - - xml_cfg = """ - - - %s - - """ % xml_str - - set_nc_config(self.min_rx_interval, xml_cfg) - self.changed = True - - def check_params(self): - """Check all input params""" - - # check session_name - if not self.session_name: - self.module.fail_json(msg="Error: Missing required arguments: session_name.") - - if self.session_name: - if len(self.session_name) < 1 or len(self.session_name) > 15: - self.module.fail_json(msg="Error: Session name is invalid.") - - # check local_discr - if self.local_discr is not None: - if self.local_discr < 1 or self.local_discr > 16384: - self.module.fail_json(msg="Error: Session local_discr is not ranges from 1 to 16384.") - - # check remote_discr - if self.remote_discr is not None: - if self.remote_discr < 1 or self.remote_discr > 4294967295: - self.module.fail_json(msg="Error: Session remote_discr is not ranges from 1 to 4294967295.") - - # check min_tx_interval - if self.min_tx_interval is not None: - if self.min_tx_interval < 50 or self.min_tx_interval > 1000: - self.module.fail_json(msg="Error: Session min_tx_interval is not ranges from 50 to 1000.") - - # check min_rx_interval - if self.min_rx_interval is not None: - if self.min_rx_interval < 50 or self.min_rx_interval > 1000: - self.module.fail_json(msg="Error: Session min_rx_interval is not ranges from 50 to 1000.") - - # check detect_multi - if self.detect_multi is not None: - if self.detect_multi < 3 or self.detect_multi > 50: - self.module.fail_json(msg="Error: Session detect_multi is not ranges from 3 to 50.") - - # check wtr_interval - if self.wtr_interval is not None: - if self.wtr_interval < 1 or self.wtr_interval > 60: - self.module.fail_json(msg="Error: Session wtr_interval is not ranges from 1 to 60.") - - # check tos_exp - if self.tos_exp is not None: - if self.tos_exp < 0 or self.tos_exp > 7: - self.module.fail_json(msg="Error: Session tos_exp is not ranges from 0 to 7.") - - # check description - if self.description: - if len(self.description) < 1 or len(self.description) > 51: - self.module.fail_json(msg="Error: Session description is invalid.") - - def get_proposed(self): - """get proposed info""" - - # base config - self.proposed["session_name"] = self.session_name - self.proposed["local_discr"] = self.local_discr - self.proposed["remote_discr"] = self.remote_discr - self.proposed["min_tx_interval"] = self.min_tx_interval - self.proposed["min_rx_interval"] = self.min_rx_interval - self.proposed["detect_multi"] = self.detect_multi - self.proposed["wtr_interval"] = self.wtr_interval - self.proposed["tos_exp"] = self.tos_exp - self.proposed["admin_down"] = self.admin_down - self.proposed["description"] = self.description - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.bfd_dict: - return - - self.existing["session"] = self.bfd_dict.get("session") - - def get_end_state(self): - """get end state info""" - - bfd_dict = self.get_bfd_dict() - if not bfd_dict: - return - - self.end_state["session"] = bfd_dict.get("session") - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.bfd_dict = self.get_bfd_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - xml_str = '' - if self.session_name: - xml_str += self.config_session() - - # update to device - if xml_str: - self.netconf_load_config(xml_str) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - session_name=dict(required=True, type='str'), - local_discr=dict(required=False, type='int'), - remote_discr=dict(required=False, type='int'), - min_tx_interval=dict(required=False, type='int'), - min_rx_interval=dict(required=False, type='int'), - detect_multi=dict(required=False, type='int'), - wtr_interval=dict(required=False, type='int'), - tos_exp=dict(required=False, type='int'), - admin_down=dict(required=False, type='bool', default=False), - description=dict(required=False, type='str'), - state=dict(required=False, default='present', choices=['present', 'absent']) - ) - - argument_spec.update(ce_argument_spec) - module = BfdView(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_bgp.py b/plugins/modules/network/cloudengine/ce_bgp.py deleted file mode 100644 index d4dcc87031..0000000000 --- a/plugins/modules/network/cloudengine/ce_bgp.py +++ /dev/null @@ -1,2331 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_bgp -short_description: Manages BGP configuration on HUAWEI CloudEngine switches. -description: - - Manages BGP configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] - as_number: - description: - - Local AS number. - The value is a string of 1 to 11 characters. - graceful_restart: - description: - - Enable GR of the BGP speaker in the specified address family, peer address, or peer group. - default: no_use - choices: ['no_use','true','false'] - time_wait_for_rib: - description: - - Period of waiting for the End-Of-RIB flag. - The value is an integer ranging from 3 to 3000. The default value is 600. - as_path_limit: - description: - - Maximum number of AS numbers in the AS_Path attribute. The default value is 255. - check_first_as: - description: - - Check the first AS in the AS_Path of the update messages from EBGP peers. - default: no_use - choices: ['no_use','true','false'] - confed_id_number: - description: - - Confederation ID. - The value is a string of 1 to 11 characters. - confed_nonstanded: - description: - - Configure the device to be compatible with devices in a nonstandard confederation. - default: no_use - choices: ['no_use','true','false'] - bgp_rid_auto_sel: - description: - - The function to automatically select router IDs for all VPN BGP instances is enabled. - default: no_use - choices: ['no_use','true','false'] - keep_all_routes: - description: - - If the value is true, the system stores all route update messages received from all peers (groups) after - BGP connection setup. - If the value is false, the system stores only BGP update messages that are received from peers and pass - the configured import policy. - default: no_use - choices: ['no_use','true','false'] - memory_limit: - description: - - Support BGP RIB memory protection. - default: no_use - choices: ['no_use','true','false'] - gr_peer_reset: - description: - - Peer disconnection through GR. - default: no_use - choices: ['no_use','true','false'] - is_shutdown: - description: - - Interrupt BGP all neighbor. - default: no_use - choices: ['no_use','true','false'] - suppress_interval: - description: - - Suppress interval. - hold_interval: - description: - - Hold interval. - clear_interval: - description: - - Clear interval. - confed_peer_as_num: - description: - - Confederation AS number, in two-byte or four-byte format. - The value is a string of 1 to 11 characters. - vrf_name: - description: - - Name of a BGP instance. The name is a case-sensitive string of characters. - vrf_rid_auto_sel: - description: - - If the value is true, VPN BGP instances are enabled to automatically select router IDs. - If the value is false, VPN BGP instances are disabled from automatically selecting router IDs. - default: no_use - choices: ['no_use','true','false'] - router_id: - description: - - ID of a router that is in IPv4 address format. - keepalive_time: - description: - - If the value of a timer changes, the BGP peer relationship between the routers is disconnected. - The value is an integer ranging from 0 to 21845. The default value is 60. - hold_time: - description: - - Hold time, in seconds. The value of the hold time can be 0 or range from 3 to 65535. - min_hold_time: - description: - - Min hold time, in seconds. The value of the hold time can be 0 or range from 20 to 65535. - conn_retry_time: - description: - - ConnectRetry interval. The value is an integer, in seconds. The default value is 32s. - ebgp_if_sensitive: - description: - - If the value is true, After the fast EBGP interface awareness function is enabled, EBGP sessions on - an interface are deleted immediately when the interface goes Down. - If the value is false, After the fast EBGP interface awareness function is enabled, EBGP sessions - on an interface are not deleted immediately when the interface goes Down. - default: no_use - choices: ['no_use','true','false'] - default_af_type: - description: - - Type of a created address family, which can be IPv4 unicast or IPv6 unicast. - The default type is IPv4 unicast. - choices: ['ipv4uni','ipv6uni'] -''' - -EXAMPLES = ''' - -- name: CloudEngine BGP test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Enable BGP" - ce_bgp: - state: present - as_number: 100 - confed_id_number: 250 - provider: "{{ cli }}" - - - name: "Disable BGP" - ce_bgp: - state: absent - as_number: 100 - confed_id_number: 250 - provider: "{{ cli }}" - - - name: "Create confederation peer AS num" - ce_bgp: - state: present - confed_peer_as_num: 260 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"as_number": "100", state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"bgp_enable": [["100"], ["true"]]} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"bgp_enable": [["100"], ["true"]]} -updates: - description: command sent to the device - returned: always - type: list - sample: ["bgp 100"] -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -SUCCESS = """success""" -FAILED = """failed""" - - -# get bgp enable -CE_GET_BGP_ENABLE = """ - - - - - - - - - - -""" - -CE_GET_BGP_ENABLE_HEADER = """ - - - - -""" - -CE_GET_BGP_ENABLE_TAIL = """ - - - - -""" - -# merge bgp enable -CE_MERGE_BGP_ENABLE_HEADER = """ - - - - -""" -CE_MERGE_BGP_ENABLE_TAIL = """ - - - - -""" - -# get bgp confederation peer as -CE_GET_BGP_CONFED_PEER_AS = """ - - - - - - - - - - - -""" - -# merge bgp confederation peer as -CE_MERGE_BGP_CONFED_PEER_AS = """ - - - - - - %s - - - - - -""" - -# create bgp confederation peer as -CE_CREATE_BGP_CONFED_PEER_AS = """ - - - - - - %s - - - - - -""" - -# delete bgp confederation peer as -CE_DELETE_BGP_CONFED_PEER_AS = """ - - - - - - %s - - - - - -""" - -# get bgp instance -CE_GET_BGP_INSTANCE = """ - - - - - - - - - - - -""" - -# get bgp instance -CE_GET_BGP_INSTANCE_HEADER = """ - - - - - -""" -CE_GET_BGP_INSTANCE_TAIL = """ - - - - - -""" - -# merge bgp instance -CE_MERGE_BGP_INSTANCE_HEADER = """ - - - - - -""" -CE_MERGE_BGP_INSTANCE_TAIL = """ - - - - - -""" - -# create bgp instance -CE_CREATE_BGP_INSTANCE_HEADER = """ - - - - - -""" -CE_CREATE_BGP_INSTANCE_TAIL = """ - - - - - -""" - -# delete bgp instance -CE_DELETE_BGP_INSTANCE_HEADER = """ - - - - - -""" -CE_DELETE_BGP_INSTANCE_TAIL = """ - - - - - -""" - - -def check_ip_addr(**kwargs): - """ check_ip_addr """ - - ipaddr = kwargs["ipaddr"] - - addr = ipaddr.strip().split('.') - - if len(addr) != 4: - return FAILED - - for i in range(4): - addr[i] = int(addr[i]) - - if addr[i] <= 255 and addr[i] >= 0: - pass - else: - return FAILED - return SUCCESS - - -def check_bgp_enable_args(**kwargs): - """ check_bgp_enable_args """ - - module = kwargs["module"] - - need_cfg = False - - as_number = module.params['as_number'] - if as_number: - if len(as_number) > 11 or len(as_number) == 0: - module.fail_json( - msg='Error: The len of as_number %s is out of [1 - 11].' % as_number) - else: - need_cfg = True - - return need_cfg - - -def check_bgp_confed_args(**kwargs): - """ check_bgp_confed_args """ - - module = kwargs["module"] - - need_cfg = False - - confed_peer_as_num = module.params['confed_peer_as_num'] - if confed_peer_as_num: - if len(confed_peer_as_num) > 11 or len(confed_peer_as_num) == 0: - module.fail_json( - msg='Error: The len of confed_peer_as_num %s is out of [1 - 11].' % confed_peer_as_num) - else: - need_cfg = True - - return need_cfg - - -class Bgp(object): - """ Manages BGP configuration """ - - def netconf_get_config(self, **kwargs): - """ netconf_get_config """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = get_nc_config(module, conf_str) - - return xml_str - - def netconf_set_config(self, **kwargs): - """ netconf_set_config """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = set_nc_config(module, conf_str) - - return xml_str - - def check_bgp_enable_other_args(self, **kwargs): - """ check_bgp_enable_other_args """ - - module = kwargs["module"] - state = module.params['state'] - result = dict() - need_cfg = False - - graceful_restart = module.params['graceful_restart'] - if graceful_restart != 'no_use': - - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["graceful_restart"] = re_find - if re_find[0] != graceful_restart: - need_cfg = True - else: - need_cfg = True - - time_wait_for_rib = module.params['time_wait_for_rib'] - if time_wait_for_rib: - if int(time_wait_for_rib) > 3000 or int(time_wait_for_rib) < 3: - module.fail_json( - msg='Error: The time_wait_for_rib %s is out of [3 - 3000].' % time_wait_for_rib) - else: - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["time_wait_for_rib"] = re_find - if re_find[0] != time_wait_for_rib: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["time_wait_for_rib"] = re_find - if re_find[0] == time_wait_for_rib: - need_cfg = True - - as_path_limit = module.params['as_path_limit'] - if as_path_limit: - if int(as_path_limit) > 2000 or int(as_path_limit) < 1: - module.fail_json( - msg='Error: The as_path_limit %s is out of [1 - 2000].' % as_path_limit) - else: - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["as_path_limit"] = re_find - if re_find[0] != as_path_limit: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["as_path_limit"] = re_find - if re_find[0] == as_path_limit: - need_cfg = True - - check_first_as = module.params['check_first_as'] - if check_first_as != 'no_use': - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["check_first_as"] = re_find - if re_find[0] != check_first_as: - need_cfg = True - else: - need_cfg = True - - confed_id_number = module.params['confed_id_number'] - if confed_id_number: - if len(confed_id_number) > 11 or len(confed_id_number) == 0: - module.fail_json( - msg='Error: The len of confed_id_number %s is out of [1 - 11].' % confed_id_number) - else: - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["confed_id_number"] = re_find - if re_find[0] != confed_id_number: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["confed_id_number"] = re_find - if re_find[0] == confed_id_number: - need_cfg = True - - confed_nonstanded = module.params['confed_nonstanded'] - if confed_nonstanded != 'no_use': - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["confed_nonstanded"] = re_find - if re_find[0] != confed_nonstanded: - need_cfg = True - else: - need_cfg = True - - bgp_rid_auto_sel = module.params['bgp_rid_auto_sel'] - if bgp_rid_auto_sel != 'no_use': - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["bgp_rid_auto_sel"] = re_find - if re_find[0] != bgp_rid_auto_sel: - need_cfg = True - else: - need_cfg = True - - keep_all_routes = module.params['keep_all_routes'] - if keep_all_routes != 'no_use': - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["keep_all_routes"] = re_find - if re_find[0] != keep_all_routes: - need_cfg = True - else: - need_cfg = True - - memory_limit = module.params['memory_limit'] - if memory_limit != 'no_use': - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["memory_limit"] = re_find - if re_find[0] != memory_limit: - need_cfg = True - else: - need_cfg = True - - gr_peer_reset = module.params['gr_peer_reset'] - if gr_peer_reset != 'no_use': - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["gr_peer_reset"] = re_find - if re_find[0] != gr_peer_reset: - need_cfg = True - else: - need_cfg = True - - is_shutdown = module.params['is_shutdown'] - if is_shutdown != 'no_use': - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_shutdown"] = re_find - if re_find[0] != is_shutdown: - need_cfg = True - else: - need_cfg = True - - suppress_interval = module.params['suppress_interval'] - hold_interval = module.params['hold_interval'] - clear_interval = module.params['clear_interval'] - if suppress_interval: - - if not hold_interval or not clear_interval: - module.fail_json( - msg='Error: Please input suppress_interval hold_interval clear_interval at the same time.') - - if int(suppress_interval) > 65535 or int(suppress_interval) < 1: - module.fail_json( - msg='Error: The suppress_interval %s is out of [1 - 65535].' % suppress_interval) - else: - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["suppress_interval"] = re_find - if re_find[0] != suppress_interval: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["suppress_interval"] = re_find - if re_find[0] == suppress_interval: - need_cfg = True - - if hold_interval: - - if not suppress_interval or not clear_interval: - module.fail_json( - msg='Error: Please input suppress_interval hold_interval clear_interval at the same time.') - - if int(hold_interval) > 65535 or int(hold_interval) < 1: - module.fail_json( - msg='Error: The hold_interval %s is out of [1 - 65535].' % hold_interval) - else: - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["hold_interval"] = re_find - if re_find[0] != hold_interval: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["hold_interval"] = re_find - if re_find[0] == hold_interval: - need_cfg = True - - if clear_interval: - - if not suppress_interval or not hold_interval: - module.fail_json( - msg='Error: Please input suppress_interval hold_interval clear_interval at the same time.') - - if int(clear_interval) > 65535 or int(clear_interval) < 1: - module.fail_json( - msg='Error: The clear_interval %s is out of [1 - 65535].' % clear_interval) - else: - conf_str = CE_GET_BGP_ENABLE_HEADER + \ - "" + CE_GET_BGP_ENABLE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["clear_interval"] = re_find - if re_find[0] != clear_interval: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["clear_interval"] = re_find - if re_find[0] == clear_interval: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def check_bgp_instance_args(self, **kwargs): - """ check_bgp_instance_args """ - - module = kwargs["module"] - state = module.params['state'] - need_cfg = False - - vrf_name = module.params['vrf_name'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='the len of vrf_name %s is out of [1 - 31].' % vrf_name) - conf_str = CE_GET_BGP_INSTANCE_HEADER + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - check_vrf_name = vrf_name - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if check_vrf_name not in re_find: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if check_vrf_name in re_find: - need_cfg = True - - return need_cfg - - def check_bgp_instance_other_args(self, **kwargs): - """ check_bgp_instance_other_args """ - - module = kwargs["module"] - state = module.params['state'] - result = dict() - need_cfg = False - - vrf_name = module.params['vrf_name'] - - router_id = module.params['router_id'] - if router_id: - - if not vrf_name: - module.fail_json( - msg='Error: Please input vrf_name.') - - if check_ip_addr(ipaddr=router_id) == FAILED: - module.fail_json( - msg='Error: The router_id %s is invalid.' % router_id) - - conf_str = CE_GET_BGP_INSTANCE_HEADER + "%s" % vrf_name + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["router_id"] = re_find - if re_find[0] != router_id: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["router_id"] = re_find - if re_find[0] == router_id: - need_cfg = True - - vrf_rid_auto_sel = module.params['vrf_rid_auto_sel'] - if vrf_rid_auto_sel != 'no_use': - - if not vrf_name: - module.fail_json( - msg='Error: Please input vrf_name.') - - conf_str = CE_GET_BGP_INSTANCE_HEADER + "%s" % vrf_name + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["vrf_rid_auto_sel"] = re_find - - if re_find[0] != vrf_rid_auto_sel: - need_cfg = True - else: - need_cfg = True - - keepalive_time = module.params['keepalive_time'] - if keepalive_time: - - if not vrf_name: - module.fail_json( - msg='Error: Please input vrf_name.') - - if int(keepalive_time) > 21845 or int(keepalive_time) < 0: - module.fail_json( - msg='keepalive_time %s is out of [0 - 21845].' % keepalive_time) - else: - conf_str = CE_GET_BGP_INSTANCE_HEADER + "%s" % vrf_name + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["keepalive_time"] = re_find - if re_find[0] != keepalive_time: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["keepalive_time"] = re_find - if re_find[0] == keepalive_time: - need_cfg = True - - hold_time = module.params['hold_time'] - if hold_time: - - if not vrf_name: - module.fail_json( - msg='Error: Please input vrf_name.') - - if int(hold_time) > 65535 or int(hold_time) < 3: - module.fail_json( - msg='hold_time %s is out of [3 - 65535].' % hold_time) - else: - conf_str = CE_GET_BGP_INSTANCE_HEADER + "%s" % vrf_name + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["hold_time"] = re_find - if re_find[0] != hold_time: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["hold_time"] = re_find - if re_find[0] == hold_time: - need_cfg = True - - min_hold_time = module.params['min_hold_time'] - if min_hold_time: - - if not vrf_name: - module.fail_json( - msg='Error: Please input vrf_name.') - - if int(min_hold_time) != 0 and (int(min_hold_time) > 65535 or int(min_hold_time) < 20): - module.fail_json( - msg='min_hold_time %s is out of [0, or 20 - 65535].' % min_hold_time) - else: - conf_str = CE_GET_BGP_INSTANCE_HEADER + "%s" % vrf_name + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["min_hold_time"] = re_find - if re_find[0] != min_hold_time: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["min_hold_time"] = re_find - if re_find[0] == min_hold_time: - need_cfg = True - - conn_retry_time = module.params['conn_retry_time'] - if conn_retry_time: - - if not vrf_name: - module.fail_json( - msg='Error: Please input vrf_name.') - - if int(conn_retry_time) > 65535 or int(conn_retry_time) < 1: - module.fail_json( - msg='conn_retry_time %s is out of [1 - 65535].' % conn_retry_time) - else: - conf_str = CE_GET_BGP_INSTANCE_HEADER + "%s" % vrf_name + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config( - module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["conn_retry_time"] = re_find - if re_find[0] != conn_retry_time: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["conn_retry_time"] = re_find - if re_find[0] == conn_retry_time: - need_cfg = True - else: - pass - - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - if ebgp_if_sensitive != 'no_use': - - if not vrf_name: - module.fail_json( - msg='Error: Please input vrf_name.') - - conf_str = CE_GET_BGP_INSTANCE_HEADER + "%s" % vrf_name + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ebgp_if_sensitive"] = re_find - if re_find[0] != ebgp_if_sensitive: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ebgp_if_sensitive"] = re_find - if re_find[0] == ebgp_if_sensitive: - need_cfg = True - else: - pass - - default_af_type = module.params['default_af_type'] - if default_af_type: - - if not vrf_name: - module.fail_json( - msg='Error: Please input vrf_name.') - - conf_str = CE_GET_BGP_INSTANCE_HEADER + "%s" % vrf_name + \ - "" + CE_GET_BGP_INSTANCE_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["default_af_type"] = re_find - if re_find[0] != default_af_type: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["default_af_type"] = re_find - if re_find[0] == default_af_type: - need_cfg = True - else: - pass - - result["need_cfg"] = need_cfg - return result - - def get_bgp_enable(self, **kwargs): - """ get_bgp_enable """ - - module = kwargs["module"] - - conf_str = CE_GET_BGP_ENABLE - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*\s*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_bgp_enable(self, **kwargs): - """ merge_bgp_enable """ - - module = kwargs["module"] - conf_str = CE_MERGE_BGP_ENABLE_HEADER - - state = module.params['state'] - - if state == "present": - conf_str += "true" - else: - conf_str += "false" - - as_number = module.params['as_number'] - if as_number: - conf_str += "%s" % as_number - - conf_str += CE_MERGE_BGP_ENABLE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp enable failed.') - - cmds = [] - if state == "present": - cmd = "bgp %s" % as_number - else: - cmd = "undo bgp %s" % as_number - cmds.append(cmd) - - return cmds - - def merge_bgp_enable_other(self, **kwargs): - """ merge_bgp_enable_other """ - - module = kwargs["module"] - conf_str = CE_MERGE_BGP_ENABLE_HEADER - - cmds = [] - - graceful_restart = module.params['graceful_restart'] - if graceful_restart != 'no_use': - conf_str += "%s" % graceful_restart - - if graceful_restart == "true": - cmd = "graceful-restart" - else: - cmd = "undo graceful-restart" - cmds.append(cmd) - - time_wait_for_rib = module.params['time_wait_for_rib'] - if time_wait_for_rib: - conf_str += "%s" % time_wait_for_rib - - cmd = "graceful-restart timer wait-for-rib %s" % time_wait_for_rib - cmds.append(cmd) - - as_path_limit = module.params['as_path_limit'] - if as_path_limit: - conf_str += "%s" % as_path_limit - - cmd = "as-path-limit %s" % as_path_limit - cmds.append(cmd) - - check_first_as = module.params['check_first_as'] - if check_first_as != 'no_use': - conf_str += "%s" % check_first_as - - if check_first_as == "true": - cmd = "check-first-as" - else: - cmd = "undo check-first-as" - cmds.append(cmd) - - confed_id_number = module.params['confed_id_number'] - if confed_id_number: - conf_str += "%s" % confed_id_number - - cmd = "confederation id %s" % confed_id_number - cmds.append(cmd) - - confed_nonstanded = module.params['confed_nonstanded'] - if confed_nonstanded != 'no_use': - conf_str += "%s" % confed_nonstanded - - if confed_nonstanded == "true": - cmd = "confederation nonstandard" - else: - cmd = "undo confederation nonstandard" - cmds.append(cmd) - - bgp_rid_auto_sel = module.params['bgp_rid_auto_sel'] - if bgp_rid_auto_sel != 'no_use': - conf_str += "%s" % bgp_rid_auto_sel - - if bgp_rid_auto_sel == "true": - cmd = "router-id vpn-instance auto-select" - else: - cmd = "undo router-id" - cmds.append(cmd) - - keep_all_routes = module.params['keep_all_routes'] - if keep_all_routes != 'no_use': - conf_str += "%s" % keep_all_routes - - if keep_all_routes == "true": - cmd = "keep-all-routes" - else: - cmd = "undo keep-all-routes" - cmds.append(cmd) - - memory_limit = module.params['memory_limit'] - if memory_limit != 'no_use': - conf_str += "%s" % memory_limit - - if memory_limit == "true": - cmd = "prefix memory-limit" - else: - cmd = "undo prefix memory-limit" - cmds.append(cmd) - - gr_peer_reset = module.params['gr_peer_reset'] - if gr_peer_reset != 'no_use': - conf_str += "%s" % gr_peer_reset - - if gr_peer_reset == "true": - cmd = "graceful-restart peer-reset" - else: - cmd = "undo graceful-restart peer-reset" - cmds.append(cmd) - - is_shutdown = module.params['is_shutdown'] - if is_shutdown != 'no_use': - conf_str += "%s" % is_shutdown - - if is_shutdown == "true": - cmd = "shutdown" - else: - cmd = "undo shutdown" - cmds.append(cmd) - - suppress_interval = module.params['suppress_interval'] - hold_interval = module.params['hold_interval'] - clear_interval = module.params['clear_interval'] - if suppress_interval: - conf_str += "%s" % suppress_interval - - cmd = "nexthop recursive-lookup restrain suppress-interval %s hold-interval %s " \ - "clear-interval %s" % (suppress_interval, hold_interval, clear_interval) - cmds.append(cmd) - - if hold_interval: - conf_str += "%s" % hold_interval - - if clear_interval: - conf_str += "%s" % clear_interval - - conf_str += CE_MERGE_BGP_ENABLE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp enable failed.') - - return cmds - - def delete_bgp_enable_other(self, **kwargs): - """ delete bgp enable other args """ - - module = kwargs["module"] - conf_str = CE_MERGE_BGP_ENABLE_HEADER - - cmds = [] - - graceful_restart = module.params['graceful_restart'] - if graceful_restart != 'no_use': - conf_str += "%s" % graceful_restart - - if graceful_restart == "true": - cmd = "graceful-restart" - else: - cmd = "undo graceful-restart" - cmds.append(cmd) - - time_wait_for_rib = module.params['time_wait_for_rib'] - if time_wait_for_rib: - conf_str += "600" - - cmd = "undo graceful-restart timer wait-for-rib" - cmds.append(cmd) - - as_path_limit = module.params['as_path_limit'] - if as_path_limit: - conf_str += "255" - - cmd = "undo as-path-limit" - cmds.append(cmd) - - check_first_as = module.params['check_first_as'] - if check_first_as != 'no_use': - conf_str += "%s" % check_first_as - - if check_first_as == "true": - cmd = "check-first-as" - else: - cmd = "undo check-first-as" - cmds.append(cmd) - - confed_id_number = module.params['confed_id_number'] - confed_peer_as_num = module.params['confed_peer_as_num'] - if confed_id_number and not confed_peer_as_num: - conf_str += "" - - cmd = "undo confederation id" - cmds.append(cmd) - - confed_nonstanded = module.params['confed_nonstanded'] - if confed_nonstanded != 'no_use': - conf_str += "%s" % confed_nonstanded - - if confed_nonstanded == "true": - cmd = "confederation nonstandard" - else: - cmd = "undo confederation nonstandard" - cmds.append(cmd) - - bgp_rid_auto_sel = module.params['bgp_rid_auto_sel'] - if bgp_rid_auto_sel != 'no_use': - conf_str += "%s" % bgp_rid_auto_sel - - if bgp_rid_auto_sel == "true": - cmd = "router-id vpn-instance auto-select" - else: - cmd = "undo router-id" - cmds.append(cmd) - - keep_all_routes = module.params['keep_all_routes'] - if keep_all_routes != 'no_use': - conf_str += "%s" % keep_all_routes - - if keep_all_routes == "true": - cmd = "keep-all-routes" - else: - cmd = "undo keep-all-routes" - cmds.append(cmd) - - memory_limit = module.params['memory_limit'] - if memory_limit != 'no_use': - conf_str += "%s" % memory_limit - - if memory_limit == "true": - cmd = "prefix memory-limit" - else: - cmd = "undo prefix memory-limit" - cmds.append(cmd) - - gr_peer_reset = module.params['gr_peer_reset'] - if gr_peer_reset != 'no_use': - conf_str += "%s" % gr_peer_reset - - if gr_peer_reset == "true": - cmd = "graceful-restart peer-reset" - else: - cmd = "undo graceful-restart peer-reset" - cmds.append(cmd) - - is_shutdown = module.params['is_shutdown'] - if is_shutdown != 'no_use': - conf_str += "%s" % is_shutdown - - if is_shutdown == "true": - cmd = "shutdown" - else: - cmd = "undo shutdown" - cmds.append(cmd) - - suppress_interval = module.params['suppress_interval'] - hold_interval = module.params['hold_interval'] - clear_interval = module.params['clear_interval'] - if suppress_interval: - conf_str += "60" - - cmd = "undo nexthop recursive-lookup restrain suppress-interval hold-interval clear-interval" - cmds.append(cmd) - - if hold_interval: - conf_str += "120" - - if clear_interval: - conf_str += "600" - - conf_str += CE_MERGE_BGP_ENABLE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete bgp enable failed.') - - return cmds - - def get_bgp_confed_peer_as(self, **kwargs): - """ get_bgp_confed_peer_as """ - - module = kwargs["module"] - - conf_str = CE_GET_BGP_CONFED_PEER_AS - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_bgp_confed_peer_as(self, **kwargs): - """ merge_bgp_confed_peer_as """ - - module = kwargs["module"] - confed_peer_as_num = module.params['confed_peer_as_num'] - - conf_str = CE_MERGE_BGP_CONFED_PEER_AS % confed_peer_as_num - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp confed peer as failed.') - - cmds = [] - cmd = "confederation peer-as %s" % confed_peer_as_num - cmds.append(cmd) - - return cmds - - def create_bgp_confed_peer_as(self, **kwargs): - """ create_bgp_confed_peer_as """ - - module = kwargs["module"] - confed_peer_as_num = module.params['confed_peer_as_num'] - - conf_str = CE_CREATE_BGP_CONFED_PEER_AS % confed_peer_as_num - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create bgp confed peer as failed.') - - cmds = [] - cmd = "confederation peer-as %s" % confed_peer_as_num - cmds.append(cmd) - - return cmds - - def delete_bgp_confed_peer_as(self, **kwargs): - """ delete_bgp_confed_peer_as """ - - module = kwargs["module"] - confed_peer_as_num = module.params['confed_peer_as_num'] - - conf_str = CE_DELETE_BGP_CONFED_PEER_AS % confed_peer_as_num - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete bgp confed peer as failed.') - - cmds = [] - cmd = "undo confederation peer-as %s" % confed_peer_as_num - cmds.append(cmd) - - return cmds - - def get_bgp_instance(self, **kwargs): - """ get_bgp_instance """ - - module = kwargs["module"] - conf_str = CE_GET_BGP_INSTANCE - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_bgp_instance(self, **kwargs): - """ merge_bgp_instance """ - - module = kwargs["module"] - conf_str = CE_MERGE_BGP_INSTANCE_HEADER - - vrf_name = module.params['vrf_name'] - conf_str += "%s" % vrf_name - - conf_str += CE_MERGE_BGP_INSTANCE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp instance failed.') - - cmds = [] - - if vrf_name != "_public_": - cmd = "ipv4-family vpn-instance %s" % vrf_name - cmds.append(cmd) - - return cmds - - def create_bgp_instance(self, **kwargs): - """ create_bgp_instance """ - - module = kwargs["module"] - conf_str = CE_CREATE_BGP_INSTANCE_HEADER - - cmds = [] - - vrf_name = module.params['vrf_name'] - if vrf_name: - if vrf_name == "_public_": - return cmds - conf_str += "%s" % vrf_name - - conf_str += CE_CREATE_BGP_INSTANCE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create bgp instance failed.') - - if vrf_name != "_public_": - cmd = "ipv4-family vpn-instance %s" % vrf_name - cmds.append(cmd) - - return cmds - - def delete_bgp_instance(self, **kwargs): - """ delete_bgp_instance """ - - module = kwargs["module"] - conf_str = CE_DELETE_BGP_INSTANCE_HEADER - - vrf_name = module.params['vrf_name'] - if vrf_name: - conf_str += "%s" % vrf_name - - conf_str += CE_DELETE_BGP_INSTANCE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete bgp instance failed.') - - cmds = [] - if vrf_name != "_public_": - cmd = "undo ipv4-family vpn-instance %s" % vrf_name - cmds.append(cmd) - - return cmds - - def merge_bgp_instance_other(self, **kwargs): - """ merge_bgp_instance_other """ - - module = kwargs["module"] - conf_str = CE_MERGE_BGP_INSTANCE_HEADER - - vrf_name = module.params['vrf_name'] - conf_str += "%s" % vrf_name - - cmds = [] - - default_af_type = module.params['default_af_type'] - if default_af_type: - conf_str += "%s" % default_af_type - - if vrf_name != "_public_": - if default_af_type == "ipv6uni": - cmd = "ipv6-family vpn-instance %s" % vrf_name - cmds.append(cmd) - - vrf_rid_auto_sel = module.params['vrf_rid_auto_sel'] - if vrf_rid_auto_sel != 'no_use': - conf_str += "%s" % vrf_rid_auto_sel - - if vrf_rid_auto_sel == "true": - cmd = "router-id auto-select" - else: - cmd = "undo router-id auto-select" - cmds.append(cmd) - - router_id = module.params['router_id'] - if router_id: - conf_str += "%s" % router_id - - cmd = "router-id %s" % router_id - cmds.append(cmd) - - keepalive_time = module.params['keepalive_time'] - if keepalive_time: - conf_str += "%s" % keepalive_time - - cmd = "timer keepalive %s" % keepalive_time - cmds.append(cmd) - - hold_time = module.params['hold_time'] - if hold_time: - conf_str += "%s" % hold_time - - cmd = "timer hold %s" % hold_time - cmds.append(cmd) - - min_hold_time = module.params['min_hold_time'] - if min_hold_time: - conf_str += "%s" % min_hold_time - - cmd = "timer min-holdtime %s" % min_hold_time - cmds.append(cmd) - - conn_retry_time = module.params['conn_retry_time'] - if conn_retry_time: - conf_str += "%s" % conn_retry_time - - cmd = "timer connect-retry %s" % conn_retry_time - cmds.append(cmd) - - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - if ebgp_if_sensitive != 'no_use': - conf_str += "%s" % ebgp_if_sensitive - - if ebgp_if_sensitive == "true": - cmd = "ebgp-interface-sensitive" - else: - cmd = "undo ebgp-interface-sensitive" - cmds.append(cmd) - - conf_str += CE_MERGE_BGP_INSTANCE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp instance other failed.') - - return cmds - - def delete_bgp_instance_other_comm(self, **kwargs): - """ delete_bgp_instance_other_comm """ - - module = kwargs["module"] - conf_str = CE_DELETE_BGP_INSTANCE_HEADER - - vrf_name = module.params['vrf_name'] - conf_str += "%s" % vrf_name - - cmds = [] - - router_id = module.params['router_id'] - if router_id: - conf_str += "%s" % router_id - - cmd = "undo router-id" - cmds.append(cmd) - - vrf_rid_auto_sel = module.params['vrf_rid_auto_sel'] - if vrf_rid_auto_sel != 'no_use': - conf_str += "%s" % vrf_rid_auto_sel - - cmd = "undo router-id vpn-instance auto-select" - cmds.append(cmd) - - keepalive_time = module.params['keepalive_time'] - if keepalive_time: - conf_str += "%s" % keepalive_time - - cmd = "undo timer keepalive" - cmds.append(cmd) - - hold_time = module.params['hold_time'] - if hold_time: - conf_str += "%s" % hold_time - - cmd = "undo timer hold" - cmds.append(cmd) - - min_hold_time = module.params['min_hold_time'] - if min_hold_time: - conf_str += "%s" % min_hold_time - - cmd = "undo timer min-holdtime" - cmds.append(cmd) - - conn_retry_time = module.params['conn_retry_time'] - if conn_retry_time: - conf_str += "%s" % conn_retry_time - - cmd = "undo timer connect-retry" - cmds.append(cmd) - - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - if ebgp_if_sensitive != 'no_use': - conf_str += "%s" % ebgp_if_sensitive - - cmd = "undo ebgp-interface-sensitive" - cmds.append(cmd) - - default_af_type = module.params['default_af_type'] - if default_af_type: - conf_str += "%s" % default_af_type - - if vrf_name != "_public_": - if default_af_type == "ipv6uni": - cmd = "undo ipv6-family vpn-instance %s" % vrf_name - cmds.append(cmd) - else: - cmd = "undo ipv4-family vpn-instance %s" % vrf_name - cmds.append(cmd) - else: - if vrf_name != "_public_": - cmd = "undo ipv4-family vpn-instance %s" % vrf_name - cmds.append(cmd) - - conf_str += CE_DELETE_BGP_INSTANCE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Delete common vpn bgp instance other args failed.') - - return cmds - - def delete_instance_other_public(self, **kwargs): - """ delete_instance_other_public """ - - module = kwargs["module"] - conf_str = CE_MERGE_BGP_INSTANCE_HEADER - - vrf_name = module.params['vrf_name'] - conf_str += "%s" % vrf_name - - cmds = [] - - router_id = module.params['router_id'] - if router_id: - conf_str += "" - - cmd = "undo router-id" - cmds.append(cmd) - - vrf_rid_auto_sel = module.params['vrf_rid_auto_sel'] - if vrf_rid_auto_sel != 'no_use': - conf_str += "%s" % "false" - - cmd = "undo router-id vpn-instance auto-select" - cmds.append(cmd) - - keepalive_time = module.params['keepalive_time'] - if keepalive_time: - conf_str += "%s" % "60" - - cmd = "undo timer keepalive" - cmds.append(cmd) - - hold_time = module.params['hold_time'] - if hold_time: - conf_str += "%s" % "180" - - cmd = "undo timer hold" - cmds.append(cmd) - - min_hold_time = module.params['min_hold_time'] - if min_hold_time: - conf_str += "%s" % "0" - - cmd = "undo timer min-holdtime" - cmds.append(cmd) - - conn_retry_time = module.params['conn_retry_time'] - if conn_retry_time: - conf_str += "%s" % "32" - - cmd = "undo timer connect-retry" - cmds.append(cmd) - - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - if ebgp_if_sensitive != 'no_use': - conf_str += "%s" % "true" - - cmd = "ebgp-interface-sensitive" - cmds.append(cmd) - - default_af_type = module.params['default_af_type'] - if default_af_type: - conf_str += "%s" % "ipv4uni" - - if vrf_name != "_public_": - if default_af_type == "ipv6uni": - cmd = "undo ipv6-family vpn-instance %s" % vrf_name - cmds.append(cmd) - else: - cmd = "undo ipv4-family vpn-instance %s" % vrf_name - cmds.append(cmd) - else: - if vrf_name != "_public_": - cmd = "undo ipv4-family vpn-instance %s" % vrf_name - cmds.append(cmd) - - conf_str += CE_MERGE_BGP_INSTANCE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Delete default vpn bgp instance other args failed.') - - return cmds - - -def main(): - """ main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - as_number=dict(type='str'), - graceful_restart=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - time_wait_for_rib=dict(type='str'), - as_path_limit=dict(type='str'), - check_first_as=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - confed_id_number=dict(type='str'), - confed_nonstanded=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - bgp_rid_auto_sel=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - keep_all_routes=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - memory_limit=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - gr_peer_reset=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - is_shutdown=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - suppress_interval=dict(type='str'), - hold_interval=dict(type='str'), - clear_interval=dict(type='str'), - confed_peer_as_num=dict(type='str'), - vrf_name=dict(type='str'), - vrf_rid_auto_sel=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - router_id=dict(type='str'), - keepalive_time=dict(type='str'), - hold_time=dict(type='str'), - min_hold_time=dict(type='str'), - conn_retry_time=dict(type='str'), - ebgp_if_sensitive=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - default_af_type=dict(type='str', choices=['ipv4uni', 'ipv6uni']) - ) - - argument_spec.update(ce_argument_spec) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - - changed = False - proposed = dict() - existing = dict() - end_state = dict() - updates = [] - - state = module.params['state'] - as_number = module.params['as_number'] - graceful_restart = module.params['graceful_restart'] - time_wait_for_rib = module.params['time_wait_for_rib'] - as_path_limit = module.params['as_path_limit'] - check_first_as = module.params['check_first_as'] - confed_id_number = module.params['confed_id_number'] - confed_nonstanded = module.params['confed_nonstanded'] - bgp_rid_auto_sel = module.params['bgp_rid_auto_sel'] - keep_all_routes = module.params['keep_all_routes'] - memory_limit = module.params['memory_limit'] - gr_peer_reset = module.params['gr_peer_reset'] - is_shutdown = module.params['is_shutdown'] - suppress_interval = module.params['suppress_interval'] - hold_interval = module.params['hold_interval'] - clear_interval = module.params['clear_interval'] - confed_peer_as_num = module.params['confed_peer_as_num'] - router_id = module.params['router_id'] - vrf_name = module.params['vrf_name'] - vrf_rid_auto_sel = module.params['vrf_rid_auto_sel'] - keepalive_time = module.params['keepalive_time'] - hold_time = module.params['hold_time'] - min_hold_time = module.params['min_hold_time'] - conn_retry_time = module.params['conn_retry_time'] - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - default_af_type = module.params['default_af_type'] - - ce_bgp_obj = Bgp() - - if not ce_bgp_obj: - module.fail_json(msg='Error: Init module failed.') - - # get proposed - proposed["state"] = state - if as_number: - proposed["as_number"] = as_number - if graceful_restart != 'no_use': - proposed["graceful_restart"] = graceful_restart - if time_wait_for_rib: - proposed["time_wait_for_rib"] = time_wait_for_rib - if as_path_limit: - proposed["as_path_limit"] = as_path_limit - if check_first_as != 'no_use': - proposed["check_first_as"] = check_first_as - if confed_id_number: - proposed["confed_id_number"] = confed_id_number - if confed_nonstanded != 'no_use': - proposed["confed_nonstanded"] = confed_nonstanded - if bgp_rid_auto_sel != 'no_use': - proposed["bgp_rid_auto_sel"] = bgp_rid_auto_sel - if keep_all_routes != 'no_use': - proposed["keep_all_routes"] = keep_all_routes - if memory_limit != 'no_use': - proposed["memory_limit"] = memory_limit - if gr_peer_reset != 'no_use': - proposed["gr_peer_reset"] = gr_peer_reset - if is_shutdown != 'no_use': - proposed["is_shutdown"] = is_shutdown - if suppress_interval: - proposed["suppress_interval"] = suppress_interval - if hold_interval: - proposed["hold_interval"] = hold_interval - if clear_interval: - proposed["clear_interval"] = clear_interval - if confed_peer_as_num: - proposed["confed_peer_as_num"] = confed_peer_as_num - if router_id: - proposed["router_id"] = router_id - if vrf_name: - proposed["vrf_name"] = vrf_name - if vrf_rid_auto_sel != 'no_use': - proposed["vrf_rid_auto_sel"] = vrf_rid_auto_sel - if keepalive_time: - proposed["keepalive_time"] = keepalive_time - if hold_time: - proposed["hold_time"] = hold_time - if min_hold_time: - proposed["min_hold_time"] = min_hold_time - if conn_retry_time: - proposed["conn_retry_time"] = conn_retry_time - if ebgp_if_sensitive != 'no_use': - proposed["ebgp_if_sensitive"] = ebgp_if_sensitive - if default_af_type: - proposed["default_af_type"] = default_af_type - - need_bgp_enable = check_bgp_enable_args(module=module) - need_bgp_enable_other_rst = ce_bgp_obj.check_bgp_enable_other_args( - module=module) - need_bgp_confed = check_bgp_confed_args(module=module) - need_bgp_instance = ce_bgp_obj.check_bgp_instance_args(module=module) - need_bgp_instance_other_rst = ce_bgp_obj.check_bgp_instance_other_args( - module=module) - - router_id_exist = ce_bgp_obj.get_bgp_instance(module=module) - existing["bgp instance"] = router_id_exist - - # bgp enable/disable - if need_bgp_enable: - - bgp_enable_exist = ce_bgp_obj.get_bgp_enable(module=module) - existing["bgp enable"] = bgp_enable_exist - if bgp_enable_exist: - asnumber_exist = bgp_enable_exist[0][1] - bgpenable_exist = bgp_enable_exist[0][0] - else: - asnumber_exist = None - bgpenable_exist = None - - if state == "present": - bgp_enable_new = ("true", as_number) - - if bgp_enable_new in bgp_enable_exist: - pass - elif bgpenable_exist == "true" and asnumber_exist != as_number: - module.fail_json( - msg='Error: BGP is already running. The AS is %s.' % asnumber_exist) - else: - cmd = ce_bgp_obj.merge_bgp_enable(module=module) - changed = True - for item in cmd: - updates.append(item) - - else: - if need_bgp_enable_other_rst["need_cfg"] or need_bgp_confed or \ - need_bgp_instance_other_rst["need_cfg"] or need_bgp_instance: - pass - elif bgpenable_exist == "false": - pass - elif bgpenable_exist == "true" and asnumber_exist == as_number: - cmd = ce_bgp_obj.merge_bgp_enable(module=module) - changed = True - for item in cmd: - updates.append(item) - - else: - module.fail_json( - msg='Error: BGP is already running. The AS is %s.' % asnumber_exist) - - bgp_enable_end = ce_bgp_obj.get_bgp_enable(module=module) - end_state["bgp enable"] = bgp_enable_end - - # bgp enable/disable other args - exist_tmp = dict() - for item in need_bgp_enable_other_rst: - if item != "need_cfg": - exist_tmp[item] = need_bgp_enable_other_rst[item] - - if exist_tmp: - existing["bgp enable other"] = exist_tmp - - if need_bgp_enable_other_rst["need_cfg"]: - if state == "present": - cmd = ce_bgp_obj.merge_bgp_enable_other(module=module) - changed = True - for item in cmd: - updates.append(item) - else: - cmd = ce_bgp_obj.delete_bgp_enable_other(module=module) - changed = True - for item in cmd: - updates.append(item) - - need_bgp_enable_other_rst = ce_bgp_obj.check_bgp_enable_other_args( - module=module) - - end_tmp = dict() - for item in need_bgp_enable_other_rst: - if item != "need_cfg": - end_tmp[item] = need_bgp_enable_other_rst[item] - - if end_tmp: - end_state["bgp enable other"] = end_tmp - - # bgp confederation peer as - if need_bgp_confed: - confed_exist = ce_bgp_obj.get_bgp_confed_peer_as(module=module) - existing["confederation peer as"] = confed_exist - confed_new = (confed_peer_as_num) - - if state == "present": - if len(confed_exist) == 0: - cmd = ce_bgp_obj.create_bgp_confed_peer_as(module=module) - changed = True - for item in cmd: - updates.append(item) - - elif confed_new not in confed_exist: - cmd = ce_bgp_obj.merge_bgp_confed_peer_as(module=module) - changed = True - for item in cmd: - updates.append(item) - - else: - if len(confed_exist) == 0: - pass - - elif confed_new not in confed_exist: - pass - - else: - cmd = ce_bgp_obj.delete_bgp_confed_peer_as(module=module) - changed = True - for item in cmd: - updates.append(item) - - confed_end = ce_bgp_obj.get_bgp_confed_peer_as(module=module) - end_state["confederation peer as"] = confed_end - - # bgp instance - if need_bgp_instance and default_af_type != "ipv6uni": - router_id_new = vrf_name - - if state == "present": - if len(router_id_exist) == 0: - cmd = ce_bgp_obj.create_bgp_instance(module=module) - changed = True - updates.extend(cmd) - elif router_id_new not in router_id_exist: - cmd = ce_bgp_obj.merge_bgp_instance(module=module) - changed = True - updates.extend(cmd) - else: - if not need_bgp_instance_other_rst["need_cfg"]: - if vrf_name != "_public_": - if len(router_id_exist) == 0: - pass - elif router_id_new not in router_id_exist: - pass - else: - cmd = ce_bgp_obj.delete_bgp_instance(module=module) - changed = True - for item in cmd: - updates.append(item) - - # bgp instance other - exist_tmp = dict() - for item in need_bgp_instance_other_rst: - if item != "need_cfg": - exist_tmp[item] = need_bgp_instance_other_rst[item] - - if exist_tmp: - existing["bgp instance other"] = exist_tmp - - if need_bgp_instance_other_rst["need_cfg"]: - if state == "present": - cmd = ce_bgp_obj.merge_bgp_instance_other(module=module) - changed = True - for item in cmd: - updates.append(item) - - else: - if vrf_name == "_public_": - cmd = ce_bgp_obj.delete_instance_other_public( - module=module) - changed = True - for item in cmd: - updates.append(item) - else: - cmd = ce_bgp_obj.delete_bgp_instance_other_comm(module=module) - changed = True - for item in cmd: - updates.append(item) - - need_bgp_instance_other_rst = ce_bgp_obj.check_bgp_instance_other_args( - module=module) - - router_id_end = ce_bgp_obj.get_bgp_instance(module=module) - end_state["bgp instance"] = router_id_end - - end_tmp = dict() - for item in need_bgp_instance_other_rst: - if item != "need_cfg": - end_tmp[item] = need_bgp_instance_other_rst[item] - - if end_tmp: - end_state["bgp instance other"] = end_tmp - if end_state == existing: - changed = False - updates = list() - results = dict() - results['proposed'] = proposed - results['existing'] = existing - results['changed'] = changed - results['end_state'] = end_state - results['updates'] = updates - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_bgp_af.py b/plugins/modules/network/cloudengine/ce_bgp_af.py deleted file mode 100644 index 089b3b3231..0000000000 --- a/plugins/modules/network/cloudengine/ce_bgp_af.py +++ /dev/null @@ -1,3434 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_bgp_af -short_description: Manages BGP Address-family configuration on HUAWEI CloudEngine switches. -description: - - Manages BGP Address-family configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] - vrf_name: - description: - - Name of a BGP instance. The name is a case-sensitive string of characters. - The BGP instance can be used only after the corresponding VPN instance is created. - The value is a string of 1 to 31 case-sensitive characters. - required: true - af_type: - description: - - Address family type of a BGP instance. - required: true - choices: ['ipv4uni','ipv4multi', 'ipv4vpn', 'ipv6uni', 'ipv6vpn', 'evpn'] - max_load_ibgp_num: - description: - - Specify the maximum number of equal-cost IBGP routes. - The value is an integer ranging from 1 to 65535. - ibgp_ecmp_nexthop_changed: - description: - - If the value is true, the next hop of an advertised route is changed to the advertiser itself in IBGP - load-balancing scenarios. - If the value is false, the next hop of an advertised route is not changed to the advertiser itself in - IBGP load-balancing scenarios. - choices: ['no_use','true','false'] - default: no_use - max_load_ebgp_num: - description: - - Specify the maximum number of equal-cost EBGP routes. - The value is an integer ranging from 1 to 65535. - ebgp_ecmp_nexthop_changed: - description: - - If the value is true, the next hop of an advertised route is changed to the advertiser itself in EBGP - load-balancing scenarios. - If the value is false, the next hop of an advertised route is not changed to the advertiser itself in - EBGP load-balancing scenarios. - choices: ['no_use','true','false'] - default: no_use - maximum_load_balance: - description: - - Specify the maximum number of equal-cost routes in the BGP routing table. - The value is an integer ranging from 1 to 65535. - ecmp_nexthop_changed: - description: - - If the value is true, the next hop of an advertised route is changed to the advertiser itself in BGP - load-balancing scenarios. - If the value is false, the next hop of an advertised route is not changed to the advertiser itself - in BGP load-balancing scenarios. - choices: ['no_use','true','false'] - default: no_use - default_local_pref: - description: - - Set the Local-Preference attribute. The value is an integer. - The value is an integer ranging from 0 to 4294967295. - default_med: - description: - - Specify the Multi-Exit-Discriminator (MED) of BGP routes. - The value is an integer ranging from 0 to 4294967295. - default_rt_import_enable: - description: - - If the value is true, importing default routes to the BGP routing table is allowed. - If the value is false, importing default routes to the BGP routing table is not allowed. - choices: ['no_use','true','false'] - default: no_use - router_id: - description: - - ID of a router that is in IPv4 address format. - The value is a string of 0 to 255 characters. - The value is in dotted decimal notation. - vrf_rid_auto_sel: - description: - - If the value is true, VPN BGP instances are enabled to automatically select router IDs. - If the value is false, VPN BGP instances are disabled from automatically selecting router IDs. - choices: ['no_use','true','false'] - default: no_use - nexthop_third_party: - description: - - If the value is true, the third-party next hop function is enabled. - If the value is false, the third-party next hop function is disabled. - choices: ['no_use','true','false'] - default: no_use - summary_automatic: - description: - - If the value is true, automatic aggregation is enabled for locally imported routes. - If the value is false, automatic aggregation is disabled for locally imported routes. - choices: ['no_use','true','false'] - default: no_use - auto_frr_enable: - description: - - If the value is true, BGP auto FRR is enabled. - If the value is false, BGP auto FRR is disabled. - choices: ['no_use','true','false'] - default: no_use - load_balancing_as_path_ignore: - description: - - Load balancing as path ignore. - choices: ['no_use','true','false'] - default: no_use - rib_only_enable: - description: - - If the value is true, BGP routes cannot be advertised to the IP routing table. - If the value is false, Routes preferred by BGP are advertised to the IP routing table. - choices: ['no_use','true','false'] - default: no_use - rib_only_policy_name: - description: - - Specify the name of a routing policy. - The value is a string of 1 to 40 characters. - active_route_advertise: - description: - - If the value is true, BGP is enabled to advertise only optimal routes in the RM to peers. - If the value is false, BGP is not enabled to advertise only optimal routes in the RM to peers. - choices: ['no_use','true','false'] - default: no_use - as_path_neglect: - description: - - If the value is true, the AS path attribute is ignored when BGP selects an optimal route. - If the value is false, the AS path attribute is not ignored when BGP selects an optimal route. - An AS path with a smaller length has a higher priority. - choices: ['no_use','true','false'] - default: no_use - med_none_as_maximum: - description: - - If the value is true, when BGP selects an optimal route, the system uses 4294967295 as the - MED value of a route if the route's attribute does not carry a MED value. - If the value is false, the system uses 0 as the MED value of a route if the route's attribute - does not carry a MED value. - choices: ['no_use','true','false'] - default: no_use - router_id_neglect: - description: - - If the value is true, the router ID attribute is ignored when BGP selects the optimal route. - If the value is false, the router ID attribute is not ignored when BGP selects the optimal route. - choices: ['no_use','true','false'] - default: no_use - igp_metric_ignore: - description: - - If the value is true, the metrics of next-hop IGP routes are not compared when BGP selects - an optimal route. - If the value is false, the metrics of next-hop IGP routes are not compared when BGP selects - an optimal route. - A route with a smaller metric has a higher priority. - choices: ['no_use','true','false'] - default: no_use - always_compare_med: - description: - - If the value is true, the MEDs of routes learned from peers in different autonomous systems - are compared when BGP selects an optimal route. - If the value is false, the MEDs of routes learned from peers in different autonomous systems - are not compared when BGP selects an optimal route. - choices: ['no_use','true','false'] - default: no_use - determin_med: - description: - - If the value is true, BGP deterministic-MED is enabled. - If the value is false, BGP deterministic-MED is disabled. - choices: ['no_use','true','false'] - default: no_use - preference_external: - description: - - Set the protocol priority of EBGP routes. - The value is an integer ranging from 1 to 255. - preference_internal: - description: - - Set the protocol priority of IBGP routes. - The value is an integer ranging from 1 to 255. - preference_local: - description: - - Set the protocol priority of a local BGP route. - The value is an integer ranging from 1 to 255. - prefrence_policy_name: - description: - - Set a routing policy to filter routes so that a configured priority is applied to - the routes that match the specified policy. - The value is a string of 1 to 40 characters. - reflect_between_client: - description: - - If the value is true, route reflection is enabled between clients. - If the value is false, route reflection is disabled between clients. - choices: ['no_use','true','false'] - default: no_use - reflector_cluster_id: - description: - - Set a cluster ID. Configuring multiple RRs in a cluster can enhance the stability of the network. - The value is an integer ranging from 1 to 4294967295. - reflector_cluster_ipv4: - description: - - Set a cluster ipv4 address. The value is expressed in the format of an IPv4 address. - rr_filter_number: - description: - - Set the number of the extended community filter supported by an RR group. - The value is a string of 1 to 51 characters. - policy_vpn_target: - description: - - If the value is true, VPN-Target filtering function is performed for received VPN routes. - If the value is false, VPN-Target filtering function is not performed for received VPN routes. - choices: ['no_use','true','false'] - default: no_use - next_hop_sel_depend_type: - description: - - Next hop select depend type. - choices: ['default','dependTunnel', 'dependIp'] - default: default - nhp_relay_route_policy_name: - description: - - Specify the name of a route-policy for route iteration. - The value is a string of 1 to 40 characters. - ebgp_if_sensitive: - description: - - If the value is true, after the fast EBGP interface awareness function is enabled, - EBGP sessions on an interface are deleted immediately when the interface goes Down. - If the value is false, after the fast EBGP interface awareness function is enabled, - EBGP sessions on an interface are not deleted immediately when the interface goes Down. - choices: ['no_use','true','false'] - default: no_use - reflect_chg_path: - description: - - If the value is true, the route reflector is enabled to modify route path attributes - based on an export policy. - If the value is false, the route reflector is disabled from modifying route path attributes - based on an export policy. - choices: ['no_use','true','false'] - default: no_use - add_path_sel_num: - description: - - Number of Add-Path routes. - The value is an integer ranging from 2 to 64. - route_sel_delay: - description: - - Route selection delay. - The value is an integer ranging from 0 to 3600. - allow_invalid_as: - description: - - Allow routes with BGP origin AS validation result Invalid to be selected. - If the value is true, invalid routes can participate in route selection. - If the value is false, invalid routes cannot participate in route selection. - choices: ['no_use','true','false'] - default: no_use - policy_ext_comm_enable: - description: - - If the value is true, modifying extended community attributes is allowed. - If the value is false, modifying extended community attributes is not allowed. - choices: ['no_use','true','false'] - default: no_use - supernet_uni_adv: - description: - - If the value is true, the function to advertise supernetwork unicast routes is enabled. - If the value is false, the function to advertise supernetwork unicast routes is disabled. - choices: ['no_use','true','false'] - default: no_use - supernet_label_adv: - description: - - If the value is true, the function to advertise supernetwork label is enabled. - If the value is false, the function to advertise supernetwork label is disabled. - choices: ['no_use','true','false'] - default: no_use - ingress_lsp_policy_name: - description: - - Ingress lsp policy name. - originator_prior: - description: - - Originator prior. - choices: ['no_use','true','false'] - default: no_use - lowest_priority: - description: - - If the value is true, enable reduce priority to advertise route. - If the value is false, disable reduce priority to advertise route. - choices: ['no_use','true','false'] - default: no_use - relay_delay_enable: - description: - - If the value is true, relay delay enable. - If the value is false, relay delay disable. - choices: ['no_use','true','false'] - default: no_use - import_protocol: - description: - - Routing protocol from which routes can be imported. - choices: ['direct', 'ospf', 'isis', 'static', 'rip', 'ospfv3', 'ripng'] - import_process_id: - description: - - Process ID of an imported routing protocol. - The value is an integer ranging from 0 to 4294967295. - network_address: - description: - - Specify the IP address advertised by BGP. - The value is a string of 0 to 255 characters. - mask_len: - description: - - Specify the mask length of an IP address. - The value is an integer ranging from 0 to 128. -''' - -EXAMPLES = ''' -- name: CloudEngine BGP address family test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - tasks: - - name: "Config BGP Address_Family" - ce_bgp_af: - state: present - vrf_name: js - af_type: ipv4uni - provider: "{{ cli }}" - - name: "Undo BGP Address_Family" - ce_bgp_af: - state: absent - vrf_name: js - af_type: ipv4uni - provider: "{{ cli }}" - - name: "Config import route" - ce_bgp_af: - state: present - vrf_name: js - af_type: ipv4uni - import_protocol: ospf - import_process_id: 123 - provider: "{{ cli }}" - - name: "Undo import route" - ce_bgp_af: - state: absent - vrf_name: js - af_type: ipv4uni - import_protocol: ospf - import_process_id: 123 - provider: "{{ cli }}" - - name: "Config network route" - ce_bgp_af: - state: present - vrf_name: js - af_type: ipv4uni - network_address: 1.1.1.1 - mask_len: 24 - provider: "{{ cli }}" - - name: "Undo network route" - ce_bgp_af: - state: absent - vrf_name: js - af_type: ipv4uni - network_address: 1.1.1.1 - mask_len: 24 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"af_type": "ipv4uni", - "state": "present", "vrf_name": "js"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"af_type": "ipv4uni", "vrf_name": "js"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["ipv4-family vpn-instance js"] -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - -# get bgp address family -CE_GET_BGP_ADDRESS_FAMILY_HEADER = """ - - - - - - %s - - - %s -""" -CE_GET_BGP_ADDRESS_FAMILY_TAIL = """ - - - - - - - -""" - -# merge bgp address family -CE_MERGE_BGP_ADDRESS_FAMILY_HEADER = """ - - - - - - %s - - - %s -""" -CE_MERGE_BGP_ADDRESS_FAMILY_TAIL = """ - - - - - - - -""" - -# create bgp address family -CE_CREATE_BGP_ADDRESS_FAMILY_HEADER = """ - - - - - - %s - - - %s -""" -CE_CREATE_BGP_ADDRESS_FAMILY_TAIL = """ - - - - - - - -""" - -# delete bgp address family -CE_DELETE_BGP_ADDRESS_FAMILY_HEADER = """ - - - - - - %s - - - %s -""" -CE_DELETE_BGP_ADDRESS_FAMILY_TAIL = """ - - - - - - - -""" - -# get bgp import route -CE_GET_BGP_IMPORT_AND_NETWORK_ROUTE = """ - - - - - - %s - - - %s - - - - - - - - - - - - - - - - - - - -""" - -# merge bgp import route -CE_MERGE_BGP_IMPORT_ROUTE_HEADER = """ - - - - - - %s - - - %s - - - %s - %s -""" -CE_MERGE_BGP_IMPORT_ROUTE_TAIL = """ - - - - - - - - - -""" - -# create bgp import route -CE_CREATE_BGP_IMPORT_ROUTE = """ - - - - - - %s - - - %s - - - %s - %s - - - - - - - - - -""" - -# delete bgp import route -CE_DELETE_BGP_IMPORT_ROUTE = """ - - - - - - %s - - - %s - - - %s - %s - - - - - - - - - -""" - -# get bgp network route -CE_GET_BGP_NETWORK_ROUTE_HEADER = """ - - - - - - %s - - - %s - - - - -""" -CE_GET_BGP_NETWORK_ROUTE_TAIL = """ - - - - - - - - - -""" - -# merge bgp network route -CE_MERGE_BGP_NETWORK_ROUTE_HEADER = """ - - - - - - %s - - - %s - - - %s - %s -""" -CE_MERGE_BGP_NETWORK_ROUTE_TAIL = """ - - - - - - - - - -""" - -# create bgp network route -CE_CREATE_BGP_NETWORK_ROUTE = """ - - - - - - %s - - - %s - - - %s - %s - - - - - - - - - -""" - -# delete bgp network route -CE_DELETE_BGP_NETWORK_ROUTE = """ - - - - - - %s - - - %s - - - %s - %s - - - - - - - - - -""" - -# bgp import and network route header -CE_BGP_IMPORT_NETWORK_ROUTE_HEADER = """ - - - - - - %s - - - %s -""" -CE_BGP_IMPORT_NETWORK_ROUTE_TAIL = """ - - - - - - - -""" -CE_BGP_MERGE_IMPORT_UNIT = """ - - - %s - %s - - -""" -CE_BGP_CREATE_IMPORT_UNIT = """ - - - %s - %s - - -""" -CE_BGP_DELETE_IMPORT_UNIT = """ - - - %s - %s - - -""" -CE_BGP_MERGE_NETWORK_UNIT = """ - - - %s - %s - - -""" -CE_BGP_CREATE_NETWORK_UNIT = """ - - - %s - %s - - -""" -CE_BGP_DELETE_NETWORK_UNIT = """ - - - %s - %s - - -""" - - -class BgpAf(object): - """ Manages BGP Address-family configuration """ - - def netconf_get_config(self, **kwargs): - """ netconf_get_config """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = get_nc_config(module, conf_str) - - return xml_str - - def netconf_set_config(self, **kwargs): - """ netconf_set_config """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = set_nc_config(module, conf_str) - - return xml_str - - def check_bgp_af_args(self, **kwargs): - """ check_bgp_af_args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='Error: The len of vrf_name %s is out of [1 - 31].' % vrf_name) - else: - module.fail_json(msg='Error: Please input vrf_name.') - - state = module.params['state'] - af_type = module.params['af_type'] - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["af_type"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != af_type: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["af_type"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] == af_type: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def check_bgp_af_other_can_del(self, **kwargs): - """ check_bgp_af_other_can_del """ - module = kwargs["module"] - result = dict() - need_cfg = False - - state = module.params['state'] - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - - router_id = module.params['router_id'] - if router_id: - if len(router_id) > 255: - module.fail_json( - msg='Error: The len of router_id %s is out of [0 - 255].' % router_id) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if re_find[0] != router_id: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if re_find[0] == router_id: - need_cfg = True - else: - pass - - determin_med = module.params['determin_med'] - if determin_med != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if re_find[0] != determin_med: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if re_find[0] == determin_med: - need_cfg = True - else: - pass - - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - if ebgp_if_sensitive != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if re_find[0] != ebgp_if_sensitive: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if re_find[0] == ebgp_if_sensitive: - need_cfg = True - else: - pass - - relay_delay_enable = module.params['relay_delay_enable'] - if relay_delay_enable != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if re_find[0] != relay_delay_enable: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - if re_find[0] == relay_delay_enable: - need_cfg = True - else: - pass - - result["need_cfg"] = need_cfg - return result - - def check_bgp_af_other_args(self, **kwargs): - """ check_bgp_af_other_args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - - max_load_ibgp_num = module.params['max_load_ibgp_num'] - if max_load_ibgp_num: - if int(max_load_ibgp_num) > 65535 or int(max_load_ibgp_num) < 1: - module.fail_json( - msg='Error: The value of max_load_ibgp_num %s is out of [1 - 65535].' % max_load_ibgp_num) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["max_load_ibgp_num"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != max_load_ibgp_num: - need_cfg = True - else: - need_cfg = True - - ibgp_ecmp_nexthop_changed = module.params['ibgp_ecmp_nexthop_changed'] - if ibgp_ecmp_nexthop_changed != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ibgp_ecmp_nexthop_changed"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != ibgp_ecmp_nexthop_changed: - need_cfg = True - else: - need_cfg = True - - max_load_ebgp_num = module.params['max_load_ebgp_num'] - if max_load_ebgp_num: - if int(max_load_ebgp_num) > 65535 or int(max_load_ebgp_num) < 1: - module.fail_json( - msg='Error: The value of max_load_ebgp_num %s is out of [1 - 65535].' % max_load_ebgp_num) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["max_load_ebgp_num"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != max_load_ebgp_num: - need_cfg = True - else: - need_cfg = True - - ebgp_ecmp_nexthop_changed = module.params['ebgp_ecmp_nexthop_changed'] - if ebgp_ecmp_nexthop_changed != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ebgp_ecmp_nexthop_changed"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != ebgp_ecmp_nexthop_changed: - need_cfg = True - else: - need_cfg = True - - maximum_load_balance = module.params['maximum_load_balance'] - if maximum_load_balance: - if int(maximum_load_balance) > 65535 or int(maximum_load_balance) < 1: - module.fail_json( - msg='Error: The value of maximum_load_balance %s is out of [1 - 65535].' % maximum_load_balance) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["maximum_load_balance"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != maximum_load_balance: - need_cfg = True - else: - need_cfg = True - - ecmp_nexthop_changed = module.params['ecmp_nexthop_changed'] - if ecmp_nexthop_changed != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ecmp_nexthop_changed"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != ecmp_nexthop_changed: - need_cfg = True - else: - need_cfg = True - - default_local_pref = module.params['default_local_pref'] - if default_local_pref: - if int(default_local_pref) < 0: - module.fail_json( - msg='Error: The value of default_local_pref %s is out of [0 - 4294967295].' % default_local_pref) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["default_local_pref"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != default_local_pref: - need_cfg = True - else: - need_cfg = True - - default_med = module.params['default_med'] - if default_med: - if int(default_med) < 0: - module.fail_json( - msg='Error: The value of default_med %s is out of [0 - 4294967295].' % default_med) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["default_med"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != default_med: - need_cfg = True - else: - need_cfg = True - - default_rt_import_enable = module.params['default_rt_import_enable'] - if default_rt_import_enable != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["default_rt_import_enable"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != default_rt_import_enable: - need_cfg = True - else: - need_cfg = True - - router_id = module.params['router_id'] - if router_id: - if len(router_id) > 255: - module.fail_json( - msg='Error: The len of router_id %s is out of [0 - 255].' % router_id) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["router_id"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != router_id: - need_cfg = True - else: - need_cfg = True - - vrf_rid_auto_sel = module.params['vrf_rid_auto_sel'] - if vrf_rid_auto_sel != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["vrf_rid_auto_sel"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != vrf_rid_auto_sel: - need_cfg = True - else: - need_cfg = True - - nexthop_third_party = module.params['nexthop_third_party'] - if nexthop_third_party != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["nexthop_third_party"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != nexthop_third_party: - need_cfg = True - else: - need_cfg = True - - summary_automatic = module.params['summary_automatic'] - if summary_automatic != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["summary_automatic"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != summary_automatic: - need_cfg = True - else: - need_cfg = True - - auto_frr_enable = module.params['auto_frr_enable'] - if auto_frr_enable != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["auto_frr_enable"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != auto_frr_enable: - need_cfg = True - else: - need_cfg = True - - load_balancing_as_path_ignore = module.params['load_balancing_as_path_ignore'] - if load_balancing_as_path_ignore != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + \ - CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["load_balancing_as_path_ignore"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != load_balancing_as_path_ignore: - need_cfg = True - else: - need_cfg = True - - rib_only_enable = module.params['rib_only_enable'] - if rib_only_enable != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["rib_only_enable"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != rib_only_enable: - need_cfg = True - else: - need_cfg = True - - rib_only_policy_name = module.params['rib_only_policy_name'] - if rib_only_policy_name: - if len(rib_only_policy_name) > 40 or len(rib_only_policy_name) < 1: - module.fail_json( - msg='Error: The len of rib_only_policy_name %s is out of [1 - 40].' % rib_only_policy_name) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["rib_only_policy_name"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != rib_only_policy_name: - need_cfg = True - else: - need_cfg = True - - active_route_advertise = module.params['active_route_advertise'] - if active_route_advertise != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["active_route_advertise"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != active_route_advertise: - need_cfg = True - else: - need_cfg = True - - as_path_neglect = module.params['as_path_neglect'] - if as_path_neglect != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["as_path_neglect"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != as_path_neglect: - need_cfg = True - else: - need_cfg = True - - med_none_as_maximum = module.params['med_none_as_maximum'] - if med_none_as_maximum != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["med_none_as_maximum"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != med_none_as_maximum: - need_cfg = True - else: - need_cfg = True - - router_id_neglect = module.params['router_id_neglect'] - if router_id_neglect != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["router_id_neglect"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != router_id_neglect: - need_cfg = True - else: - need_cfg = True - - igp_metric_ignore = module.params['igp_metric_ignore'] - if igp_metric_ignore != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["igp_metric_ignore"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != igp_metric_ignore: - need_cfg = True - else: - need_cfg = True - - always_compare_med = module.params['always_compare_med'] - if always_compare_med != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["always_compare_med"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != always_compare_med: - need_cfg = True - else: - need_cfg = True - - determin_med = module.params['determin_med'] - if determin_med != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["determin_med"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != determin_med: - need_cfg = True - else: - need_cfg = True - - preference_external = module.params['preference_external'] - if preference_external: - if int(preference_external) > 255 or int(preference_external) < 1: - module.fail_json( - msg='Error: The value of preference_external %s is out of [1 - 255].' % preference_external) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["preference_external"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != preference_external: - need_cfg = True - else: - need_cfg = True - - preference_internal = module.params['preference_internal'] - if preference_internal: - if int(preference_internal) > 255 or int(preference_internal) < 1: - module.fail_json( - msg='Error: The value of preference_internal %s is out of [1 - 255].' % preference_internal) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["preference_internal"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != preference_internal: - need_cfg = True - else: - need_cfg = True - - preference_local = module.params['preference_local'] - if preference_local: - if int(preference_local) > 255 or int(preference_local) < 1: - module.fail_json( - msg='Error: The value of preference_local %s is out of [1 - 255].' % preference_local) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["preference_local"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != preference_local: - need_cfg = True - else: - need_cfg = True - - prefrence_policy_name = module.params['prefrence_policy_name'] - if prefrence_policy_name: - if len(prefrence_policy_name) > 40 or len(prefrence_policy_name) < 1: - module.fail_json( - msg='Error: The len of prefrence_policy_name %s is out of [1 - 40].' % prefrence_policy_name) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["prefrence_policy_name"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != prefrence_policy_name: - need_cfg = True - else: - need_cfg = True - - reflect_between_client = module.params['reflect_between_client'] - if reflect_between_client != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["reflect_between_client"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != reflect_between_client: - need_cfg = True - else: - need_cfg = True - - reflector_cluster_id = module.params['reflector_cluster_id'] - if reflector_cluster_id: - if int(reflector_cluster_id) < 0: - module.fail_json( - msg='Error: The value of reflector_cluster_id %s is out of ' - '[1 - 4294967295].' % reflector_cluster_id) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["reflector_cluster_id"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != reflector_cluster_id: - need_cfg = True - else: - need_cfg = True - - reflector_cluster_ipv4 = module.params['reflector_cluster_ipv4'] - if reflector_cluster_ipv4: - if len(reflector_cluster_ipv4) > 255: - module.fail_json( - msg='Error: The len of reflector_cluster_ipv4 %s is out of [0 - 255].' % reflector_cluster_ipv4) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["reflector_cluster_ipv4"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != reflector_cluster_ipv4: - need_cfg = True - else: - need_cfg = True - - rr_filter_number = module.params['rr_filter_number'] - if rr_filter_number: - if len(rr_filter_number) > 51 or len(rr_filter_number) < 1: - module.fail_json( - msg='Error: The len of rr_filter_number %s is out of [1 - 51].' % rr_filter_number) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["rr_filter_number"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != rr_filter_number: - need_cfg = True - else: - need_cfg = True - - policy_vpn_target = module.params['policy_vpn_target'] - if policy_vpn_target != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["policy_vpn_target"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != policy_vpn_target: - need_cfg = True - else: - need_cfg = True - - next_hop_sel_depend_type = module.params['next_hop_sel_depend_type'] - if next_hop_sel_depend_type: - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["next_hop_sel_depend_type"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != next_hop_sel_depend_type: - need_cfg = True - else: - need_cfg = True - - nhp_relay_route_policy_name = module.params[ - 'nhp_relay_route_policy_name'] - if nhp_relay_route_policy_name: - if len(nhp_relay_route_policy_name) > 40 or len(nhp_relay_route_policy_name) < 1: - module.fail_json( - msg='Error: The len of nhp_relay_route_policy_name %s is ' - 'out of [1 - 40].' % nhp_relay_route_policy_name) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + \ - CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["nhp_relay_route_policy_name"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != nhp_relay_route_policy_name: - need_cfg = True - else: - need_cfg = True - - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - if ebgp_if_sensitive != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ebgp_if_sensitive"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != ebgp_if_sensitive: - need_cfg = True - else: - need_cfg = True - - reflect_chg_path = module.params['reflect_chg_path'] - if reflect_chg_path != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["reflect_chg_path"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != reflect_chg_path: - need_cfg = True - else: - need_cfg = True - - add_path_sel_num = module.params['add_path_sel_num'] - if add_path_sel_num: - if int(add_path_sel_num) > 64 or int(add_path_sel_num) < 2: - module.fail_json( - msg='Error: The value of add_path_sel_num %s is out of [2 - 64].' % add_path_sel_num) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["add_path_sel_num"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != add_path_sel_num: - need_cfg = True - else: - need_cfg = True - - route_sel_delay = module.params['route_sel_delay'] - if route_sel_delay: - if int(route_sel_delay) > 3600 or int(route_sel_delay) < 0: - module.fail_json( - msg='Error: The value of route_sel_delay %s is out of [0 - 3600].' % route_sel_delay) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["route_sel_delay"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != route_sel_delay: - need_cfg = True - else: - need_cfg = True - - allow_invalid_as = module.params['allow_invalid_as'] - if allow_invalid_as != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["allow_invalid_as"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != allow_invalid_as: - need_cfg = True - else: - need_cfg = True - - policy_ext_comm_enable = module.params['policy_ext_comm_enable'] - if policy_ext_comm_enable != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["policy_ext_comm_enable"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != policy_ext_comm_enable: - need_cfg = True - else: - need_cfg = True - - supernet_uni_adv = module.params['supernet_uni_adv'] - if supernet_uni_adv != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["supernet_uni_adv"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != supernet_uni_adv: - need_cfg = True - else: - need_cfg = True - - supernet_label_adv = module.params['supernet_label_adv'] - if supernet_label_adv != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["supernet_label_adv"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != supernet_label_adv: - need_cfg = True - else: - need_cfg = True - - ingress_lsp_policy_name = module.params['ingress_lsp_policy_name'] - if ingress_lsp_policy_name: - if len(ingress_lsp_policy_name) > 40 or len(ingress_lsp_policy_name) < 1: - module.fail_json( - msg='Error: The len of ingress_lsp_policy_name %s is out of [1 - 40].' % ingress_lsp_policy_name) - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ingress_lsp_policy_name"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != ingress_lsp_policy_name: - need_cfg = True - else: - need_cfg = True - - originator_prior = module.params['originator_prior'] - if originator_prior != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["originator_prior"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != originator_prior: - need_cfg = True - else: - need_cfg = True - - lowest_priority = module.params['lowest_priority'] - if lowest_priority != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["lowest_priority"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != lowest_priority: - need_cfg = True - else: - need_cfg = True - - relay_delay_enable = module.params['relay_delay_enable'] - if relay_delay_enable != 'no_use': - - conf_str = CE_GET_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) + \ - "" + CE_GET_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["relay_delay_enable"] = re_find - result["vrf_name"] = vrf_name - if re_find[0] != relay_delay_enable: - need_cfg = True - else: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def check_bgp_import_network_route(self, **kwargs): - """ check_bgp_import_network_route """ - - module = kwargs["module"] - result = dict() - import_need_cfg = False - network_need_cfg = False - - vrf_name = module.params['vrf_name'] - - state = module.params['state'] - af_type = module.params['af_type'] - import_protocol = module.params['import_protocol'] - import_process_id = module.params['import_process_id'] - - if import_protocol and (import_protocol != "direct" and import_protocol != "static"): - if not import_process_id: - module.fail_json( - msg='Error: Please input import_protocol and import_process_id value at the same time.') - else: - if int(import_process_id) < 0: - module.fail_json( - msg='Error: The value of import_process_id %s is out of [0 - 4294967295].' % import_process_id) - - if import_process_id: - if not import_protocol: - module.fail_json( - msg='Error: Please input import_protocol and import_process_id value at the same time.') - - network_address = module.params['network_address'] - mask_len = module.params['mask_len'] - - if network_address: - if not mask_len: - module.fail_json( - msg='Error: Please input network_address and mask_len value at the same time.') - if mask_len: - if not network_address: - module.fail_json( - msg='Error: Please input network_address and mask_len value at the same time.') - - conf_str = CE_GET_BGP_IMPORT_AND_NETWORK_ROUTE % (vrf_name, af_type) - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if import_protocol: - - if import_protocol == "direct" or import_protocol == "static": - import_process_id = "0" - else: - if not import_process_id or import_process_id == "0": - module.fail_json( - msg='Error: Please input import_process_id not 0 when import_protocol is ' - '[ospf, isis, rip, ospfv3, ripng].') - - bgp_import_route_new = (import_protocol, import_process_id) - - if state == "present": - if "" in recv_xml: - import_need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*\s.*(.*).*', - recv_xml) - - if re_find: - result["bgp_import_route"] = re_find - result["vrf_name"] = vrf_name - if bgp_import_route_new not in re_find: - import_need_cfg = True - else: - import_need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*\s.*(.*).*', - recv_xml) - - if re_find: - result["bgp_import_route"] = re_find - result["vrf_name"] = vrf_name - if bgp_import_route_new in re_find: - import_need_cfg = True - - if network_address and mask_len: - - bgp_network_route_new = (network_address, mask_len) - - if not check_ip_addr(ipaddr=network_address): - module.fail_json( - msg='Error: The network_address %s is invalid.' % network_address) - - if len(mask_len) > 128: - module.fail_json( - msg='Error: The len of mask_len %s is out of [0 - 128].' % mask_len) - - if state == "present": - if "" in recv_xml: - network_need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*\s.*(.*).*', recv_xml) - - if re_find: - result["bgp_network_route"] = re_find - result["vrf_name"] = vrf_name - if bgp_network_route_new not in re_find: - network_need_cfg = True - else: - network_need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*\s.*(.*).*', recv_xml) - - if re_find: - result["bgp_network_route"] = re_find - result["vrf_name"] = vrf_name - if bgp_network_route_new in re_find: - network_need_cfg = True - - result["import_need_cfg"] = import_need_cfg - result["network_need_cfg"] = network_need_cfg - return result - - def merge_bgp_af(self, **kwargs): - """ merge_bgp_af """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - - conf_str = CE_MERGE_BGP_ADDRESS_FAMILY_HEADER % ( - vrf_name, af_type) + CE_MERGE_BGP_ADDRESS_FAMILY_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp address family failed.') - - cmds = [] - - cmd = "ipv4-family vpn-instance %s" % vrf_name - - if af_type == "ipv4multi": - cmd = "ipv4-family multicast" - elif af_type == "ipv4vpn": - cmd = "ipv4-family vpnv4" - elif af_type == "ipv6uni": - cmd = "ipv6-family vpn-instance %s" % vrf_name - if vrf_name == "_public_": - cmd = "ipv6-family unicast" - elif af_type == "ipv6vpn": - cmd = "ipv6-family vpnv6" - elif af_type == "evpn": - cmd = "l2vpn-family evpn" - cmds.append(cmd) - - return cmds - - def create_bgp_af(self, **kwargs): - """ create_bgp_af """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - - conf_str = CE_CREATE_BGP_ADDRESS_FAMILY_HEADER % ( - vrf_name, af_type) + CE_CREATE_BGP_ADDRESS_FAMILY_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create bgp address family failed.') - - cmds = [] - - cmd = "ipv4-family vpn-instance %s" % vrf_name - - if af_type == "ipv4multi": - cmd = "ipv4-family multicast" - elif af_type == "ipv4vpn": - cmd = "ipv4-family vpnv4" - elif af_type == "ipv6uni": - cmd = "ipv6-family vpn-instance %s" % vrf_name - if vrf_name == "_public_": - cmd = "ipv6-family unicast" - elif af_type == "ipv6vpn": - cmd = "ipv6-family vpnv6" - elif af_type == "evpn": - cmd = "l2vpn-family evpn" - cmds.append(cmd) - - return cmds - - def delete_bgp_af(self, **kwargs): - """ delete_bgp_af """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - - conf_str = CE_DELETE_BGP_ADDRESS_FAMILY_HEADER % ( - vrf_name, af_type) + CE_DELETE_BGP_ADDRESS_FAMILY_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete bgp address family failed.') - - cmds = [] - - cmd = "undo ipv4-family vpn-instance %s" % vrf_name - - if af_type == "ipv4multi": - cmd = "undo ipv4-family multicast" - elif af_type == "ipv4vpn": - cmd = "undo ipv4-family vpnv4" - elif af_type == "ipv6uni": - cmd = "undo ipv6-family vpn-instance %s" % vrf_name - if vrf_name == "_public_": - cmd = "undo ipv6-family unicast" - elif af_type == "ipv6vpn": - cmd = "undo ipv6-family vpnv6" - elif af_type == "evpn": - cmd = "l2vpn-family evpn" - cmds.append(cmd) - - return cmds - - def merge_bgp_af_other(self, **kwargs): - """ merge_bgp_af_other """ - - module = kwargs["module"] - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - - conf_str = CE_MERGE_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) - - cmds = [] - - max_load_ibgp_num = module.params['max_load_ibgp_num'] - if max_load_ibgp_num: - conf_str += "%s" % max_load_ibgp_num - - cmd = "maximum load-balancing ibgp %s" % max_load_ibgp_num - cmds.append(cmd) - - ibgp_ecmp_nexthop_changed = module.params['ibgp_ecmp_nexthop_changed'] - if ibgp_ecmp_nexthop_changed != 'no_use': - conf_str += "%s" % ibgp_ecmp_nexthop_changed - - if ibgp_ecmp_nexthop_changed == "true": - cmd = "maximum load-balancing ibgp %s ecmp-nexthop-changed" % max_load_ibgp_num - cmds.append(cmd) - else: - cmd = "undo maximum load-balancing ibgp %s ecmp-nexthop-changed" % max_load_ibgp_num - cmds.append(cmd) - max_load_ebgp_num = module.params['max_load_ebgp_num'] - if max_load_ebgp_num: - conf_str += "%s" % max_load_ebgp_num - - cmd = "maximum load-balancing ebgp %s" % max_load_ebgp_num - cmds.append(cmd) - - ebgp_ecmp_nexthop_changed = module.params['ebgp_ecmp_nexthop_changed'] - if ebgp_ecmp_nexthop_changed != 'no_use': - conf_str += "%s" % ebgp_ecmp_nexthop_changed - - if ebgp_ecmp_nexthop_changed == "true": - cmd = "maximum load-balancing ebgp %s ecmp-nexthop-changed" % max_load_ebgp_num - else: - cmd = "undo maximum load-balancing ebgp %s ecmp-nexthop-changed" % max_load_ebgp_num - cmds.append(cmd) - - maximum_load_balance = module.params['maximum_load_balance'] - if maximum_load_balance: - conf_str += "%s" % maximum_load_balance - - cmd = "maximum load-balancing %s" % maximum_load_balance - cmds.append(cmd) - - ecmp_nexthop_changed = module.params['ecmp_nexthop_changed'] - if ecmp_nexthop_changed != 'no_use': - conf_str += "%s" % ecmp_nexthop_changed - - if ecmp_nexthop_changed == "true": - cmd = "maximum load-balancing %s ecmp-nexthop-changed" % maximum_load_balance - else: - cmd = "undo maximum load-balancing %s ecmp-nexthop-changed" % maximum_load_balance - cmds.append(cmd) - - default_local_pref = module.params['default_local_pref'] - if default_local_pref: - conf_str += "%s" % default_local_pref - - cmd = "default local-preference %s" % default_local_pref - cmds.append(cmd) - - default_med = module.params['default_med'] - if default_med: - conf_str += "%s" % default_med - - cmd = "default med %s" % default_med - cmds.append(cmd) - - default_rt_import_enable = module.params['default_rt_import_enable'] - if default_rt_import_enable != 'no_use': - conf_str += "%s" % default_rt_import_enable - - if default_rt_import_enable == "true": - cmd = "default-route imported" - else: - cmd = "undo default-route imported" - cmds.append(cmd) - - router_id = module.params['router_id'] - if router_id: - conf_str += "%s" % router_id - - cmd = "router-id %s" % router_id - cmds.append(cmd) - - vrf_rid_auto_sel = module.params['vrf_rid_auto_sel'] - if vrf_rid_auto_sel != 'no_use': - conf_str += "%s" % vrf_rid_auto_sel - family = "ipv4-family" - if af_type == "ipv6uni": - family = "ipv6-family" - if vrf_rid_auto_sel == "true": - cmd = "%s vpn-instance %s" % (family, vrf_name) - cmds.append(cmd) - cmd = "router-id auto-select" - cmds.append(cmd) - else: - cmd = "%s vpn-instance %s" % (family, vrf_name) - cmds.append(cmd) - cmd = "undo router-id auto-select" - cmds.append(cmd) - - nexthop_third_party = module.params['nexthop_third_party'] - if nexthop_third_party != 'no_use': - conf_str += "%s" % nexthop_third_party - - if nexthop_third_party == "true": - cmd = "nexthop third-party" - else: - cmd = "undo nexthop third-party" - cmds.append(cmd) - - summary_automatic = module.params['summary_automatic'] - if summary_automatic != 'no_use': - conf_str += "%s" % summary_automatic - - if summary_automatic == "true": - cmd = "summary automatic" - else: - cmd = "undo summary automatic" - cmds.append(cmd) - - auto_frr_enable = module.params['auto_frr_enable'] - if auto_frr_enable != 'no_use': - conf_str += "%s" % auto_frr_enable - - if auto_frr_enable == "true": - cmd = "auto-frr" - else: - cmd = "undo auto-frr" - cmds.append(cmd) - - load_balancing_as_path_ignore = module.params[ - 'load_balancing_as_path_ignore'] - if load_balancing_as_path_ignore != 'no_use': - conf_str += "%s" % load_balancing_as_path_ignore - - if load_balancing_as_path_ignore == "true": - cmd = "load-balancing as-path-ignore" - else: - cmd = "undo load-balancing as-path-ignore" - cmds.append(cmd) - - rib_only_enable = module.params['rib_only_enable'] - if rib_only_enable != 'no_use': - conf_str += "%s" % rib_only_enable - - if rib_only_enable == "true": - cmd = "routing-table rib-only" - else: - cmd = "undo routing-table rib-only" - cmds.append(cmd) - - rib_only_policy_name = module.params['rib_only_policy_name'] - if rib_only_policy_name and rib_only_enable == "true": - conf_str += "%s" % rib_only_policy_name - - cmd = "routing-table rib-only route-policy %s" % rib_only_policy_name - cmds.append(cmd) - - active_route_advertise = module.params['active_route_advertise'] - if active_route_advertise != 'no_use': - conf_str += "%s" % active_route_advertise - - if active_route_advertise == "true": - cmd = "active-route-advertise" - else: - cmd = "undo active-route-advertise" - cmds.append(cmd) - - as_path_neglect = module.params['as_path_neglect'] - if as_path_neglect != 'no_use': - conf_str += "%s" % as_path_neglect - - if as_path_neglect == "true": - cmd = "bestroute as-path-ignore" - else: - cmd = "undo bestroute as-path-ignore" - cmds.append(cmd) - - med_none_as_maximum = module.params['med_none_as_maximum'] - if med_none_as_maximum != 'no_use': - conf_str += "%s" % med_none_as_maximum - - if med_none_as_maximum == "true": - cmd = "bestroute med-none-as-maximum" - else: - cmd = "undo bestroute med-none-as-maximum" - cmds.append(cmd) - - router_id_neglect = module.params['router_id_neglect'] - if router_id_neglect != 'no_use': - conf_str += "%s" % router_id_neglect - - if router_id_neglect == "true": - cmd = "bestroute router-id-ignore" - else: - cmd = "undo bestroute router-id-ignore" - cmds.append(cmd) - - igp_metric_ignore = module.params['igp_metric_ignore'] - if igp_metric_ignore != 'no_use': - conf_str += "%s" % igp_metric_ignore - - if igp_metric_ignore == "true": - cmd = "bestroute igp-metric-ignore" - cmds.append(cmd) - else: - cmd = "undo bestroute igp-metric-ignore" - cmds.append(cmd) - always_compare_med = module.params['always_compare_med'] - if always_compare_med != 'no_use': - conf_str += "%s" % always_compare_med - - if always_compare_med == "true": - cmd = "compare-different-as-med" - cmds.append(cmd) - else: - cmd = "undo compare-different-as-med" - cmds.append(cmd) - determin_med = module.params['determin_med'] - if determin_med != 'no_use': - conf_str += "%s" % determin_med - - if determin_med == "true": - cmd = "deterministic-med" - cmds.append(cmd) - else: - cmd = "undo deterministic-med" - cmds.append(cmd) - - preference_external = module.params['preference_external'] - preference_internal = module.params['preference_internal'] - preference_local = module.params['preference_local'] - if any([preference_external, preference_internal, preference_local]): - preference_external = preference_external or "255" - preference_internal = preference_internal or "255" - preference_local = preference_local or "255" - - conf_str += "%s" % preference_external - conf_str += "%s" % preference_internal - conf_str += "%s" % preference_local - - cmd = "preference %s %s %s" % ( - preference_external, preference_internal, preference_local) - cmds.append(cmd) - - prefrence_policy_name = module.params['prefrence_policy_name'] - if prefrence_policy_name: - conf_str += "%s" % prefrence_policy_name - - cmd = "preference route-policy %s" % prefrence_policy_name - cmds.append(cmd) - - reflect_between_client = module.params['reflect_between_client'] - if reflect_between_client != 'no_use': - conf_str += "%s" % reflect_between_client - - if reflect_between_client == "true": - cmd = "reflect between-clients" - else: - cmd = "undo reflect between-clients" - cmds.append(cmd) - - reflector_cluster_id = module.params['reflector_cluster_id'] - if reflector_cluster_id: - conf_str += "%s" % reflector_cluster_id - - cmd = "reflector cluster-id %s" % reflector_cluster_id - cmds.append(cmd) - - reflector_cluster_ipv4 = module.params['reflector_cluster_ipv4'] - if reflector_cluster_ipv4: - conf_str += "%s" % reflector_cluster_ipv4 - - cmd = "reflector cluster-id %s" % reflector_cluster_ipv4 - cmds.append(cmd) - - rr_filter_number = module.params['rr_filter_number'] - if rr_filter_number: - conf_str += "%s" % rr_filter_number - cmd = 'rr-filter %s' % rr_filter_number - cmds.append(cmd) - - policy_vpn_target = module.params['policy_vpn_target'] - if policy_vpn_target != 'no_use': - conf_str += "%s" % policy_vpn_target - if policy_vpn_target == 'true': - cmd = 'policy vpn-target' - else: - cmd = 'undo policy vpn-target' - cmds.append(cmd) - - next_hop_sel_depend_type = module.params['next_hop_sel_depend_type'] - if next_hop_sel_depend_type: - conf_str += "%s" % next_hop_sel_depend_type - - nhp_relay_route_policy_name = module.params[ - 'nhp_relay_route_policy_name'] - if nhp_relay_route_policy_name: - conf_str += "%s" % nhp_relay_route_policy_name - - cmd = "nexthop recursive-lookup route-policy %s" % nhp_relay_route_policy_name - cmds.append(cmd) - - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - if ebgp_if_sensitive != 'no_use': - conf_str += "%s" % ebgp_if_sensitive - - if ebgp_if_sensitive == "true": - cmd = "ebgp-interface-sensitive" - else: - cmd = "undo ebgp-interface-sensitive" - cmds.append(cmd) - - reflect_chg_path = module.params['reflect_chg_path'] - if reflect_chg_path != 'no_use': - conf_str += "%s" % reflect_chg_path - - if reflect_chg_path == "true": - cmd = "reflect change-path-attribute" - else: - cmd = "undo reflect change-path-attribute" - cmds.append(cmd) - - add_path_sel_num = module.params['add_path_sel_num'] - if add_path_sel_num: - conf_str += "%s" % add_path_sel_num - - cmd = "bestroute add-path path-number %s" % add_path_sel_num - cmds.append(cmd) - - route_sel_delay = module.params['route_sel_delay'] - if route_sel_delay: - conf_str += "%s" % route_sel_delay - - cmd = "route-select delay %s" % route_sel_delay - cmds.append(cmd) - - allow_invalid_as = module.params['allow_invalid_as'] - if allow_invalid_as != 'no_use': - conf_str += "%s" % allow_invalid_as - - policy_ext_comm_enable = module.params['policy_ext_comm_enable'] - if policy_ext_comm_enable != 'no_use': - conf_str += "%s" % policy_ext_comm_enable - - if policy_ext_comm_enable == "true": - cmd = "ext-community-change enable" - else: - cmd = "undo ext-community-change enable" - cmds.append(cmd) - - supernet_uni_adv = module.params['supernet_uni_adv'] - if supernet_uni_adv != 'no_use': - conf_str += "%s" % supernet_uni_adv - - if supernet_uni_adv == "true": - cmd = "supernet unicast advertise enable" - else: - cmd = "undo supernet unicast advertise enable" - cmds.append(cmd) - - supernet_label_adv = module.params['supernet_label_adv'] - if supernet_label_adv != 'no_use': - conf_str += "%s" % supernet_label_adv - - if supernet_label_adv == "true": - cmd = "supernet label-route advertise enable" - else: - cmd = "undo supernet label-route advertise enable" - cmds.append(cmd) - - ingress_lsp_policy_name = module.params['ingress_lsp_policy_name'] - if ingress_lsp_policy_name: - conf_str += "%s" % ingress_lsp_policy_name - cmd = "ingress-lsp trigger route-policy %s" % ingress_lsp_policy_name - cmds.append(cmd) - - originator_prior = module.params['originator_prior'] - if originator_prior != 'no_use': - conf_str += "%s" % originator_prior - if originator_prior == "true": - cmd = "bestroute routerid-prior-clusterlist" - else: - cmd = "undo bestroute routerid-prior-clusterlist" - cmds.append(cmd) - - lowest_priority = module.params['lowest_priority'] - if lowest_priority != 'no_use': - conf_str += "%s" % lowest_priority - - if lowest_priority == "true": - cmd = "advertise lowest-priority on-startup" - else: - cmd = "undo advertise lowest-priority on-startup" - cmds.append(cmd) - - relay_delay_enable = module.params['relay_delay_enable'] - if relay_delay_enable != 'no_use': - conf_str += "%s" % relay_delay_enable - - if relay_delay_enable == "true": - cmd = "nexthop recursive-lookup restrain enable" - else: - cmd = "nexthop recursive-lookup restrain disable" - cmds.append(cmd) - conf_str += CE_MERGE_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Merge bgp address family other agrus failed.') - - return cmds - - def delete_bgp_af_other(self, **kwargs): - """ delete_bgp_af_other """ - - module = kwargs["module"] - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - - conf_str = CE_MERGE_BGP_ADDRESS_FAMILY_HEADER % (vrf_name, af_type) - - cmds = [] - - router_id = module.params['router_id'] - if router_id: - conf_str += "" - - cmd = "undo router-id %s" % router_id - cmds.append(cmd) - - determin_med = module.params['determin_med'] - if determin_med != 'no_use': - conf_str += "" - - cmd = "undo deterministic-med" - cmds.append(cmd) - - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - if ebgp_if_sensitive != 'no_use': - conf_str += "" - - cmd = "undo ebgp-interface-sensitive" - cmds.append(cmd) - - relay_delay_enable = module.params['relay_delay_enable'] - if relay_delay_enable != 'no_use': - conf_str += "" - - conf_str += CE_MERGE_BGP_ADDRESS_FAMILY_TAIL - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Merge bgp address family other agrus failed.') - - return cmds - - def merge_bgp_import_route(self, **kwargs): - """ merge_bgp_import_route """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - import_protocol = module.params['import_protocol'] - import_process_id = module.params['import_process_id'] - - if import_protocol == "direct" or import_protocol == "static": - import_process_id = "0" - - conf_str = CE_MERGE_BGP_IMPORT_ROUTE_HEADER % ( - vrf_name, af_type, import_protocol, import_process_id) + CE_MERGE_BGP_IMPORT_ROUTE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp import route failed.') - - cmds = [] - cmd = "import-route %s %s" % (import_protocol, import_process_id) - if import_protocol == "direct" or import_protocol == "static": - cmd = "import-route %s" % import_protocol - cmds.append(cmd) - - return cmds - - def create_bgp_import_route(self, **kwargs): - """ create_bgp_import_route """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - import_protocol = module.params['import_protocol'] - import_process_id = module.params['import_process_id'] - - if import_protocol == "direct" or import_protocol == "static": - import_process_id = "0" - - conf_str = CE_CREATE_BGP_IMPORT_ROUTE % ( - vrf_name, af_type, import_protocol, import_process_id) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create bgp import route failed.') - - cmds = [] - cmd = "import-route %s %s" % (import_protocol, import_process_id) - if import_protocol == "direct" or import_protocol == "static": - cmd = "import-route %s" % import_protocol - cmds.append(cmd) - - return cmds - - def delete_bgp_import_route(self, **kwargs): - """ delete_bgp_import_route """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - import_protocol = module.params['import_protocol'] - import_process_id = module.params['import_process_id'] - - if import_protocol == "direct" or import_protocol == "static": - import_process_id = "0" - - conf_str = CE_DELETE_BGP_IMPORT_ROUTE % ( - vrf_name, af_type, import_protocol, import_process_id) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete bgp import route failed.') - - cmds = [] - cmd = "undo import-route %s %s" % (import_protocol, import_process_id) - if import_protocol == "direct" or import_protocol == "static": - cmd = "undo import-route %s" % import_protocol - cmds.append(cmd) - - return cmds - - def merge_bgp_network_route(self, **kwargs): - """ merge_bgp_network_route """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - network_address = module.params['network_address'] - mask_len = module.params['mask_len'] - - conf_str = CE_MERGE_BGP_NETWORK_ROUTE_HEADER % ( - vrf_name, af_type, network_address, mask_len) + CE_MERGE_BGP_NETWORK_ROUTE_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp network route failed.') - - cmds = [] - cmd = "network %s %s" % (network_address, mask_len) - cmds.append(cmd) - - return cmds - - def create_bgp_network_route(self, **kwargs): - """ create_bgp_network_route """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - network_address = module.params['network_address'] - mask_len = module.params['mask_len'] - - conf_str = CE_CREATE_BGP_NETWORK_ROUTE % ( - vrf_name, af_type, network_address, mask_len) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create bgp network route failed.') - - cmds = [] - cmd = "network %s %s" % (network_address, mask_len) - cmds.append(cmd) - - return cmds - - def delete_bgp_network_route(self, **kwargs): - """ delete_bgp_network_route """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - af_type = module.params['af_type'] - network_address = module.params['network_address'] - mask_len = module.params['mask_len'] - - conf_str = CE_DELETE_BGP_NETWORK_ROUTE % ( - vrf_name, af_type, network_address, mask_len) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete bgp network route failed.') - - cmds = [] - cmd = "undo network %s %s" % (network_address, mask_len) - cmds.append(cmd) - - return cmds - - -def main(): - """ main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - vrf_name=dict(type='str', required=True), - af_type=dict(choices=['ipv4uni', 'ipv4multi', 'ipv4vpn', - 'ipv6uni', 'ipv6vpn', 'evpn'], required=True), - max_load_ibgp_num=dict(type='str'), - ibgp_ecmp_nexthop_changed=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - max_load_ebgp_num=dict(type='str'), - ebgp_ecmp_nexthop_changed=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - maximum_load_balance=dict(type='str'), - ecmp_nexthop_changed=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - default_local_pref=dict(type='str'), - default_med=dict(type='str'), - default_rt_import_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - router_id=dict(type='str'), - vrf_rid_auto_sel=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - nexthop_third_party=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - summary_automatic=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - auto_frr_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - load_balancing_as_path_ignore=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - rib_only_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - rib_only_policy_name=dict(type='str'), - active_route_advertise=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - as_path_neglect=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - med_none_as_maximum=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - router_id_neglect=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - igp_metric_ignore=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - always_compare_med=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - determin_med=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - preference_external=dict(type='str'), - preference_internal=dict(type='str'), - preference_local=dict(type='str'), - prefrence_policy_name=dict(type='str'), - reflect_between_client=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - reflector_cluster_id=dict(type='str'), - reflector_cluster_ipv4=dict(type='str'), - rr_filter_number=dict(type='str'), - policy_vpn_target=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - next_hop_sel_depend_type=dict( - choices=['default', 'dependTunnel', 'dependIp']), - nhp_relay_route_policy_name=dict(type='str'), - ebgp_if_sensitive=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - reflect_chg_path=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - add_path_sel_num=dict(type='str'), - route_sel_delay=dict(type='str'), - allow_invalid_as=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - policy_ext_comm_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - supernet_uni_adv=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - supernet_label_adv=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - ingress_lsp_policy_name=dict(type='str'), - originator_prior=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - lowest_priority=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - relay_delay_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - import_protocol=dict( - choices=['direct', 'ospf', 'isis', 'static', 'rip', 'ospfv3', 'ripng']), - import_process_id=dict(type='str'), - network_address=dict(type='str'), - mask_len=dict(type='str')) - - argument_spec.update(ce_argument_spec) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - - changed = False - proposed = dict() - existing = dict() - end_state = dict() - updates = [] - - state = module.params['state'] - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - max_load_ibgp_num = module.params['max_load_ibgp_num'] - ibgp_ecmp_nexthop_changed = module.params['ibgp_ecmp_nexthop_changed'] - max_load_ebgp_num = module.params['max_load_ebgp_num'] - ebgp_ecmp_nexthop_changed = module.params['ebgp_ecmp_nexthop_changed'] - maximum_load_balance = module.params['maximum_load_balance'] - ecmp_nexthop_changed = module.params['ecmp_nexthop_changed'] - default_local_pref = module.params['default_local_pref'] - default_med = module.params['default_med'] - default_rt_import_enable = module.params['default_rt_import_enable'] - router_id = module.params['router_id'] - vrf_rid_auto_sel = module.params['vrf_rid_auto_sel'] - nexthop_third_party = module.params['nexthop_third_party'] - summary_automatic = module.params['summary_automatic'] - auto_frr_enable = module.params['auto_frr_enable'] - load_balancing_as_path_ignore = module.params[ - 'load_balancing_as_path_ignore'] - rib_only_enable = module.params['rib_only_enable'] - rib_only_policy_name = module.params['rib_only_policy_name'] - active_route_advertise = module.params['active_route_advertise'] - as_path_neglect = module.params['as_path_neglect'] - med_none_as_maximum = module.params['med_none_as_maximum'] - router_id_neglect = module.params['router_id_neglect'] - igp_metric_ignore = module.params['igp_metric_ignore'] - always_compare_med = module.params['always_compare_med'] - determin_med = module.params['determin_med'] - preference_external = module.params['preference_external'] - preference_internal = module.params['preference_internal'] - preference_local = module.params['preference_local'] - prefrence_policy_name = module.params['prefrence_policy_name'] - reflect_between_client = module.params['reflect_between_client'] - reflector_cluster_id = module.params['reflector_cluster_id'] - reflector_cluster_ipv4 = module.params['reflector_cluster_ipv4'] - rr_filter_number = module.params['rr_filter_number'] - policy_vpn_target = module.params['policy_vpn_target'] - next_hop_sel_depend_type = module.params['next_hop_sel_depend_type'] - nhp_relay_route_policy_name = module.params['nhp_relay_route_policy_name'] - ebgp_if_sensitive = module.params['ebgp_if_sensitive'] - reflect_chg_path = module.params['reflect_chg_path'] - add_path_sel_num = module.params['add_path_sel_num'] - route_sel_delay = module.params['route_sel_delay'] - allow_invalid_as = module.params['allow_invalid_as'] - policy_ext_comm_enable = module.params['policy_ext_comm_enable'] - supernet_uni_adv = module.params['supernet_uni_adv'] - supernet_label_adv = module.params['supernet_label_adv'] - ingress_lsp_policy_name = module.params['ingress_lsp_policy_name'] - originator_prior = module.params['originator_prior'] - lowest_priority = module.params['lowest_priority'] - relay_delay_enable = module.params['relay_delay_enable'] - import_protocol = module.params['import_protocol'] - import_process_id = module.params['import_process_id'] - network_address = module.params['network_address'] - mask_len = module.params['mask_len'] - - ce_bgp_af_obj = BgpAf() - - if not ce_bgp_af_obj: - module.fail_json(msg='Error: Init module failed.') - - # get proposed - proposed["state"] = state - if vrf_name: - proposed["vrf_name"] = vrf_name - if af_type: - proposed["af_type"] = af_type - if max_load_ibgp_num: - proposed["max_load_ibgp_num"] = max_load_ibgp_num - if ibgp_ecmp_nexthop_changed != 'no_use': - proposed["ibgp_ecmp_nexthop_changed"] = ibgp_ecmp_nexthop_changed - if max_load_ebgp_num: - proposed["max_load_ebgp_num"] = max_load_ebgp_num - if ebgp_ecmp_nexthop_changed != 'no_use': - proposed["ebgp_ecmp_nexthop_changed"] = ebgp_ecmp_nexthop_changed - if maximum_load_balance: - proposed["maximum_load_balance"] = maximum_load_balance - if ecmp_nexthop_changed != 'no_use': - proposed["ecmp_nexthop_changed"] = ecmp_nexthop_changed - if default_local_pref: - proposed["default_local_pref"] = default_local_pref - if default_med: - proposed["default_med"] = default_med - if default_rt_import_enable != 'no_use': - proposed["default_rt_import_enable"] = default_rt_import_enable - if router_id: - proposed["router_id"] = router_id - if vrf_rid_auto_sel != 'no_use': - proposed["vrf_rid_auto_sel"] = vrf_rid_auto_sel - if nexthop_third_party != 'no_use': - proposed["nexthop_third_party"] = nexthop_third_party - if summary_automatic != 'no_use': - proposed["summary_automatic"] = summary_automatic - if auto_frr_enable != 'no_use': - proposed["auto_frr_enable"] = auto_frr_enable - if load_balancing_as_path_ignore != 'no_use': - proposed["load_balancing_as_path_ignore"] = load_balancing_as_path_ignore - if rib_only_enable != 'no_use': - proposed["rib_only_enable"] = rib_only_enable - if rib_only_policy_name: - proposed["rib_only_policy_name"] = rib_only_policy_name - if active_route_advertise != 'no_use': - proposed["active_route_advertise"] = active_route_advertise - if as_path_neglect != 'no_use': - proposed["as_path_neglect"] = as_path_neglect - if med_none_as_maximum != 'no_use': - proposed["med_none_as_maximum"] = med_none_as_maximum - if router_id_neglect != 'no_use': - proposed["router_id_neglect"] = router_id_neglect - if igp_metric_ignore != 'no_use': - proposed["igp_metric_ignore"] = igp_metric_ignore - if always_compare_med != 'no_use': - proposed["always_compare_med"] = always_compare_med - if determin_med != 'no_use': - proposed["determin_med"] = determin_med - if preference_external: - proposed["preference_external"] = preference_external - if preference_internal: - proposed["preference_internal"] = preference_internal - if preference_local: - proposed["preference_local"] = preference_local - if prefrence_policy_name: - proposed["prefrence_policy_name"] = prefrence_policy_name - if reflect_between_client != 'no_use': - proposed["reflect_between_client"] = reflect_between_client - if reflector_cluster_id: - proposed["reflector_cluster_id"] = reflector_cluster_id - if reflector_cluster_ipv4: - proposed["reflector_cluster_ipv4"] = reflector_cluster_ipv4 - if rr_filter_number: - proposed["rr_filter_number"] = rr_filter_number - if policy_vpn_target != 'no_use': - proposed["policy_vpn_target"] = policy_vpn_target - if next_hop_sel_depend_type: - proposed["next_hop_sel_depend_type"] = next_hop_sel_depend_type - if nhp_relay_route_policy_name: - proposed["nhp_relay_route_policy_name"] = nhp_relay_route_policy_name - if ebgp_if_sensitive != 'no_use': - proposed["ebgp_if_sensitive"] = ebgp_if_sensitive - if reflect_chg_path != 'no_use': - proposed["reflect_chg_path"] = reflect_chg_path - if add_path_sel_num: - proposed["add_path_sel_num"] = add_path_sel_num - if route_sel_delay: - proposed["route_sel_delay"] = route_sel_delay - if allow_invalid_as != 'no_use': - proposed["allow_invalid_as"] = allow_invalid_as - if policy_ext_comm_enable != 'no_use': - proposed["policy_ext_comm_enable"] = policy_ext_comm_enable - if supernet_uni_adv != 'no_use': - proposed["supernet_uni_adv"] = supernet_uni_adv - if supernet_label_adv != 'no_use': - proposed["supernet_label_adv"] = supernet_label_adv - if ingress_lsp_policy_name: - proposed["ingress_lsp_policy_name"] = ingress_lsp_policy_name - if originator_prior != 'no_use': - proposed["originator_prior"] = originator_prior - if lowest_priority != 'no_use': - proposed["lowest_priority"] = lowest_priority - if relay_delay_enable != 'no_use': - proposed["relay_delay_enable"] = relay_delay_enable - if import_protocol: - proposed["import_protocol"] = import_protocol - if import_process_id: - proposed["import_process_id"] = import_process_id - if network_address: - proposed["network_address"] = network_address - if mask_len: - proposed["mask_len"] = mask_len - - bgp_af_rst = ce_bgp_af_obj.check_bgp_af_args(module=module) - bgp_af_other_rst = ce_bgp_af_obj.check_bgp_af_other_args(module=module) - bgp_af_other_can_del_rst = ce_bgp_af_obj.check_bgp_af_other_can_del( - module=module) - bgp_import_network_route_rst = ce_bgp_af_obj.check_bgp_import_network_route( - module=module) - - # state exist bgp address family config - exist_tmp = dict() - for item in bgp_af_rst: - if item != "need_cfg": - exist_tmp[item] = bgp_af_rst[item] - - if exist_tmp: - existing["bgp af"] = exist_tmp - # state exist bgp address family other config - exist_tmp = dict() - for item in bgp_af_other_rst: - if item != "need_cfg": - exist_tmp[item] = bgp_af_other_rst[item] - if exist_tmp: - existing["bgp af other"] = exist_tmp - # state exist bgp import route config - exist_tmp = dict() - for item in bgp_import_network_route_rst: - if item != "need_cfg": - exist_tmp[item] = bgp_import_network_route_rst[item] - - if exist_tmp: - existing["bgp import & network route"] = exist_tmp - - if state == "present": - if bgp_af_rst["need_cfg"] and bgp_import_network_route_rst["import_need_cfg"] and \ - bgp_import_network_route_rst["network_need_cfg"]: - changed = True - if "af_type" in bgp_af_rst.keys(): - conf_str = CE_MERGE_BGP_ADDRESS_FAMILY_HEADER % ( - vrf_name, af_type) - else: - conf_str = CE_CREATE_BGP_ADDRESS_FAMILY_HEADER % ( - vrf_name, af_type) - - if "bgp_import_route" in bgp_import_network_route_rst.keys(): - conf_str += CE_BGP_MERGE_IMPORT_UNIT % ( - import_protocol, import_process_id) - else: - conf_str += CE_BGP_CREATE_IMPORT_UNIT % ( - import_protocol, import_process_id) - - if "bgp_network_route" in bgp_import_network_route_rst.keys(): - conf_str += CE_BGP_MERGE_NETWORK_UNIT % ( - network_address, mask_len) - else: - conf_str += CE_BGP_CREATE_NETWORK_UNIT % ( - network_address, mask_len) - - conf_str += CE_MERGE_BGP_ADDRESS_FAMILY_TAIL - recv_xml = ce_bgp_af_obj.netconf_set_config( - module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Present bgp af_type import and network route failed.') - - cmd = "import-route %s %s" % (import_protocol, import_process_id) - updates.append(cmd) - cmd = "network %s %s" % (network_address, mask_len) - updates.append(cmd) - - elif bgp_import_network_route_rst["import_need_cfg"] and bgp_import_network_route_rst["network_need_cfg"]: - changed = True - conf_str = CE_BGP_IMPORT_NETWORK_ROUTE_HEADER % (vrf_name, af_type) - - if "bgp_import_route" in bgp_import_network_route_rst.keys(): - conf_str += CE_BGP_MERGE_IMPORT_UNIT % ( - import_protocol, import_process_id) - else: - conf_str += CE_BGP_CREATE_IMPORT_UNIT % ( - import_protocol, import_process_id) - - if "bgp_network_route" in bgp_import_network_route_rst.keys(): - conf_str += CE_BGP_MERGE_NETWORK_UNIT % ( - network_address, mask_len) - else: - conf_str += CE_BGP_CREATE_NETWORK_UNIT % ( - network_address, mask_len) - - conf_str += CE_BGP_IMPORT_NETWORK_ROUTE_TAIL - recv_xml = ce_bgp_af_obj.netconf_set_config( - module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Present bgp import and network route failed.') - - cmd = "import-route %s %s" % (import_protocol, import_process_id) - updates.append(cmd) - cmd = "network %s %s" % (network_address, mask_len) - updates.append(cmd) - - else: - if bgp_af_rst["need_cfg"]: - if "af_type" in bgp_af_rst.keys(): - cmd = ce_bgp_af_obj.merge_bgp_af(module=module) - changed = True - for item in cmd: - updates.append(item) - else: - cmd = ce_bgp_af_obj.create_bgp_af(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_af_other_rst["need_cfg"]: - cmd = ce_bgp_af_obj.merge_bgp_af_other(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_import_network_route_rst["import_need_cfg"]: - if "bgp_import_route" in bgp_import_network_route_rst.keys(): - cmd = ce_bgp_af_obj.merge_bgp_import_route(module=module) - changed = True - for item in cmd: - updates.append(item) - else: - cmd = ce_bgp_af_obj.create_bgp_import_route(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_import_network_route_rst["network_need_cfg"]: - if "bgp_network_route" in bgp_import_network_route_rst.keys(): - cmd = ce_bgp_af_obj.merge_bgp_network_route(module=module) - changed = True - for item in cmd: - updates.append(item) - else: - cmd = ce_bgp_af_obj.create_bgp_network_route(module=module) - changed = True - for item in cmd: - updates.append(item) - - else: - if bgp_import_network_route_rst["import_need_cfg"] and bgp_import_network_route_rst["network_need_cfg"]: - changed = True - conf_str = CE_BGP_IMPORT_NETWORK_ROUTE_HEADER % (vrf_name, af_type) - conf_str += CE_BGP_DELETE_IMPORT_UNIT % ( - import_protocol, import_process_id) - conf_str += CE_BGP_DELETE_NETWORK_UNIT % ( - network_address, mask_len) - - conf_str += CE_BGP_IMPORT_NETWORK_ROUTE_TAIL - recv_xml = ce_bgp_af_obj.netconf_set_config( - module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json( - msg='Error: Absent bgp import and network route failed.') - - cmd = "undo import-route %s %s" % (import_protocol, - import_process_id) - updates.append(cmd) - cmd = "undo network %s %s" % (network_address, mask_len) - updates.append(cmd) - - else: - if bgp_import_network_route_rst["import_need_cfg"]: - cmd = ce_bgp_af_obj.delete_bgp_import_route(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_import_network_route_rst["network_need_cfg"]: - cmd = ce_bgp_af_obj.delete_bgp_network_route(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_af_other_can_del_rst["need_cfg"]: - cmd = ce_bgp_af_obj.delete_bgp_af_other(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_af_rst["need_cfg"] and not bgp_af_other_can_del_rst["need_cfg"]: - cmd = ce_bgp_af_obj.delete_bgp_af(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_af_other_rst["need_cfg"]: - pass - - # state end bgp address family config - bgp_af_rst = ce_bgp_af_obj.check_bgp_af_args(module=module) - end_tmp = dict() - for item in bgp_af_rst: - if item != "need_cfg": - end_tmp[item] = bgp_af_rst[item] - if end_tmp: - end_state["bgp af"] = end_tmp - # state end bgp address family other config - bgp_af_other_rst = ce_bgp_af_obj.check_bgp_af_other_args(module=module) - end_tmp = dict() - for item in bgp_af_other_rst: - if item != "need_cfg": - end_tmp[item] = bgp_af_other_rst[item] - if end_tmp: - end_state["bgp af other"] = end_tmp - # state end bgp import route config - bgp_import_network_route_rst = ce_bgp_af_obj.check_bgp_import_network_route( - module=module) - end_tmp = dict() - for item in bgp_import_network_route_rst: - if item != "need_cfg": - end_tmp[item] = bgp_import_network_route_rst[item] - if end_tmp: - end_state["bgp import & network route"] = end_tmp - if end_state == existing: - changed = False - updates = list() - - results = dict() - results['proposed'] = proposed - results['existing'] = existing - results['changed'] = changed - results['end_state'] = end_state - results['updates'] = updates - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_bgp_neighbor.py b/plugins/modules/network/cloudengine/ce_bgp_neighbor.py deleted file mode 100644 index a61eaf1a11..0000000000 --- a/plugins/modules/network/cloudengine/ce_bgp_neighbor.py +++ /dev/null @@ -1,2051 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_bgp_neighbor -short_description: Manages BGP peer configuration on HUAWEI CloudEngine switches. -description: - - Manages BGP peer configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] - vrf_name: - description: - - Name of a BGP instance. The name is a case-sensitive string of characters. - The BGP instance can be used only after the corresponding VPN instance is created. - required: true - peer_addr: - description: - - Connection address of a peer, which can be an IPv4 or IPv6 address. - required: true - remote_as: - description: - - AS number of a peer. - The value is a string of 1 to 11 characters. - required: true - description: - description: - - Description of a peer, which can be letters or digits. - The value is a string of 1 to 80 characters. - fake_as: - description: - - Fake AS number that is specified for a local peer. - The value is a string of 1 to 11 characters. - dual_as: - description: - - If the value is true, the EBGP peer can use either a fake AS number or the actual AS number. - If the value is false, the EBGP peer can only use a fake AS number. - choices: ['no_use','true','false'] - default: no_use - conventional: - description: - - If the value is true, the router has all extended capabilities. - If the value is false, the router does not have all extended capabilities. - choices: ['no_use','true','false'] - default: no_use - route_refresh: - description: - - If the value is true, BGP is enabled to advertise REFRESH packets. - If the value is false, the route refresh function is enabled. - choices: ['no_use','true','false'] - default: no_use - is_ignore: - description: - - If the value is true, the session with a specified peer is torn down and all related - routing entries are cleared. - If the value is false, the session with a specified peer is retained. - choices: ['no_use','true','false'] - default: no_use - local_if_name: - description: - - Name of a source interface that sends BGP packets. - The value is a string of 1 to 63 characters. - ebgp_max_hop: - description: - - Maximum number of hops in an indirect EBGP connection. - The value is an ranging from 1 to 255. - valid_ttl_hops: - description: - - Enable GTSM on a peer or peer group. - The valid-TTL-Value parameter is used to specify the number of TTL hops to be detected. - The value is an integer ranging from 1 to 255. - connect_mode: - description: - - The value can be Connect-only, Listen-only, or Both. - is_log_change: - description: - - If the value is true, BGP is enabled to record peer session status and event information. - If the value is false, BGP is disabled from recording peer session status and event information. - choices: ['no_use','true','false'] - default: no_use - pswd_type: - description: - - Enable BGP peers to establish a TCP connection and perform the Message Digest 5 (MD5) - authentication for BGP messages. - choices: ['null','cipher','simple'] - pswd_cipher_text: - description: - - The character string in a password identifies the contents of the password, spaces not supported. - The value is a string of 1 to 255 characters. - keep_alive_time: - description: - - Specify the Keepalive time of a peer or peer group. - The value is an integer ranging from 0 to 21845. The default value is 60. - hold_time: - description: - - Specify the Hold time of a peer or peer group. - The value is 0 or an integer ranging from 3 to 65535. - min_hold_time: - description: - - Specify the Min hold time of a peer or peer group. - key_chain_name: - description: - - Specify the Keychain authentication name used when BGP peers establish a TCP connection. - The value is a string of 1 to 47 case-insensitive characters. - conn_retry_time: - description: - - ConnectRetry interval. - The value is an integer ranging from 1 to 65535. - tcp_MSS: - description: - - Maximum TCP MSS value used for TCP connection establishment for a peer. - The value is an integer ranging from 176 to 4096. - mpls_local_ifnet_disable: - description: - - If the value is true, peer create MPLS Local IFNET disable. - If the value is false, peer create MPLS Local IFNET enable. - choices: ['no_use','true','false'] - default: no_use - prepend_global_as: - description: - - Add the global AS number to the Update packets to be advertised. - choices: ['no_use','true','false'] - default: no_use - prepend_fake_as: - description: - - Add the Fake AS number to received Update packets. - choices: ['no_use','true','false'] - default: no_use - is_bfd_block: - description: - - If the value is true, peers are enabled to inherit the BFD function from the peer group. - If the value is false, peers are disabled to inherit the BFD function from the peer group. - choices: ['no_use','true','false'] - default: no_use - multiplier: - description: - - Specify the detection multiplier. The default value is 3. - The value is an integer ranging from 3 to 50. - is_bfd_enable: - description: - - If the value is true, BFD is enabled. - If the value is false, BFD is disabled. - choices: ['no_use','true','false'] - default: no_use - rx_interval: - description: - - Specify the minimum interval at which BFD packets are received. - The value is an integer ranging from 50 to 1000, in milliseconds. - tx_interval: - description: - - Specify the minimum interval at which BFD packets are sent. - The value is an integer ranging from 50 to 1000, in milliseconds. - is_single_hop: - description: - - If the value is true, the system is enabled to preferentially use the single-hop mode for - BFD session setup between IBGP peers. - If the value is false, the system is disabled from preferentially using the single-hop - mode for BFD session setup between IBGP peers. - choices: ['no_use','true','false'] - default: no_use -''' - -EXAMPLES = ''' - -- name: CloudEngine BGP neighbor test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config bgp peer" - ce_bgp_neighbor: - state: present - vrf_name: js - peer_addr: 192.168.10.10 - remote_as: 500 - provider: "{{ cli }}" - - - name: "Config bgp route id" - ce_bgp_neighbor: - state: absent - vrf_name: js - peer_addr: 192.168.10.10 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"peer_addr": "192.168.10.10", "remote_as": "500", "state": "present", "vrf_name": "js"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"bgp peer": []} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"bgp peer": [["192.168.10.10", "500"]]} -updates: - description: command sent to the device - returned: always - type: list - sample: ["peer 192.168.10.10 as-number 500"] -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - - -# get bgp peer -CE_GET_BGP_PEER_HEADER = """ - - - - - - %s - - - %s -""" -CE_GET_BGP_PEER_TAIL = """ - - - - - - - -""" - -# merge bgp peer -CE_MERGE_BGP_PEER_HEADER = """ - - - - - - %s - - - %s -""" -CE_MERGE_BGP_PEER_TAIL = """ - - - - - - - -""" - -# create bgp peer -CE_CREATE_BGP_PEER_HEADER = """ - - - - - - %s - - - %s -""" -CE_CREATE_BGP_PEER_TAIL = """ - - - - - - - -""" - -# delete bgp peer -CE_DELETE_BGP_PEER_HEADER = """ - - - - - - %s - - - %s -""" -CE_DELETE_BGP_PEER_TAIL = """ - - - - - - - -""" - -# get peer bfd -CE_GET_PEER_BFD_HEADER = """ - - - - - - %s - - - %s - -""" -CE_GET_PEER_BFD_TAIL = """ - - - - - - - - -""" - -# merge peer bfd -CE_MERGE_PEER_BFD_HEADER = """ - - - - - - %s - - - %s - -""" -CE_MERGE_PEER_BFD_TAIL = """ - - - - - - - - -""" - -# delete peer bfd -CE_DELETE_PEER_BFD_HEADER = """ - - - - - - %s - - - %s - -""" -CE_DELETE_PEER_BFD_TAIL = """ - - - - - - - - -""" - - -class BgpNeighbor(object): - """ Manages BGP peer configuration """ - - def netconf_get_config(self, **kwargs): - """ netconf_get_config """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = get_nc_config(module, conf_str) - - return xml_str - - def netconf_set_config(self, **kwargs): - """ netconf_set_config """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = set_nc_config(module, conf_str) - - return xml_str - - def check_bgp_peer_args(self, **kwargs): - """ check_bgp_peer_args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - - vrf_name = module.params['vrf_name'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='Error: The len of vrf_name %s is out of [1 - 31].' % vrf_name) - - peer_addr = module.params['peer_addr'] - if peer_addr: - if not check_ip_addr(ipaddr=peer_addr): - module.fail_json( - msg='Error: The peer_addr %s is invalid.' % peer_addr) - - need_cfg = True - - remote_as = module.params['remote_as'] - if remote_as: - if len(remote_as) > 11 or len(remote_as) < 1: - module.fail_json( - msg='Error: The len of remote_as %s is out of [1 - 11].' % remote_as) - - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def check_bgp_peer_other_args(self, **kwargs): - """ check_bgp_peer_other_args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - - peerip = module.params['peer_addr'] - vrf_name = module.params['vrf_name'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='Error: The len of vrf_name %s is out of [1 - 31].' % vrf_name) - - description = module.params['description'] - if description: - if len(description) > 80 or len(description) < 1: - module.fail_json( - msg='Error: The len of description %s is out of [1 - 80].' % description) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["description"] = re_find - if re_find[0] != description: - need_cfg = True - else: - need_cfg = True - - fake_as = module.params['fake_as'] - if fake_as: - if len(fake_as) > 11 or len(fake_as) < 1: - module.fail_json( - msg='Error: The len of fake_as %s is out of [1 - 11].' % fake_as) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["fake_as"] = re_find - if re_find[0] != fake_as: - need_cfg = True - else: - need_cfg = True - - dual_as = module.params['dual_as'] - if dual_as != 'no_use': - if not fake_as: - module.fail_json(msg='fake_as must exist.') - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["dual_as"] = re_find - if re_find[0] != dual_as: - need_cfg = True - else: - need_cfg = True - - conventional = module.params['conventional'] - if conventional != 'no_use': - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["conventional"] = re_find - if re_find[0] != conventional: - need_cfg = True - else: - need_cfg = True - - route_refresh = module.params['route_refresh'] - if route_refresh != 'no_use': - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["route_refresh"] = re_find - if re_find[0] != route_refresh: - need_cfg = True - else: - need_cfg = True - - four_byte_as = module.params['four_byte_as'] - if four_byte_as != 'no_use': - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["four_byte_as"] = re_find - if re_find[0] != four_byte_as: - need_cfg = True - else: - need_cfg = True - - is_ignore = module.params['is_ignore'] - if is_ignore != 'no_use': - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_ignore"] = re_find - if re_find[0] != is_ignore: - need_cfg = True - else: - need_cfg = True - - local_if_name = module.params['local_if_name'] - if local_if_name: - if len(local_if_name) > 63 or len(local_if_name) < 1: - module.fail_json( - msg='Error: The len of local_if_name %s is out of [1 - 63].' % local_if_name) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["local_if_name"] = re_find - if re_find[0].lower() != local_if_name.lower(): - need_cfg = True - else: - need_cfg = True - - ebgp_max_hop = module.params['ebgp_max_hop'] - if ebgp_max_hop: - if int(ebgp_max_hop) > 255 or int(ebgp_max_hop) < 1: - module.fail_json( - msg='Error: The value of ebgp_max_hop %s is out of [1 - 255].' % ebgp_max_hop) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ebgp_max_hop"] = re_find - if re_find[0] != ebgp_max_hop: - need_cfg = True - else: - need_cfg = True - - valid_ttl_hops = module.params['valid_ttl_hops'] - if valid_ttl_hops: - if int(valid_ttl_hops) > 255 or int(valid_ttl_hops) < 1: - module.fail_json( - msg='Error: The value of valid_ttl_hops %s is out of [1 - 255].' % valid_ttl_hops) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["valid_ttl_hops"] = re_find - if re_find[0] != valid_ttl_hops: - need_cfg = True - else: - need_cfg = True - - connect_mode = module.params['connect_mode'] - if connect_mode: - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["connect_mode"] = re_find - if re_find[0] != connect_mode: - need_cfg = True - else: - need_cfg = True - - is_log_change = module.params['is_log_change'] - if is_log_change != 'no_use': - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_log_change"] = re_find - if re_find[0] != is_log_change: - need_cfg = True - else: - need_cfg = True - - pswd_type = module.params['pswd_type'] - if pswd_type: - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["pswd_type"] = re_find - if re_find[0] != pswd_type: - need_cfg = True - else: - need_cfg = True - - pswd_cipher_text = module.params['pswd_cipher_text'] - if pswd_cipher_text: - if len(pswd_cipher_text) > 255 or len(pswd_cipher_text) < 1: - module.fail_json( - msg='Error: The len of pswd_cipher_text %s is out of [1 - 255].' % pswd_cipher_text) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["pswd_cipher_text"] = re_find - if re_find[0] != pswd_cipher_text: - need_cfg = True - else: - need_cfg = True - - keep_alive_time = module.params['keep_alive_time'] - if keep_alive_time: - if int(keep_alive_time) > 21845 or len(keep_alive_time) < 0: - module.fail_json( - msg='Error: The len of keep_alive_time %s is out of [0 - 21845].' % keep_alive_time) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["keep_alive_time"] = re_find - if re_find[0] != keep_alive_time: - need_cfg = True - else: - need_cfg = True - - hold_time = module.params['hold_time'] - if hold_time: - if int(hold_time) != 0 and (int(hold_time) > 65535 or int(hold_time) < 3): - module.fail_json( - msg='Error: The value of hold_time %s is out of [0 or 3 - 65535].' % hold_time) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["hold_time"] = re_find - if re_find[0] != hold_time: - need_cfg = True - else: - need_cfg = True - - min_hold_time = module.params['min_hold_time'] - if min_hold_time: - if int(min_hold_time) != 0 and (int(min_hold_time) > 65535 or int(min_hold_time) < 20): - module.fail_json( - msg='Error: The value of min_hold_time %s is out of [0 or 20 - 65535].' % min_hold_time) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["min_hold_time"] = re_find - if re_find[0] != min_hold_time: - need_cfg = True - else: - need_cfg = True - - key_chain_name = module.params['key_chain_name'] - if key_chain_name: - if len(key_chain_name) > 47 or len(key_chain_name) < 1: - module.fail_json( - msg='Error: The len of key_chain_name %s is out of [1 - 47].' % key_chain_name) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["key_chain_name"] = re_find - if re_find[0] != key_chain_name: - need_cfg = True - else: - need_cfg = True - - conn_retry_time = module.params['conn_retry_time'] - if conn_retry_time: - if int(conn_retry_time) > 65535 or int(conn_retry_time) < 1: - module.fail_json( - msg='Error: The value of conn_retry_time %s is out of [1 - 65535].' % conn_retry_time) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["conn_retry_time"] = re_find - if re_find[0] != conn_retry_time: - need_cfg = True - else: - need_cfg = True - - tcp_mss = module.params['tcp_MSS'] - if tcp_mss: - if int(tcp_mss) > 4096 or int(tcp_mss) < 176: - module.fail_json( - msg='Error: The value of tcp_mss %s is out of [176 - 4096].' % tcp_mss) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["tcp_MSS"] = re_find - if re_find[0] != tcp_mss: - need_cfg = True - else: - need_cfg = True - - mpls_local_ifnet_disable = module.params['mpls_local_ifnet_disable'] - if mpls_local_ifnet_disable != 'no_use': - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["mpls_local_ifnet_disable"] = re_find - if re_find[0] != mpls_local_ifnet_disable: - need_cfg = True - else: - need_cfg = True - - prepend_global_as = module.params['prepend_global_as'] - if prepend_global_as != 'no_use': - if not fake_as: - module.fail_json(msg='fake_as must exist.') - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["prepend_global_as"] = re_find - if re_find[0] != prepend_global_as: - need_cfg = True - else: - need_cfg = True - - prepend_fake_as = module.params['prepend_fake_as'] - if prepend_fake_as != 'no_use': - if not fake_as: - module.fail_json(msg='fake_as must exist.') - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["prepend_fake_as"] = re_find - if re_find[0] != prepend_fake_as: - need_cfg = True - else: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def check_peer_bfd_merge_args(self, **kwargs): - """ check_peer_bfd_merge_args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - - state = module.params['state'] - if state == "absent": - result["need_cfg"] = need_cfg - return result - - vrf_name = module.params['vrf_name'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='Error: The len of vrf_name %s is out of [1 - 31].' % vrf_name) - - peer_addr = module.params['peer_addr'] - - is_bfd_block = module.params['is_bfd_block'] - if is_bfd_block != 'no_use': - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_bfd_block"] = re_find - if re_find[0] != is_bfd_block: - need_cfg = True - else: - need_cfg = True - - multiplier = module.params['multiplier'] - if multiplier: - if int(multiplier) > 50 or int(multiplier) < 3: - module.fail_json( - msg='Error: The value of multiplier %s is out of [3 - 50].' % multiplier) - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["multiplier"] = re_find - if re_find[0] != multiplier: - need_cfg = True - else: - need_cfg = True - - is_bfd_enable = module.params['is_bfd_enable'] - if is_bfd_enable != 'no_use': - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_bfd_enable"] = re_find - if re_find[0] != is_bfd_enable: - need_cfg = True - else: - need_cfg = True - - rx_interval = module.params['rx_interval'] - if rx_interval: - if int(rx_interval) > 1000 or int(rx_interval) < 50: - module.fail_json( - msg='Error: The value of rx_interval %s is out of [50 - 1000].' % rx_interval) - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["rx_interval"] = re_find - if re_find[0] != rx_interval: - need_cfg = True - else: - need_cfg = True - - tx_interval = module.params['tx_interval'] - if tx_interval: - if int(tx_interval) > 1000 or int(tx_interval) < 50: - module.fail_json( - msg='Error: The value of tx_interval %s is out of [50 - 1000].' % tx_interval) - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["tx_interval"] = re_find - if re_find[0] != tx_interval: - need_cfg = True - else: - need_cfg = True - - is_single_hop = module.params['is_single_hop'] - if is_single_hop != 'no_use': - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_single_hop"] = re_find - if re_find[0] != is_single_hop: - need_cfg = True - else: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def check_peer_bfd_delete_args(self, **kwargs): - """ check_peer_bfd_delete_args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - - state = module.params['state'] - if state == "present": - result["need_cfg"] = need_cfg - return result - - vrf_name = module.params['vrf_name'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='Error: The len of vrf_name %s is out of [1 - 31].' % vrf_name) - - peer_addr = module.params['peer_addr'] - - is_bfd_block = module.params['is_bfd_block'] - if is_bfd_block != 'no_use': - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_bfd_block"] = re_find - if re_find[0] == is_bfd_block: - need_cfg = True - - multiplier = module.params['multiplier'] - if multiplier: - if int(multiplier) > 50 or int(multiplier) < 3: - module.fail_json( - msg='Error: The value of multiplier %s is out of [3 - 50].' % multiplier) - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["multiplier"] = re_find - if re_find[0] == multiplier: - need_cfg = True - - is_bfd_enable = module.params['is_bfd_enable'] - if is_bfd_enable != 'no_use': - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_bfd_enable"] = re_find - if re_find[0] == is_bfd_enable: - need_cfg = True - - rx_interval = module.params['rx_interval'] - if rx_interval: - if int(rx_interval) > 1000 or int(rx_interval) < 50: - module.fail_json( - msg='Error: The value of rx_interval %s is out of [50 - 1000].' % rx_interval) - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["rx_interval"] = re_find - if re_find[0] == rx_interval: - need_cfg = True - - tx_interval = module.params['tx_interval'] - if tx_interval: - if int(tx_interval) > 1000 or int(tx_interval) < 50: - module.fail_json( - msg='Error: The value of tx_interval %s is out of [50 - 1000].' % tx_interval) - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["tx_interval"] = re_find - if re_find[0] == tx_interval: - need_cfg = True - - is_single_hop = module.params['is_single_hop'] - if is_single_hop != 'no_use': - - conf_str = CE_GET_PEER_BFD_HEADER % ( - vrf_name, peer_addr) + "" + CE_GET_PEER_BFD_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_single_hop"] = re_find - if re_find[0] == is_single_hop: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def get_bgp_peer(self, **kwargs): - """ get_bgp_peer """ - - module = kwargs["module"] - peerip = module.params['peer_addr'] - vrf_name = module.params['vrf_name'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='Error: The len of vrf_name %s is out of [1 - 31].' % vrf_name) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + \ - "" + CE_GET_BGP_PEER_TAIL - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*\s.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def get_bgp_del_peer(self, **kwargs): - """ get_bgp_del_peer """ - - module = kwargs["module"] - peerip = module.params['peer_addr'] - vrf_name = module.params['vrf_name'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='Error: The len of vrf_name %s is out of [1 - 31].' % vrf_name) - - conf_str = CE_GET_BGP_PEER_HEADER % (vrf_name, peerip) + CE_GET_BGP_PEER_TAIL - - xml_str = self.netconf_get_config(module=module, conf_str=conf_str) - - result = list() - - if "" in xml_str: - return result - else: - re_find = re.findall( - r'.*(.*).*', xml_str) - - if re_find: - return re_find - else: - return result - - def merge_bgp_peer(self, **kwargs): - """ merge_bgp_peer """ - - module = kwargs["module"] - vrf_name = module.params['vrf_name'] - peer_addr = module.params['peer_addr'] - remote_as = module.params['remote_as'] - - conf_str = CE_MERGE_BGP_PEER_HEADER % ( - vrf_name, peer_addr) + "%s" % remote_as + CE_MERGE_BGP_PEER_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp peer failed.') - - cmds = [] - cmd = "peer %s as-number %s" % (peer_addr, remote_as) - cmds.append(cmd) - - return cmds - - def create_bgp_peer(self, **kwargs): - """ create_bgp_peer """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - - peer_addr = module.params['peer_addr'] - remote_as = module.params['remote_as'] - - conf_str = CE_CREATE_BGP_PEER_HEADER % ( - vrf_name, peer_addr) + "%s" % remote_as + CE_CREATE_BGP_PEER_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create bgp peer failed.') - - cmds = [] - cmd = "peer %s as-number %s" % (peer_addr, remote_as) - cmds.append(cmd) - - return cmds - - def delete_bgp_peer(self, **kwargs): - """ delete_bgp_peer """ - - module = kwargs["module"] - vrf_name = module.params['vrf_name'] - peer_addr = module.params['peer_addr'] - - conf_str = CE_DELETE_BGP_PEER_HEADER % ( - vrf_name, peer_addr) + CE_DELETE_BGP_PEER_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete bgp peer failed.') - - cmds = [] - cmd = "undo peer %s" % peer_addr - cmds.append(cmd) - - return cmds - - def merge_bgp_peer_other(self, **kwargs): - """ merge_bgp_peer """ - - module = kwargs["module"] - vrf_name = module.params['vrf_name'] - peer_addr = module.params['peer_addr'] - - conf_str = CE_MERGE_BGP_PEER_HEADER % (vrf_name, peer_addr) - - cmds = [] - - description = module.params['description'] - if description: - conf_str += "%s" % description - - cmd = "peer %s description %s" % (peer_addr, description) - cmds.append(cmd) - - fake_as = module.params['fake_as'] - if fake_as: - conf_str += "%s" % fake_as - - cmd = "peer %s local-as %s" % (peer_addr, fake_as) - cmds.append(cmd) - - dual_as = module.params['dual_as'] - if dual_as != 'no_use': - conf_str += "%s" % dual_as - - if dual_as == "true": - cmd = "peer %s local-as %s dual-as" % (peer_addr, fake_as) - else: - cmd = "peer %s local-as %s" % (peer_addr, fake_as) - cmds.append(cmd) - - conventional = module.params['conventional'] - if conventional != 'no_use': - conf_str += "%s" % conventional - if conventional == "true": - cmd = "peer %s capability-advertise conventional" % peer_addr - else: - cmd = "undo peer %s capability-advertise conventional" % peer_addr - cmds.append(cmd) - - route_refresh = module.params['route_refresh'] - if route_refresh != 'no_use': - conf_str += "%s" % route_refresh - - if route_refresh == "true": - cmd = "peer %s capability-advertise route-refresh" % peer_addr - else: - cmd = "undo peer %s capability-advertise route-refresh" % peer_addr - cmds.append(cmd) - - four_byte_as = module.params['four_byte_as'] - if four_byte_as != 'no_use': - conf_str += "%s" % four_byte_as - - if four_byte_as == "true": - cmd = "peer %s capability-advertise 4-byte-as" % peer_addr - else: - cmd = "undo peer %s capability-advertise 4-byte-as" % peer_addr - cmds.append(cmd) - - is_ignore = module.params['is_ignore'] - if is_ignore != 'no_use': - conf_str += "%s" % is_ignore - - if is_ignore == "true": - cmd = "peer %s ignore" % peer_addr - else: - cmd = "undo peer %s ignore" % peer_addr - cmds.append(cmd) - - local_if_name = module.params['local_if_name'] - if local_if_name: - conf_str += "%s" % local_if_name - - cmd = "peer %s connect-interface %s" % (peer_addr, local_if_name) - cmds.append(cmd) - - ebgp_max_hop = module.params['ebgp_max_hop'] - if ebgp_max_hop: - conf_str += "%s" % ebgp_max_hop - - cmd = "peer %s ebgp-max-hop %s" % (peer_addr, ebgp_max_hop) - cmds.append(cmd) - - valid_ttl_hops = module.params['valid_ttl_hops'] - if valid_ttl_hops: - conf_str += "%s" % valid_ttl_hops - - cmd = "peer %s valid-ttl-hops %s" % (peer_addr, valid_ttl_hops) - cmds.append(cmd) - - connect_mode = module.params['connect_mode'] - if connect_mode: - - if connect_mode == "listenOnly": - cmd = "peer %s listen-only" % peer_addr - cmds.append(cmd) - elif connect_mode == "connectOnly": - cmd = "peer %s connect-only" % peer_addr - cmds.append(cmd) - elif connect_mode == "both": - connect_mode = "null" - cmd = "peer %s listen-only" % peer_addr - cmds.append(cmd) - cmd = "peer %s connect-only" % peer_addr - cmds.append(cmd) - conf_str += "%s" % connect_mode - - is_log_change = module.params['is_log_change'] - if is_log_change != 'no_use': - conf_str += "%s" % is_log_change - - if is_log_change == "true": - cmd = "peer %s log-change" % peer_addr - else: - cmd = "undo peer %s log-change" % peer_addr - cmds.append(cmd) - - pswd_type = module.params['pswd_type'] - if pswd_type: - conf_str += "%s" % pswd_type - - pswd_cipher_text = module.params['pswd_cipher_text'] - if pswd_cipher_text: - conf_str += "%s" % pswd_cipher_text - - if pswd_type == "cipher": - cmd = "peer %s password cipher %s" % ( - peer_addr, pswd_cipher_text) - elif pswd_type == "simple": - cmd = "peer %s password simple %s" % ( - peer_addr, pswd_cipher_text) - cmds.append(cmd) - - keep_alive_time = module.params['keep_alive_time'] - if keep_alive_time: - conf_str += "%s" % keep_alive_time - - cmd = "peer %s timer keepalive %s" % (peer_addr, keep_alive_time) - cmds.append(cmd) - - hold_time = module.params['hold_time'] - if hold_time: - conf_str += "%s" % hold_time - - cmd = "peer %s timer hold %s" % (peer_addr, hold_time) - cmds.append(cmd) - - min_hold_time = module.params['min_hold_time'] - if min_hold_time: - conf_str += "%s" % min_hold_time - - cmd = "peer %s timer min-holdtime %s" % (peer_addr, min_hold_time) - cmds.append(cmd) - - key_chain_name = module.params['key_chain_name'] - if key_chain_name: - conf_str += "%s" % key_chain_name - - cmd = "peer %s keychain %s" % (peer_addr, key_chain_name) - cmds.append(cmd) - - conn_retry_time = module.params['conn_retry_time'] - if conn_retry_time: - conf_str += "%s" % conn_retry_time - - cmd = "peer %s timer connect-retry %s" % ( - peer_addr, conn_retry_time) - cmds.append(cmd) - - tcp_mss = module.params['tcp_MSS'] - if tcp_mss: - conf_str += "%s" % tcp_mss - - cmd = "peer %s tcp-mss %s" % (peer_addr, tcp_mss) - cmds.append(cmd) - - mpls_local_ifnet_disable = module.params['mpls_local_ifnet_disable'] - if mpls_local_ifnet_disable != 'no_use': - conf_str += "%s" % mpls_local_ifnet_disable - - if mpls_local_ifnet_disable == "false": - cmd = "undo peer %s mpls-local-ifnet disable" % peer_addr - else: - cmd = "peer %s mpls-local-ifnet disable" % peer_addr - cmds.append(cmd) - - prepend_global_as = module.params['prepend_global_as'] - if prepend_global_as != 'no_use': - conf_str += "%s" % prepend_global_as - - if prepend_global_as == "true": - cmd = "peer %s local-as %s prepend-global-as" % (peer_addr, fake_as) - else: - cmd = "undo peer %s local-as %s prepend-global-as" % (peer_addr, fake_as) - cmds.append(cmd) - - prepend_fake_as = module.params['prepend_fake_as'] - if prepend_fake_as != 'no_use': - conf_str += "%s" % prepend_fake_as - - if prepend_fake_as == "true": - cmd = "peer %s local-as %s prepend-local-as" % (peer_addr, fake_as) - else: - cmd = "undo peer %s local-as %s prepend-local-as" % (peer_addr, fake_as) - cmds.append(cmd) - - conf_str += CE_MERGE_BGP_PEER_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp peer other failed.') - - return cmds - - def merge_peer_bfd(self, **kwargs): - """ merge_peer_bfd """ - - module = kwargs["module"] - vrf_name = module.params['vrf_name'] - peer_addr = module.params['peer_addr'] - - conf_str = CE_MERGE_PEER_BFD_HEADER % (vrf_name, peer_addr) - - cmds = [] - - is_bfd_block = module.params['is_bfd_block'] - if is_bfd_block != 'no_use': - conf_str += "%s" % is_bfd_block - - if is_bfd_block == "true": - cmd = "peer %s bfd block" % peer_addr - else: - cmd = "undo peer %s bfd block" % peer_addr - cmds.append(cmd) - - multiplier = module.params['multiplier'] - if multiplier: - conf_str += "%s" % multiplier - - cmd = "peer %s bfd detect-multiplier %s" % (peer_addr, multiplier) - cmds.append(cmd) - - is_bfd_enable = module.params['is_bfd_enable'] - if is_bfd_enable != 'no_use': - conf_str += "%s" % is_bfd_enable - - if is_bfd_enable == "true": - cmd = "peer %s bfd enable" % peer_addr - else: - cmd = "undo peer %s bfd enable" % peer_addr - cmds.append(cmd) - - rx_interval = module.params['rx_interval'] - if rx_interval: - conf_str += "%s" % rx_interval - - cmd = "peer %s bfd min-rx-interval %s" % (peer_addr, rx_interval) - cmds.append(cmd) - - tx_interval = module.params['tx_interval'] - if tx_interval: - conf_str += "%s" % tx_interval - - cmd = "peer %s bfd min-tx-interval %s" % (peer_addr, tx_interval) - cmds.append(cmd) - - is_single_hop = module.params['is_single_hop'] - if is_single_hop != 'no_use': - conf_str += "%s" % is_single_hop - - if is_single_hop == "true": - cmd = "peer %s bfd enable single-hop-prefer" % peer_addr - else: - cmd = "undo peer %s bfd enable single-hop-prefer" % peer_addr - cmds.append(cmd) - - conf_str += CE_MERGE_PEER_BFD_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge peer bfd failed.') - - return cmds - - def delete_peer_bfd(self, **kwargs): - """ delete_peer_bfd """ - - module = kwargs["module"] - vrf_name = module.params['vrf_name'] - peer_addr = module.params['peer_addr'] - - conf_str = CE_DELETE_PEER_BFD_HEADER % (vrf_name, peer_addr) - - cmds = [] - - is_bfd_block = module.params['is_bfd_block'] - if is_bfd_block != 'no_use': - conf_str += "%s" % is_bfd_block - - cmd = "undo peer %s bfd block" % peer_addr - cmds.append(cmd) - - multiplier = module.params['multiplier'] - if multiplier: - conf_str += "%s" % multiplier - - cmd = "undo peer %s bfd detect-multiplier %s" % ( - peer_addr, multiplier) - cmds.append(cmd) - - is_bfd_enable = module.params['is_bfd_enable'] - if is_bfd_enable != 'no_use': - conf_str += "%s" % is_bfd_enable - - cmd = "undo peer %s bfd enable" % peer_addr - cmds.append(cmd) - - rx_interval = module.params['rx_interval'] - if rx_interval: - conf_str += "%s" % rx_interval - - cmd = "undo peer %s bfd min-rx-interval %s" % ( - peer_addr, rx_interval) - cmds.append(cmd) - - tx_interval = module.params['tx_interval'] - if tx_interval: - conf_str += "%s" % tx_interval - - cmd = "undo peer %s bfd min-tx-interval %s" % ( - peer_addr, tx_interval) - cmds.append(cmd) - - is_single_hop = module.params['is_single_hop'] - if is_single_hop != 'no_use': - conf_str += "%s" % is_single_hop - - cmd = "undo peer %s bfd enable single-hop-prefer" % peer_addr - cmds.append(cmd) - - conf_str += CE_DELETE_PEER_BFD_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete peer bfd failed.') - - return cmds - - -def main(): - """ main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - vrf_name=dict(type='str', required=True), - peer_addr=dict(type='str', required=True), - remote_as=dict(type='str', required=True), - description=dict(type='str'), - fake_as=dict(type='str'), - dual_as=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - conventional=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - route_refresh=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - four_byte_as=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - is_ignore=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - local_if_name=dict(type='str'), - ebgp_max_hop=dict(type='str'), - valid_ttl_hops=dict(type='str'), - connect_mode=dict(choices=['listenOnly', 'connectOnly', 'both']), - is_log_change=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - pswd_type=dict(choices=['null', 'cipher', 'simple']), - pswd_cipher_text=dict(type='str', no_log=True), - keep_alive_time=dict(type='str'), - hold_time=dict(type='str'), - min_hold_time=dict(type='str'), - key_chain_name=dict(type='str'), - conn_retry_time=dict(type='str'), - tcp_MSS=dict(type='str'), - mpls_local_ifnet_disable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - prepend_global_as=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - prepend_fake_as=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - is_bfd_block=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - multiplier=dict(type='str'), - is_bfd_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - rx_interval=dict(type='str'), - tx_interval=dict(type='str'), - is_single_hop=dict(type='str', default='no_use', choices=['no_use', 'true', 'false'])) - - argument_spec.update(ce_argument_spec) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - - changed = False - proposed = dict() - existing = dict() - end_state = dict() - updates = [] - - state = module.params['state'] - vrf_name = module.params['vrf_name'] - peer_addr = module.params['peer_addr'] - remote_as = module.params['remote_as'] - description = module.params['description'] - fake_as = module.params['fake_as'] - dual_as = module.params['dual_as'] - conventional = module.params['conventional'] - route_refresh = module.params['route_refresh'] - four_byte_as = module.params['four_byte_as'] - is_ignore = module.params['is_ignore'] - local_if_name = module.params['local_if_name'] - ebgp_max_hop = module.params['ebgp_max_hop'] - valid_ttl_hops = module.params['valid_ttl_hops'] - connect_mode = module.params['connect_mode'] - is_log_change = module.params['is_log_change'] - pswd_type = module.params['pswd_type'] - pswd_cipher_text = module.params['pswd_cipher_text'] - keep_alive_time = module.params['keep_alive_time'] - hold_time = module.params['hold_time'] - min_hold_time = module.params['min_hold_time'] - key_chain_name = module.params['key_chain_name'] - conn_retry_time = module.params['conn_retry_time'] - tcp_mss = module.params['tcp_MSS'] - mpls_local_ifnet_disable = module.params['mpls_local_ifnet_disable'] - prepend_global_as = module.params['prepend_global_as'] - prepend_fake_as = module.params['prepend_fake_as'] - is_bfd_block = module.params['is_bfd_block'] - multiplier = module.params['multiplier'] - is_bfd_enable = module.params['is_bfd_enable'] - rx_interval = module.params['rx_interval'] - tx_interval = module.params['tx_interval'] - is_single_hop = module.params['is_single_hop'] - - ce_bgp_peer_obj = BgpNeighbor() - - # get proposed - proposed["state"] = state - if vrf_name: - proposed["vrf_name"] = vrf_name - if peer_addr: - proposed["peer_addr"] = peer_addr - if remote_as: - proposed["remote_as"] = remote_as - if description: - proposed["description"] = description - if fake_as: - proposed["fake_as"] = fake_as - if dual_as != 'no_use': - proposed["dual_as"] = dual_as - if conventional != 'no_use': - proposed["conventional"] = conventional - if route_refresh != 'no_use': - proposed["route_refresh"] = route_refresh - if four_byte_as != 'no_use': - proposed["four_byte_as"] = four_byte_as - if is_ignore != 'no_use': - proposed["is_ignore"] = is_ignore - if local_if_name: - proposed["local_if_name"] = local_if_name - if ebgp_max_hop: - proposed["ebgp_max_hop"] = ebgp_max_hop - if valid_ttl_hops: - proposed["valid_ttl_hops"] = valid_ttl_hops - if connect_mode: - proposed["connect_mode"] = connect_mode - if is_log_change != 'no_use': - proposed["is_log_change"] = is_log_change - if pswd_type: - proposed["pswd_type"] = pswd_type - if pswd_cipher_text: - proposed["pswd_cipher_text"] = pswd_cipher_text - if keep_alive_time: - proposed["keep_alive_time"] = keep_alive_time - if hold_time: - proposed["hold_time"] = hold_time - if min_hold_time: - proposed["min_hold_time"] = min_hold_time - if key_chain_name: - proposed["key_chain_name"] = key_chain_name - if conn_retry_time: - proposed["conn_retry_time"] = conn_retry_time - if tcp_mss: - proposed["tcp_MSS"] = tcp_mss - if mpls_local_ifnet_disable != 'no_use': - proposed["mpls_local_ifnet_disable"] = mpls_local_ifnet_disable - if prepend_global_as != 'no_use': - proposed["prepend_global_as"] = prepend_global_as - if prepend_fake_as != 'no_use': - proposed["prepend_fake_as"] = prepend_fake_as - if is_bfd_block != 'no_use': - proposed["is_bfd_block"] = is_bfd_block - if multiplier: - proposed["multiplier"] = multiplier - if is_bfd_enable != 'no_use': - proposed["is_bfd_enable"] = is_bfd_enable - if rx_interval: - proposed["rx_interval"] = rx_interval - if tx_interval: - proposed["tx_interval"] = tx_interval - if is_single_hop != 'no_use': - proposed["is_single_hop"] = is_single_hop - - if not ce_bgp_peer_obj: - module.fail_json(msg='Error: Init module failed.') - - need_bgp_peer_enable = ce_bgp_peer_obj.check_bgp_peer_args(module=module) - need_bgp_peer_other_rst = ce_bgp_peer_obj.check_bgp_peer_other_args( - module=module) - need_peer_bfd_merge_rst = ce_bgp_peer_obj.check_peer_bfd_merge_args( - module=module) - need_peer_bfd_del_rst = ce_bgp_peer_obj.check_peer_bfd_delete_args( - module=module) - - # bgp peer config - if need_bgp_peer_enable["need_cfg"]: - - if state == "present": - - if remote_as: - - bgp_peer_exist = ce_bgp_peer_obj.get_bgp_peer(module=module) - existing["bgp peer"] = bgp_peer_exist - - bgp_peer_new = (peer_addr, remote_as) - if len(bgp_peer_exist) == 0: - cmd = ce_bgp_peer_obj.create_bgp_peer(module=module) - changed = True - for item in cmd: - updates.append(item) - - elif bgp_peer_new in bgp_peer_exist: - pass - - else: - cmd = ce_bgp_peer_obj.merge_bgp_peer(module=module) - changed = True - for item in cmd: - updates.append(item) - - bgp_peer_end = ce_bgp_peer_obj.get_bgp_peer(module=module) - end_state["bgp peer"] = bgp_peer_end - - else: - - bgp_peer_exist = ce_bgp_peer_obj.get_bgp_del_peer(module=module) - existing["bgp peer"] = bgp_peer_exist - - bgp_peer_new = (peer_addr) - - if len(bgp_peer_exist) == 0: - pass - - elif bgp_peer_new in bgp_peer_exist: - cmd = ce_bgp_peer_obj.delete_bgp_peer(module=module) - changed = True - for item in cmd: - updates.append(item) - - bgp_peer_end = ce_bgp_peer_obj.get_bgp_del_peer(module=module) - end_state["bgp peer"] = bgp_peer_end - - # bgp peer other args - exist_tmp = dict() - for item in need_bgp_peer_other_rst: - if item != "need_cfg": - exist_tmp[item] = need_bgp_peer_other_rst[item] - if exist_tmp: - existing["bgp peer other"] = exist_tmp - - if need_bgp_peer_other_rst["need_cfg"]: - - if state == "present": - cmd = ce_bgp_peer_obj.merge_bgp_peer_other(module=module) - changed = True - for item in cmd: - updates.append(item) - - need_bgp_peer_other_rst = ce_bgp_peer_obj.check_bgp_peer_other_args( - module=module) - end_tmp = dict() - for item in need_bgp_peer_other_rst: - if item != "need_cfg": - end_tmp[item] = need_bgp_peer_other_rst[item] - if end_tmp: - end_state["bgp peer other"] = end_tmp - - # peer bfd args - if state == "present": - exist_tmp = dict() - for item in need_peer_bfd_merge_rst: - if item != "need_cfg": - exist_tmp[item] = need_peer_bfd_merge_rst[item] - if exist_tmp: - existing["peer bfd"] = exist_tmp - - if need_peer_bfd_merge_rst["need_cfg"]: - cmd = ce_bgp_peer_obj.merge_peer_bfd(module=module) - changed = True - for item in cmd: - updates.append(item) - - need_peer_bfd_merge_rst = ce_bgp_peer_obj.check_peer_bfd_merge_args( - module=module) - end_tmp = dict() - for item in need_peer_bfd_merge_rst: - if item != "need_cfg": - end_tmp[item] = need_peer_bfd_merge_rst[item] - if end_tmp: - end_state["peer bfd"] = end_tmp - else: - exist_tmp = dict() - for item in need_peer_bfd_del_rst: - if item != "need_cfg": - exist_tmp[item] = need_peer_bfd_del_rst[item] - if exist_tmp: - existing["peer bfd"] = exist_tmp - - # has already delete with bgp peer - - need_peer_bfd_del_rst = ce_bgp_peer_obj.check_peer_bfd_delete_args( - module=module) - end_tmp = dict() - for item in need_peer_bfd_del_rst: - if item != "need_cfg": - end_tmp[item] = need_peer_bfd_del_rst[item] - if end_tmp: - end_state["peer bfd"] = end_tmp - - results = dict() - results['proposed'] = proposed - results['existing'] = existing - results['changed'] = changed - results['end_state'] = end_state - results['updates'] = updates - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_bgp_neighbor_af.py b/plugins/modules/network/cloudengine/ce_bgp_neighbor_af.py deleted file mode 100644 index cf9339ef82..0000000000 --- a/plugins/modules/network/cloudengine/ce_bgp_neighbor_af.py +++ /dev/null @@ -1,2679 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_bgp_neighbor_af -short_description: Manages BGP neighbor Address-family configuration on HUAWEI CloudEngine switches. -description: - - Manages BGP neighbor Address-family configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - vrf_name: - description: - - Name of a BGP instance. The name is a case-sensitive string of characters. - The BGP instance can be used only after the corresponding VPN instance is created. - required: true - af_type: - description: - - Address family type of a BGP instance. - required: true - choices: ['ipv4uni', 'ipv4multi', 'ipv4vpn', 'ipv6uni', 'ipv6vpn', 'evpn'] - remote_address: - description: - - IPv4 or IPv6 peer connection address. - required: true - advertise_irb: - description: - - If the value is true, advertised IRB routes are distinguished. - If the value is false, advertised IRB routes are not distinguished. - default: no_use - choices: ['no_use','true','false'] - advertise_arp: - description: - - If the value is true, advertised ARP routes are distinguished. - If the value is false, advertised ARP routes are not distinguished. - default: no_use - choices: ['no_use','true','false'] - advertise_remote_nexthop: - description: - - If the value is true, the remote next-hop attribute is advertised to peers. - If the value is false, the remote next-hop attribute is not advertised to any peers. - default: no_use - choices: ['no_use','true','false'] - advertise_community: - description: - - If the value is true, the community attribute is advertised to peers. - If the value is false, the community attribute is not advertised to peers. - default: no_use - choices: ['no_use','true','false'] - advertise_ext_community: - description: - - If the value is true, the extended community attribute is advertised to peers. - If the value is false, the extended community attribute is not advertised to peers. - default: no_use - choices: ['no_use','true','false'] - discard_ext_community: - description: - - If the value is true, the extended community attribute in the peer route information is discarded. - If the value is false, the extended community attribute in the peer route information is not discarded. - default: no_use - choices: ['no_use','true','false'] - allow_as_loop_enable: - description: - - If the value is true, repetitive local AS numbers are allowed. - If the value is false, repetitive local AS numbers are not allowed. - default: no_use - choices: ['no_use','true','false'] - allow_as_loop_limit: - description: - - Set the maximum number of repetitive local AS number. - The value is an integer ranging from 1 to 10. - keep_all_routes: - description: - - If the value is true, the system stores all route update messages received from all peers (groups) - after BGP connection setup. - If the value is false, the system stores only BGP update messages that are received from peers - and pass the configured import policy. - default: no_use - choices: ['no_use','true','false'] - nexthop_configure: - description: - - null, The next hop is not changed. - local, The next hop is changed to the local IP address. - invariable, Prevent the device from changing the next hop of each imported IGP route - when advertising it to its BGP peers. - choices: ['null', 'local', 'invariable'] - preferred_value: - description: - - Assign a preferred value for the routes learned from a specified peer. - The value is an integer ranging from 0 to 65535. - public_as_only: - description: - - If the value is true, sent BGP update messages carry only the public AS number but do not carry - private AS numbers. - If the value is false, sent BGP update messages can carry private AS numbers. - default: no_use - choices: ['no_use','true','false'] - public_as_only_force: - description: - - If the value is true, sent BGP update messages carry only the public AS number but do not carry - private AS numbers. - If the value is false, sent BGP update messages can carry private AS numbers. - default: no_use - choices: ['no_use','true','false'] - public_as_only_limited: - description: - - Limited use public as number. - default: no_use - choices: ['no_use','true','false'] - public_as_only_replace: - description: - - Private as replaced by public as number. - default: no_use - choices: ['no_use','true','false'] - public_as_only_skip_peer_as: - description: - - Public as only skip peer as. - default: no_use - choices: ['no_use','true','false'] - route_limit: - description: - - Configure the maximum number of routes that can be accepted from a peer. - The value is an integer ranging from 1 to 4294967295. - route_limit_percent: - description: - - Specify the percentage of routes when a router starts to generate an alarm. - The value is an integer ranging from 1 to 100. - route_limit_type: - description: - - Noparameter, After the number of received routes exceeds the threshold and the timeout - timer expires,no action. - AlertOnly, An alarm is generated and no additional routes will be accepted if the maximum - number of routes allowed have been received. - IdleForever, The connection that is interrupted is not automatically re-established if the - maximum number of routes allowed have been received. - IdleTimeout, After the number of received routes exceeds the threshold and the timeout timer - expires, the connection that is interrupted is automatically re-established. - choices: ['noparameter', 'alertOnly', 'idleForever', 'idleTimeout'] - route_limit_idle_timeout: - description: - - Specify the value of the idle-timeout timer to automatically reestablish the connections after - they are cut off when the number of routes exceeds the set threshold. - The value is an integer ranging from 1 to 1200. - rt_updt_interval: - description: - - Specify the minimum interval at which Update packets are sent. The value is an integer, in seconds. - The value is an integer ranging from 0 to 600. - redirect_ip: - description: - - Redirect ip. - default: no_use - choices: ['no_use','true','false'] - redirect_ip_validation: - description: - - Redirect ip validation. - default: no_use - choices: ['no_use','true','false'] - aliases: ['redirect_ip_vaildation'] - reflect_client: - description: - - If the value is true, the local device functions as the route reflector and a peer functions - as a client of the route reflector. - If the value is false, the route reflector and client functions are not configured. - default: no_use - choices: ['no_use','true','false'] - substitute_as_enable: - description: - - If the value is true, the function to replace a specified peer's AS number in the AS-Path attribute with - the local AS number is enabled. - If the value is false, the function to replace a specified peer's AS number in the AS-Path attribute with - the local AS number is disabled. - default: no_use - choices: ['no_use','true','false'] - import_rt_policy_name: - description: - - Specify the filtering policy applied to the routes learned from a peer. - The value is a string of 1 to 40 characters. - export_rt_policy_name: - description: - - Specify the filtering policy applied to the routes to be advertised to a peer. - The value is a string of 1 to 40 characters. - import_pref_filt_name: - description: - - Specify the IPv4 filtering policy applied to the routes received from a specified peer. - The value is a string of 1 to 169 characters. - export_pref_filt_name: - description: - - Specify the IPv4 filtering policy applied to the routes to be advertised to a specified peer. - The value is a string of 1 to 169 characters. - import_as_path_filter: - description: - - Apply an AS_Path-based filtering policy to the routes received from a specified peer. - The value is an integer ranging from 1 to 256. - export_as_path_filter: - description: - - Apply an AS_Path-based filtering policy to the routes to be advertised to a specified peer. - The value is an integer ranging from 1 to 256. - import_as_path_name_or_num: - description: - - A routing strategy based on the AS path list for routing received by a designated peer. - export_as_path_name_or_num: - description: - - Application of a AS path list based filtering policy to the routing of a specified peer. - import_acl_name_or_num: - description: - - Apply an IPv4 ACL-based filtering policy to the routes received from a specified peer. - The value is a string of 1 to 32 characters. - export_acl_name_or_num: - description: - - Apply an IPv4 ACL-based filtering policy to the routes to be advertised to a specified peer. - The value is a string of 1 to 32 characters. - ipprefix_orf_enable: - description: - - If the value is true, the address prefix-based Outbound Route Filter (ORF) capability is - enabled for peers. - If the value is false, the address prefix-based Outbound Route Filter (ORF) capability is - disabled for peers. - default: no_use - choices: ['no_use','true','false'] - is_nonstd_ipprefix_mod: - description: - - If the value is true, Non-standard capability codes are used during capability negotiation. - If the value is false, RFC-defined standard ORF capability codes are used during capability negotiation. - default: no_use - choices: ['no_use','true','false'] - orftype: - description: - - ORF Type. - The value is an integer ranging from 0 to 65535. - orf_mode: - description: - - ORF mode. - null, Default value. - receive, ORF for incoming packets. - send, ORF for outgoing packets. - both, ORF for incoming and outgoing packets. - choices: ['null', 'receive', 'send', 'both'] - soostring: - description: - - Configure the Site-of-Origin (SoO) extended community attribute. - The value is a string of 3 to 21 characters. - default_rt_adv_enable: - description: - - If the value is true, the function to advertise default routes to peers is enabled. - If the value is false, the function to advertise default routes to peers is disabled. - default: no_use - choices: ['no_use','true', 'false'] - default_rt_adv_policy: - description: - - Specify the name of a used policy. The value is a string. - The value is a string of 1 to 40 characters. - default_rt_match_mode: - description: - - null, Null. - matchall, Advertise the default route if all matching conditions are met. - matchany, Advertise the default route if any matching condition is met. - choices: ['null', 'matchall', 'matchany'] - add_path_mode: - description: - - null, Null. - receive, Support receiving Add-Path routes. - send, Support sending Add-Path routes. - both, Support receiving and sending Add-Path routes. - choices: ['null', 'receive', 'send', 'both'] - adv_add_path_num: - description: - - The number of addPath advertise route. - The value is an integer ranging from 2 to 64. - origin_as_valid: - description: - - If the value is true, Application results of route announcement. - If the value is false, Routing application results are not notified. - default: no_use - choices: ['no_use','true', 'false'] - vpls_enable: - description: - - If the value is true, vpls enable. - If the value is false, vpls disable. - default: no_use - choices: ['no_use','true', 'false'] - vpls_ad_disable: - description: - - If the value is true, enable vpls-ad. - If the value is false, disable vpls-ad. - default: no_use - choices: ['no_use','true', 'false'] - update_pkt_standard_compatible: - description: - - If the value is true, When the vpnv4 multicast neighbor receives and updates the message, - the message has no label. - If the value is false, When the vpnv4 multicast neighbor receives and updates the message, - the message has label. - default: no_use - choices: ['no_use','true', 'false'] -''' - -EXAMPLES = ''' - -- name: CloudEngine BGP neighbor address family test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config BGP peer Address_Family" - ce_bgp_neighbor_af: - state: present - vrf_name: js - af_type: ipv4uni - remote_address: 192.168.10.10 - nexthop_configure: local - provider: "{{ cli }}" - - - name: "Undo BGP peer Address_Family" - ce_bgp_neighbor_af: - state: absent - vrf_name: js - af_type: ipv4uni - remote_address: 192.168.10.10 - nexthop_configure: local - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"af_type": "ipv4uni", "nexthop_configure": "local", - "remote_address": "192.168.10.10", - "state": "present", "vrf_name": "js"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"bgp neighbor af": {"af_type": "ipv4uni", "remote_address": "192.168.10.10", - "vrf_name": "js"}, - "bgp neighbor af other": {"af_type": "ipv4uni", "nexthop_configure": "null", - "vrf_name": "js"}} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"bgp neighbor af": {"af_type": "ipv4uni", "remote_address": "192.168.10.10", - "vrf_name": "js"}, - "bgp neighbor af other": {"af_type": "ipv4uni", "nexthop_configure": "local", - "vrf_name": "js"}} -updates: - description: command sent to the device - returned: always - type: list - sample: ["peer 192.168.10.10 next-hop-local"] -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - -# get bgp peer af -CE_GET_BGP_PEER_AF_HEADER = """ - - - - - - %s - - - %s - - - %s -""" -CE_GET_BGP_PEER_AF_TAIL = """ - - - - - - - - - -""" - -# merge bgp peer af -CE_MERGE_BGP_PEER_AF_HEADER = """ - - - - - - %s - - - %s - - - %s -""" -CE_MERGE_BGP_PEER_AF_TAIL = """ - - - - - - - - - -""" - -# create bgp peer af -CE_CREATE_BGP_PEER_AF = """ - - - - - - %s - - - %s - - - %s - - - - - - - - - -""" - -# delete bgp peer af -CE_DELETE_BGP_PEER_AF = """ - - - - - - %s - - - %s - - - %s - - - - - - - - - -""" - - -class BgpNeighborAf(object): - """ Manages BGP neighbor Address-family configuration """ - - def netconf_get_config(self, **kwargs): - """ netconf_get_config """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = get_nc_config(module, conf_str) - - return xml_str - - def netconf_set_config(self, **kwargs): - """ netconf_set_config """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = set_nc_config(module, conf_str) - - return xml_str - - def check_bgp_neighbor_af_args(self, **kwargs): - """ check_bgp_neighbor_af_args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - - vrf_name = module.params['vrf_name'] - if vrf_name: - if len(vrf_name) > 31 or len(vrf_name) == 0: - module.fail_json( - msg='Error: The len of vrf_name %s is out of [1 - 31].' % vrf_name) - - state = module.params['state'] - af_type = module.params['af_type'] - remote_address = module.params['remote_address'] - - if not check_ip_addr(ipaddr=remote_address): - module.fail_json( - msg='Error: The remote_address %s is invalid.' % remote_address) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if state == "present": - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - if re_find: - result["remote_address"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if remote_address not in re_find: - need_cfg = True - else: - need_cfg = True - else: - if "" in recv_xml: - pass - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["remote_address"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] == remote_address: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def check_bgp_neighbor_af_other(self, **kwargs): - """ check_bgp_neighbor_af_other """ - - module = kwargs["module"] - result = dict() - need_cfg = False - - state = module.params['state'] - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - remote_address = module.params['remote_address'] - - if state == "absent": - result["need_cfg"] = need_cfg - return result - - advertise_irb = module.params['advertise_irb'] - if advertise_irb != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall(r'.*%s\s*' - r'(.*).*' % remote_address, recv_xml) - if re_find: - result["advertise_irb"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != advertise_irb: - need_cfg = True - else: - need_cfg = True - - advertise_arp = module.params['advertise_arp'] - if advertise_arp != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall(r'.*%s\s*' - r'.*(.*).*' % remote_address, recv_xml) - - if re_find: - result["advertise_arp"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != advertise_arp: - need_cfg = True - else: - need_cfg = True - - advertise_remote_nexthop = module.params['advertise_remote_nexthop'] - if advertise_remote_nexthop != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["advertise_remote_nexthop"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != advertise_remote_nexthop: - need_cfg = True - else: - need_cfg = True - - advertise_community = module.params['advertise_community'] - if advertise_community != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["advertise_community"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != advertise_community: - need_cfg = True - else: - need_cfg = True - - advertise_ext_community = module.params['advertise_ext_community'] - if advertise_ext_community != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["advertise_ext_community"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != advertise_ext_community: - need_cfg = True - else: - need_cfg = True - - discard_ext_community = module.params['discard_ext_community'] - if discard_ext_community != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["discard_ext_community"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != discard_ext_community: - need_cfg = True - else: - need_cfg = True - - allow_as_loop_enable = module.params['allow_as_loop_enable'] - if allow_as_loop_enable != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["allow_as_loop_enable"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != allow_as_loop_enable: - need_cfg = True - else: - need_cfg = True - - allow_as_loop_limit = module.params['allow_as_loop_limit'] - if allow_as_loop_limit: - if int(allow_as_loop_limit) > 10 or int(allow_as_loop_limit) < 1: - module.fail_json( - msg='the value of allow_as_loop_limit %s is out of [1 - 10].' % allow_as_loop_limit) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["allow_as_loop_limit"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != allow_as_loop_limit: - need_cfg = True - else: - need_cfg = True - - keep_all_routes = module.params['keep_all_routes'] - if keep_all_routes != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["keep_all_routes"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != keep_all_routes: - need_cfg = True - else: - need_cfg = True - - nexthop_configure = module.params['nexthop_configure'] - if nexthop_configure: - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - self.exist_nexthop_configure = "null" - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - self.exist_nexthop_configure = re_find[0] - result["nexthop_configure"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != nexthop_configure: - need_cfg = True - else: - need_cfg = True - - preferred_value = module.params['preferred_value'] - if preferred_value: - if int(preferred_value) > 65535 or int(preferred_value) < 0: - module.fail_json( - msg='the value of preferred_value %s is out of [0 - 65535].' % preferred_value) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["preferred_value"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != preferred_value: - need_cfg = True - else: - need_cfg = True - - public_as_only = module.params['public_as_only'] - if public_as_only != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["public_as_only"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != public_as_only: - need_cfg = True - else: - need_cfg = True - - public_as_only_force = module.params['public_as_only_force'] - if public_as_only_force != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["public_as_only_force"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != public_as_only_force: - need_cfg = True - else: - need_cfg = True - - public_as_only_limited = module.params['public_as_only_limited'] - if public_as_only_limited != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["public_as_only_limited"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != public_as_only_limited: - need_cfg = True - else: - need_cfg = True - - public_as_only_replace = module.params['public_as_only_replace'] - if public_as_only_replace != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["public_as_only_replace"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != public_as_only_replace: - need_cfg = True - else: - need_cfg = True - - public_as_only_skip_peer_as = module.params[ - 'public_as_only_skip_peer_as'] - if public_as_only_skip_peer_as != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["public_as_only_skip_peer_as"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != public_as_only_skip_peer_as: - need_cfg = True - else: - need_cfg = True - - route_limit = module.params['route_limit'] - if route_limit: - - if int(route_limit) < 1: - module.fail_json( - msg='the value of route_limit %s is out of [1 - 4294967295].' % route_limit) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["route_limit"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != route_limit: - need_cfg = True - else: - need_cfg = True - - route_limit_percent = module.params['route_limit_percent'] - if route_limit_percent: - - if int(route_limit_percent) < 1 or int(route_limit_percent) > 100: - module.fail_json( - msg='Error: The value of route_limit_percent %s is out of [1 - 100].' % route_limit_percent) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["route_limit_percent"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != route_limit_percent: - need_cfg = True - else: - need_cfg = True - - route_limit_type = module.params['route_limit_type'] - if route_limit_type: - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["route_limit_type"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != route_limit_type: - need_cfg = True - else: - need_cfg = True - - route_limit_idle_timeout = module.params['route_limit_idle_timeout'] - if route_limit_idle_timeout: - - if int(route_limit_idle_timeout) < 1 or int(route_limit_idle_timeout) > 1200: - module.fail_json( - msg='Error: The value of route_limit_idle_timeout %s is out of ' - '[1 - 1200].' % route_limit_idle_timeout) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["route_limit_idle_timeout"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != route_limit_idle_timeout: - need_cfg = True - else: - need_cfg = True - - rt_updt_interval = module.params['rt_updt_interval'] - if rt_updt_interval: - - if int(rt_updt_interval) < 0 or int(rt_updt_interval) > 600: - module.fail_json( - msg='Error: The value of rt_updt_interval %s is out of [0 - 600].' % rt_updt_interval) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["rt_updt_interval"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != rt_updt_interval: - need_cfg = True - else: - need_cfg = True - - redirect_ip = module.params['redirect_ip'] - if redirect_ip != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["redirect_ip"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != redirect_ip: - need_cfg = True - else: - need_cfg = True - - redirect_ip_validation = module.params['redirect_ip_validation'] - if redirect_ip_validation != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["redirect_ip_validation"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != redirect_ip_validation: - need_cfg = True - else: - need_cfg = True - - reflect_client = module.params['reflect_client'] - if reflect_client != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["reflect_client"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != reflect_client: - need_cfg = True - else: - need_cfg = True - - substitute_as_enable = module.params['substitute_as_enable'] - if substitute_as_enable != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["substitute_as_enable"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != substitute_as_enable: - need_cfg = True - else: - need_cfg = True - - import_rt_policy_name = module.params['import_rt_policy_name'] - if import_rt_policy_name: - - if len(import_rt_policy_name) < 1 or len(import_rt_policy_name) > 40: - module.fail_json( - msg='Error: The len of import_rt_policy_name %s is out of [1 - 40].' % import_rt_policy_name) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["import_rt_policy_name"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != import_rt_policy_name: - need_cfg = True - else: - need_cfg = True - - export_rt_policy_name = module.params['export_rt_policy_name'] - if export_rt_policy_name: - - if len(export_rt_policy_name) < 1 or len(export_rt_policy_name) > 40: - module.fail_json( - msg='Error: The len of export_rt_policy_name %s is out of [1 - 40].' % export_rt_policy_name) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["export_rt_policy_name"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != export_rt_policy_name: - need_cfg = True - else: - need_cfg = True - - import_pref_filt_name = module.params['import_pref_filt_name'] - if import_pref_filt_name: - - if len(import_pref_filt_name) < 1 or len(import_pref_filt_name) > 169: - module.fail_json( - msg='Error: The len of import_pref_filt_name %s is out of [1 - 169].' % import_pref_filt_name) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["import_pref_filt_name"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != import_pref_filt_name: - need_cfg = True - else: - need_cfg = True - - export_pref_filt_name = module.params['export_pref_filt_name'] - if export_pref_filt_name: - - if len(export_pref_filt_name) < 1 or len(export_pref_filt_name) > 169: - module.fail_json( - msg='Error: The len of export_pref_filt_name %s is out of [1 - 169].' % export_pref_filt_name) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["export_pref_filt_name"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != export_pref_filt_name: - need_cfg = True - else: - need_cfg = True - - import_as_path_filter = module.params['import_as_path_filter'] - if import_as_path_filter: - - if int(import_as_path_filter) < 1 or int(import_as_path_filter) > 256: - module.fail_json( - msg='Error: The value of import_as_path_filter %s is out of [1 - 256].' % import_as_path_filter) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["import_as_path_filter"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != import_as_path_filter: - need_cfg = True - else: - need_cfg = True - - export_as_path_filter = module.params['export_as_path_filter'] - if export_as_path_filter: - - if int(export_as_path_filter) < 1 or int(export_as_path_filter) > 256: - module.fail_json( - msg='Error: The value of export_as_path_filter %s is out of [1 - 256].' % export_as_path_filter) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["export_as_path_filter"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != export_as_path_filter: - need_cfg = True - else: - need_cfg = True - - import_as_path_name_or_num = module.params[ - 'import_as_path_name_or_num'] - if import_as_path_name_or_num: - - if len(import_as_path_name_or_num) < 1 or len(import_as_path_name_or_num) > 51: - module.fail_json( - msg='Error: The len of import_as_path_name_or_num %s is out ' - 'of [1 - 51].' % import_as_path_name_or_num) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["import_as_path_name_or_num"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != import_as_path_name_or_num: - need_cfg = True - else: - need_cfg = True - - export_as_path_name_or_num = module.params[ - 'export_as_path_name_or_num'] - if export_as_path_name_or_num: - - if len(export_as_path_name_or_num) < 1 or len(export_as_path_name_or_num) > 51: - module.fail_json( - msg='Error: The len of export_as_path_name_or_num %s is out ' - 'of [1 - 51].' % export_as_path_name_or_num) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["export_as_path_name_or_num"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != export_as_path_name_or_num: - need_cfg = True - else: - need_cfg = True - - import_acl_name_or_num = module.params['import_acl_name_or_num'] - if import_acl_name_or_num: - - if len(import_acl_name_or_num) < 1 or len(import_acl_name_or_num) > 32: - module.fail_json( - msg='Error: The len of import_acl_name_or_num %s is out of [1 - 32].' % import_acl_name_or_num) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["import_acl_name_or_num"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != import_acl_name_or_num: - need_cfg = True - else: - need_cfg = True - - export_acl_name_or_num = module.params['export_acl_name_or_num'] - if export_acl_name_or_num: - - if len(export_acl_name_or_num) < 1 or len(export_acl_name_or_num) > 32: - module.fail_json( - msg='Error: The len of export_acl_name_or_num %s is out of [1 - 32].' % export_acl_name_or_num) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["export_acl_name_or_num"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != export_acl_name_or_num: - need_cfg = True - else: - need_cfg = True - - ipprefix_orf_enable = module.params['ipprefix_orf_enable'] - if ipprefix_orf_enable != 'no_use': - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["ipprefix_orf_enable"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != ipprefix_orf_enable: - need_cfg = True - else: - need_cfg = True - - is_nonstd_ipprefix_mod = module.params['is_nonstd_ipprefix_mod'] - if is_nonstd_ipprefix_mod != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["is_nonstd_ipprefix_mod"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != is_nonstd_ipprefix_mod: - need_cfg = True - else: - need_cfg = True - - orftype = module.params['orftype'] - if orftype: - - if int(orftype) < 0 or int(orftype) > 65535: - module.fail_json( - msg='Error: The value of orftype %s is out of [0 - 65535].' % orftype) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["orftype"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != orftype: - need_cfg = True - else: - need_cfg = True - - orf_mode = module.params['orf_mode'] - if orf_mode: - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["orf_mode"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != orf_mode: - need_cfg = True - else: - need_cfg = True - - soostring = module.params['soostring'] - if soostring: - - if len(soostring) < 3 or len(soostring) > 21: - module.fail_json( - msg='Error: The len of soostring %s is out of [3 - 21].' % soostring) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["soostring"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != soostring: - need_cfg = True - else: - need_cfg = True - - default_rt_adv_enable = module.params['default_rt_adv_enable'] - if default_rt_adv_enable != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["default_rt_adv_enable"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != default_rt_adv_enable: - need_cfg = True - else: - need_cfg = True - - default_rt_adv_policy = module.params['default_rt_adv_policy'] - if default_rt_adv_policy: - - if len(default_rt_adv_policy) < 1 or len(default_rt_adv_policy) > 40: - module.fail_json( - msg='Error: The len of default_rt_adv_policy %s is out of [1 - 40].' % default_rt_adv_policy) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["default_rt_adv_policy"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != default_rt_adv_policy: - need_cfg = True - else: - need_cfg = True - - default_rt_match_mode = module.params['default_rt_match_mode'] - if default_rt_match_mode: - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["default_rt_match_mode"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != default_rt_match_mode: - need_cfg = True - else: - need_cfg = True - - add_path_mode = module.params['add_path_mode'] - if add_path_mode: - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["add_path_mode"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != add_path_mode: - need_cfg = True - else: - need_cfg = True - - adv_add_path_num = module.params['adv_add_path_num'] - if adv_add_path_num: - - if int(adv_add_path_num) < 2 or int(adv_add_path_num) > 64: - module.fail_json( - msg='Error: The value of adv_add_path_num %s is out of [2 - 64].' % adv_add_path_num) - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["adv_add_path_num"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != adv_add_path_num: - need_cfg = True - else: - need_cfg = True - - origin_as_valid = module.params['origin_as_valid'] - if origin_as_valid != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["origin_as_valid"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != origin_as_valid: - need_cfg = True - else: - need_cfg = True - - vpls_enable = module.params['vpls_enable'] - if vpls_enable != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["vpls_enable"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != vpls_enable: - need_cfg = True - else: - need_cfg = True - - vpls_ad_disable = module.params['vpls_ad_disable'] - if vpls_ad_disable != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["vpls_ad_disable"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != vpls_ad_disable: - need_cfg = True - else: - need_cfg = True - - update_pkt_standard_compatible = module.params[ - 'update_pkt_standard_compatible'] - if update_pkt_standard_compatible != 'no_use': - - conf_str = CE_GET_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + "" + \ - CE_GET_BGP_PEER_AF_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - need_cfg = True - else: - re_find = re.findall( - r'.*(.*).*', recv_xml) - - if re_find: - result["update_pkt_standard_compatible"] = re_find - result["vrf_name"] = vrf_name - result["af_type"] = af_type - if re_find[0] != update_pkt_standard_compatible: - need_cfg = True - else: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def merge_bgp_peer_af(self, **kwargs): - """ merge_bgp_peer_af """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - remote_address = module.params['remote_address'] - - conf_str = CE_MERGE_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) + CE_MERGE_BGP_PEER_AF_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp peer address family failed.') - - cmds = [] - cmd = af_type - if af_type == "ipv4uni": - if vrf_name == "_public_": - cmd = "ipv4-family unicast" - else: - cmd = "ipv4-family vpn-instance %s" % vrf_name - elif af_type == "ipv4multi": - cmd = "ipv4-family multicast" - elif af_type == "ipv6uni": - if vrf_name == "_public_": - cmd = "ipv6-family unicast" - else: - cmd = "ipv6-family vpn-instance %s" % vrf_name - elif af_type == "evpn": - cmd = "l2vpn-family evpn" - elif af_type == "ipv4vpn": - cmd = "ipv4-family vpnv4" - elif af_type == "ipv6vpn": - cmd = "ipv6-family vpnv6" - cmds.append(cmd) - if vrf_name == "_public_": - cmd = "peer %s enable" % remote_address - else: - cmd = "peer %s" % remote_address - cmds.append(cmd) - - return cmds - - def create_bgp_peer_af(self, **kwargs): - """ create_bgp_peer_af """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - remote_address = module.params['remote_address'] - - conf_str = CE_CREATE_BGP_PEER_AF % (vrf_name, af_type, remote_address) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create bgp peer address family failed.') - - cmds = [] - cmd = af_type - if af_type == "ipv4uni": - if vrf_name == "_public_": - cmd = "ipv4-family unicast" - else: - cmd = "ipv4-family vpn-instance %s" % vrf_name - elif af_type == "ipv4multi": - cmd = "ipv4-family multicast" - elif af_type == "ipv6uni": - if vrf_name == "_public_": - cmd = "ipv6-family unicast" - else: - cmd = "ipv6-family vpn-instance %s" % vrf_name - elif af_type == "evpn": - cmd = "l2vpn-family evpn" - elif af_type == "ipv4vpn": - cmd = "ipv4-family vpnv4" - elif af_type == "ipv6vpn": - cmd = "ipv6-family vpnv6" - cmds.append(cmd) - if vrf_name == "_public_": - cmd = "peer %s enable" % remote_address - else: - cmd = "peer %s" % remote_address - cmds.append(cmd) - - return cmds - - def delete_bgp_peer_af(self, **kwargs): - """ delete_bgp_peer_af """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - remote_address = module.params['remote_address'] - - conf_str = CE_DELETE_BGP_PEER_AF % (vrf_name, af_type, remote_address) - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete bgp peer address family failed.') - - cmds = [] - cmd = af_type - if af_type == "ipv4uni": - if vrf_name == "_public_": - cmd = "ipv4-family unicast" - else: - cmd = "ipv4-family vpn-instance %s" % vrf_name - elif af_type == "ipv4multi": - cmd = "ipv4-family multicast" - elif af_type == "ipv6uni": - if vrf_name == "_public_": - cmd = "ipv6-family unicast" - else: - cmd = "ipv6-family vpn-instance %s" % vrf_name - elif af_type == "evpn": - cmd = "l2vpn-family evpn" - elif af_type == "ipv4vpn": - cmd = "ipv4-family vpnv4" - elif af_type == "ipv6vpn": - cmd = "ipv6-family vpnv6" - cmds.append(cmd) - if vrf_name == "_public_": - cmd = "undo peer %s enable" % remote_address - else: - cmd = "undo peer %s" % remote_address - cmds.append(cmd) - - return cmds - - def merge_bgp_peer_af_other(self, **kwargs): - """ merge_bgp_peer_af_other """ - - module = kwargs["module"] - - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - remote_address = module.params['remote_address'] - - conf_str = CE_MERGE_BGP_PEER_AF_HEADER % ( - vrf_name, af_type, remote_address) - - cmds = [] - - advertise_irb = module.params['advertise_irb'] - if advertise_irb != 'no_use': - conf_str += "%s" % advertise_irb - - if advertise_irb == "true": - cmd = "peer %s advertise irb" % remote_address - else: - cmd = "undo peer %s advertise irb" % remote_address - cmds.append(cmd) - - advertise_arp = module.params['advertise_arp'] - if advertise_arp != 'no_use': - conf_str += "%s" % advertise_arp - - if advertise_arp == "true": - cmd = "peer %s advertise arp" % remote_address - else: - cmd = "undo peer %s advertise arp" % remote_address - cmds.append(cmd) - - advertise_remote_nexthop = module.params['advertise_remote_nexthop'] - if advertise_remote_nexthop != 'no_use': - conf_str += "%s" % advertise_remote_nexthop - - if advertise_remote_nexthop == "true": - cmd = "peer %s advertise remote-nexthop" % remote_address - else: - cmd = "undo peer %s advertise remote-nexthop" % remote_address - cmds.append(cmd) - - advertise_community = module.params['advertise_community'] - if advertise_community != 'no_use': - conf_str += "%s" % advertise_community - - if advertise_community == "true": - cmd = "peer %s advertise-community" % remote_address - else: - cmd = "undo peer %s advertise-community" % remote_address - cmds.append(cmd) - - advertise_ext_community = module.params['advertise_ext_community'] - if advertise_ext_community != 'no_use': - conf_str += "%s" % advertise_ext_community - - if advertise_ext_community == "true": - cmd = "peer %s advertise-ext-community" % remote_address - else: - cmd = "undo peer %s advertise-ext-community" % remote_address - cmds.append(cmd) - - discard_ext_community = module.params['discard_ext_community'] - if discard_ext_community != 'no_use': - conf_str += "%s" % discard_ext_community - - if discard_ext_community == "true": - cmd = "peer %s discard-ext-community" % remote_address - else: - cmd = "undo peer %s discard-ext-community" % remote_address - cmds.append(cmd) - - allow_as_loop_enable = module.params['allow_as_loop_enable'] - if allow_as_loop_enable != 'no_use': - conf_str += "%s" % allow_as_loop_enable - - if allow_as_loop_enable == "true": - cmd = "peer %s allow-as-loop" % remote_address - else: - cmd = "undo peer %s allow-as-loop" % remote_address - cmds.append(cmd) - - allow_as_loop_limit = module.params['allow_as_loop_limit'] - if allow_as_loop_limit: - conf_str += "%s" % allow_as_loop_limit - - if allow_as_loop_enable == "true": - cmd = "peer %s allow-as-loop %s" % (remote_address, allow_as_loop_limit) - else: - cmd = "undo peer %s allow-as-loop" % remote_address - cmds.append(cmd) - - keep_all_routes = module.params['keep_all_routes'] - if keep_all_routes != 'no_use': - conf_str += "%s" % keep_all_routes - - if keep_all_routes == "true": - cmd = "peer %s keep-all-routes" % remote_address - else: - cmd = "undo peer %s keep-all-routes" % remote_address - cmds.append(cmd) - - nexthop_configure = module.params['nexthop_configure'] - if nexthop_configure: - conf_str += "%s" % nexthop_configure - - if nexthop_configure == "local": - cmd = "peer %s next-hop-local" % remote_address - cmds.append(cmd) - elif nexthop_configure == "invariable": - cmd = "peer %s next-hop-invariable" % remote_address - cmds.append(cmd) - else: - if self.exist_nexthop_configure != "null": - if self.exist_nexthop_configure == "local": - cmd = "undo peer %s next-hop-local" % remote_address - cmds.append(cmd) - elif self.exist_nexthop_configure == "invariable": - cmd = "undo peer %s next-hop-invariable" % remote_address - cmds.append(cmd) - preferred_value = module.params['preferred_value'] - if preferred_value: - conf_str += "%s" % preferred_value - - cmd = "peer %s preferred-value %s" % (remote_address, preferred_value) - cmds.append(cmd) - - public_as_only = module.params['public_as_only'] - if public_as_only != 'no_use': - conf_str += "%s" % public_as_only - - if public_as_only == "true": - cmd = "peer %s public-as-only" % remote_address - else: - cmd = "undo peer %s public-as-only" % remote_address - cmds.append(cmd) - - public_as_only_force = module.params['public_as_only_force'] - if public_as_only_force != 'no_use': - conf_str += "%s" % public_as_only_force - - if public_as_only_force == "true": - cmd = "peer %s public-as-only force" % remote_address - else: - cmd = "undo peer %s public-as-only force" % remote_address - cmds.append(cmd) - - public_as_only_limited = module.params['public_as_only_limited'] - if public_as_only_limited != 'no_use': - conf_str += "%s" % public_as_only_limited - - if public_as_only_limited == "true": - cmd = "peer %s public-as-only limited" % remote_address - else: - cmd = "undo peer %s public-as-only limited" % remote_address - cmds.append(cmd) - - public_as_only_replace = module.params['public_as_only_replace'] - if public_as_only_replace != 'no_use': - conf_str += "%s" % public_as_only_replace - - if public_as_only_replace == "true": - if public_as_only_force != "no_use": - cmd = "peer %s public-as-only force replace" % remote_address - if public_as_only_limited != "no_use": - cmd = "peer %s public-as-only limited replace" % remote_address - else: - if public_as_only_force != "no_use": - cmd = "undo peer %s public-as-only force replace" % remote_address - if public_as_only_limited != "no_use": - cmd = "undo peer %s public-as-only limited replace" % remote_address - cmds.append(cmd) - - public_as_only_skip_peer_as = module.params[ - 'public_as_only_skip_peer_as'] - if public_as_only_skip_peer_as != 'no_use': - conf_str += "%s" % public_as_only_skip_peer_as - - if public_as_only_skip_peer_as == "true": - if public_as_only_force != "no_use": - cmd = "peer %s public-as-only force include-peer-as" % remote_address - if public_as_only_limited != "no_use": - cmd = "peer %s public-as-only limited include-peer-as" % remote_address - else: - if public_as_only_force != "no_use": - cmd = "undo peer %s public-as-only force include-peer-as" % remote_address - if public_as_only_limited != "no_use": - cmd = "undo peer %s public-as-only limited include-peer-as" % remote_address - cmds.append(cmd) - - route_limit_sign = "route-limit" - if af_type == "evpn": - route_limit_sign = "mac-limit" - route_limit = module.params['route_limit'] - if route_limit: - conf_str += "%s" % route_limit - - cmd = "peer %s %s %s" % (remote_address, route_limit_sign, route_limit) - cmds.append(cmd) - - route_limit_percent = module.params['route_limit_percent'] - if route_limit_percent: - conf_str += "%s" % route_limit_percent - - cmd = "peer %s %s %s %s" % (remote_address, route_limit_sign, route_limit, route_limit_percent) - cmds.append(cmd) - - route_limit_type = module.params['route_limit_type'] - if route_limit_type: - conf_str += "%s" % route_limit_type - - if route_limit_type == "alertOnly": - cmd = "peer %s %s %s %s alert-only" % (remote_address, route_limit_sign, route_limit, route_limit_percent) - cmds.append(cmd) - elif route_limit_type == "idleForever": - cmd = "peer %s %s %s %s idle-forever" % (remote_address, route_limit_sign, route_limit, route_limit_percent) - cmds.append(cmd) - elif route_limit_type == "idleTimeout": - cmd = "peer %s %s %s %s idle-timeout" % (remote_address, route_limit_sign, route_limit, route_limit_percent) - cmds.append(cmd) - - route_limit_idle_timeout = module.params['route_limit_idle_timeout'] - if route_limit_idle_timeout: - conf_str += "%s" % route_limit_idle_timeout - - cmd = "peer %s %s %s %s idle-timeout %s" % (remote_address, route_limit_sign, route_limit, route_limit_percent, route_limit_idle_timeout) - cmds.append(cmd) - - rt_updt_interval = module.params['rt_updt_interval'] - if rt_updt_interval: - conf_str += "%s" % rt_updt_interval - - cmd = "peer %s route-update-interval %s" % (remote_address, rt_updt_interval) - cmds.append(cmd) - - redirect_ip = module.params['redirect_ip'] - if redirect_ip != 'no_use': - conf_str += "%s" % redirect_ip - - redirect_ip_validation = module.params['redirect_ip_validation'] - if redirect_ip_validation != 'no_use': - conf_str += "%s" % redirect_ip_validation - - reflect_client = module.params['reflect_client'] - if reflect_client != 'no_use': - conf_str += "%s" % reflect_client - - if reflect_client == "true": - cmd = "peer %s reflect-client" % remote_address - else: - cmd = "undo peer %s reflect-client" % remote_address - cmds.append(cmd) - - substitute_as_enable = module.params['substitute_as_enable'] - if substitute_as_enable != 'no_use': - conf_str += "%s" % substitute_as_enable - - if substitute_as_enable == "true": - cmd = "peer %s substitute-as" % remote_address - else: - cmd = "undo peer %s substitute-as" % remote_address - cmds.append(cmd) - - import_rt_policy_name = module.params['import_rt_policy_name'] - if import_rt_policy_name: - conf_str += "%s" % import_rt_policy_name - - cmd = "peer %s route-policy %s import" % (remote_address, import_rt_policy_name) - cmds.append(cmd) - - export_rt_policy_name = module.params['export_rt_policy_name'] - if export_rt_policy_name: - conf_str += "%s" % export_rt_policy_name - - cmd = "peer %s route-policy %s export" % (remote_address, export_rt_policy_name) - cmds.append(cmd) - - import_pref_filt_name = module.params['import_pref_filt_name'] - if import_pref_filt_name: - conf_str += "%s" % import_pref_filt_name - - cmd = "peer %s ip-prefix %s import" % (remote_address, import_pref_filt_name) - cmds.append(cmd) - - export_pref_filt_name = module.params['export_pref_filt_name'] - if export_pref_filt_name: - conf_str += "%s" % export_pref_filt_name - - cmd = "peer %s ip-prefix %s export" % (remote_address, export_pref_filt_name) - cmds.append(cmd) - - import_as_path_filter = module.params['import_as_path_filter'] - if import_as_path_filter: - conf_str += "%s" % import_as_path_filter - - cmd = "peer %s as-path-filter %s import" % (remote_address, import_as_path_filter) - cmds.append(cmd) - - export_as_path_filter = module.params['export_as_path_filter'] - if export_as_path_filter: - conf_str += "%s" % export_as_path_filter - - cmd = "peer %s as-path-filter %s export" % (remote_address, export_as_path_filter) - cmds.append(cmd) - - import_as_path_name_or_num = module.params[ - 'import_as_path_name_or_num'] - if import_as_path_name_or_num: - conf_str += "%s" % import_as_path_name_or_num - - cmd = "peer %s as-path-filter %s import" % (remote_address, import_as_path_name_or_num) - cmds.append(cmd) - - export_as_path_name_or_num = module.params[ - 'export_as_path_name_or_num'] - if export_as_path_name_or_num: - conf_str += "%s" % export_as_path_name_or_num - - cmd = "peer %s as-path-filter %s export" % (remote_address, export_as_path_name_or_num) - cmds.append(cmd) - - import_acl_name_or_num = module.params['import_acl_name_or_num'] - if import_acl_name_or_num: - conf_str += "%s" % import_acl_name_or_num - if import_acl_name_or_num.isdigit(): - cmd = "peer %s filter-policy %s import" % (remote_address, import_acl_name_or_num) - else: - cmd = "peer %s filter-policy acl-name %s import" % (remote_address, import_acl_name_or_num) - cmds.append(cmd) - - export_acl_name_or_num = module.params['export_acl_name_or_num'] - if export_acl_name_or_num: - conf_str += "%s" % export_acl_name_or_num - if export_acl_name_or_num.isdigit(): - cmd = "peer %s filter-policy %s export" % (remote_address, export_acl_name_or_num) - else: - cmd = "peer %s filter-policy acl-name %s export" % (remote_address, export_acl_name_or_num) - cmds.append(cmd) - - ipprefix_orf_enable = module.params['ipprefix_orf_enable'] - if ipprefix_orf_enable != 'no_use': - conf_str += "%s" % ipprefix_orf_enable - - if ipprefix_orf_enable == "true": - cmd = "peer %s capability-advertise orf ip-prefix" % remote_address - else: - cmd = "undo peer %s capability-advertise orf ip-prefix" % remote_address - cmds.append(cmd) - - is_nonstd_ipprefix_mod = module.params['is_nonstd_ipprefix_mod'] - if is_nonstd_ipprefix_mod != 'no_use': - conf_str += "%s" % is_nonstd_ipprefix_mod - - if is_nonstd_ipprefix_mod == "true": - if ipprefix_orf_enable == "true": - cmd = "peer %s capability-advertise orf non-standard-compatible" % remote_address - else: - cmd = "undo peer %s capability-advertise orf non-standard-compatible" % remote_address - cmds.append(cmd) - else: - if ipprefix_orf_enable == "true": - cmd = "peer %s capability-advertise orf" % remote_address - else: - cmd = "undo peer %s capability-advertise orf" % remote_address - cmds.append(cmd) - - orftype = module.params['orftype'] - if orftype: - conf_str += "%s" % orftype - - orf_mode = module.params['orf_mode'] - if orf_mode: - conf_str += "%s" % orf_mode - - if ipprefix_orf_enable == "true": - cmd = "peer %s capability-advertise orf ip-prefix %s" % (remote_address, orf_mode) - else: - cmd = "undo peer %s capability-advertise orf ip-prefix %s" % (remote_address, orf_mode) - cmds.append(cmd) - - soostring = module.params['soostring'] - if soostring: - conf_str += "%s" % soostring - - cmd = "peer %s soo %s" % (remote_address, soostring) - cmds.append(cmd) - - cmd = "" - default_rt_adv_enable = module.params['default_rt_adv_enable'] - if default_rt_adv_enable != 'no_use': - conf_str += "%s" % default_rt_adv_enable - - if default_rt_adv_enable == "true": - cmd += "peer %s default-route-advertise" % remote_address - else: - cmd += "undo peer %s default-route-advertise" % remote_address - - default_rt_adv_policy = module.params['default_rt_adv_policy'] - if default_rt_adv_policy: - conf_str += "%s" % default_rt_adv_policy - cmd += " route-policy %s" % default_rt_adv_policy - - default_rt_match_mode = module.params['default_rt_match_mode'] - if default_rt_match_mode: - conf_str += "%s" % default_rt_match_mode - - if default_rt_match_mode == "matchall": - cmd += " conditional-route-match-all" - elif default_rt_match_mode == "matchany": - cmd += " conditional-route-match-any" - - if cmd: - cmds.append(cmd) - - add_path_mode = module.params['add_path_mode'] - if add_path_mode: - conf_str += "%s" % add_path_mode - if add_path_mode == "receive": - cmd = "peer %s capability-advertise add-path receive" % remote_address - elif add_path_mode == "send": - cmd = "peer %s capability-advertise add-path send" % remote_address - elif add_path_mode == "both": - cmd = "peer %s capability-advertise add-path both" % remote_address - cmds.append(cmd) - - adv_add_path_num = module.params['adv_add_path_num'] - if adv_add_path_num: - conf_str += "%s" % adv_add_path_num - cmd = "peer %s advertise add-path path-number %s" % (remote_address, adv_add_path_num) - cmds.append(cmd) - origin_as_valid = module.params['origin_as_valid'] - if origin_as_valid != 'no_use': - conf_str += "%s" % origin_as_valid - - vpls_enable = module.params['vpls_enable'] - if vpls_enable != 'no_use': - conf_str += "%s" % vpls_enable - - vpls_ad_disable = module.params['vpls_ad_disable'] - if vpls_ad_disable != 'no_use': - conf_str += "%s" % vpls_ad_disable - - update_pkt_standard_compatible = module.params[ - 'update_pkt_standard_compatible'] - if update_pkt_standard_compatible != 'no_use': - conf_str += "%s" % update_pkt_standard_compatible - - conf_str += CE_MERGE_BGP_PEER_AF_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge bgp peer address family other failed.') - - return cmds - - -def main(): - """ main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - vrf_name=dict(type='str', required=True), - af_type=dict(choices=['ipv4uni', 'ipv4multi', 'ipv4vpn', - 'ipv6uni', 'ipv6vpn', 'evpn'], required=True), - remote_address=dict(type='str', required=True), - advertise_irb=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - advertise_arp=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - advertise_remote_nexthop=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - advertise_community=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - advertise_ext_community=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - discard_ext_community=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - allow_as_loop_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - allow_as_loop_limit=dict(type='str'), - keep_all_routes=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - nexthop_configure=dict(choices=['null', 'local', 'invariable']), - preferred_value=dict(type='str'), - public_as_only=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - public_as_only_force=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - public_as_only_limited=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - public_as_only_replace=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - public_as_only_skip_peer_as=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - route_limit=dict(type='str'), - route_limit_percent=dict(type='str'), - route_limit_type=dict( - choices=['noparameter', 'alertOnly', 'idleForever', 'idleTimeout']), - route_limit_idle_timeout=dict(type='str'), - rt_updt_interval=dict(type='str'), - redirect_ip=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - redirect_ip_validation=dict( - type='str', default='no_use', - choices=['no_use', 'true', 'false'], aliases=['redirect_ip_vaildation']), - reflect_client=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - substitute_as_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - import_rt_policy_name=dict(type='str'), - export_rt_policy_name=dict(type='str'), - import_pref_filt_name=dict(type='str'), - export_pref_filt_name=dict(type='str'), - import_as_path_filter=dict(type='str'), - export_as_path_filter=dict(type='str'), - import_as_path_name_or_num=dict(type='str'), - export_as_path_name_or_num=dict(type='str'), - import_acl_name_or_num=dict(type='str'), - export_acl_name_or_num=dict(type='str'), - ipprefix_orf_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - is_nonstd_ipprefix_mod=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - orftype=dict(type='str'), - orf_mode=dict(choices=['null', 'receive', 'send', 'both']), - soostring=dict(type='str'), - default_rt_adv_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - default_rt_adv_policy=dict(type='str'), - default_rt_match_mode=dict(choices=['null', 'matchall', 'matchany']), - add_path_mode=dict(choices=['null', 'receive', 'send', 'both']), - adv_add_path_num=dict(type='str'), - origin_as_valid=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - vpls_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - vpls_ad_disable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - update_pkt_standard_compatible=dict(type='str', default='no_use', choices=['no_use', 'true', 'false'])) - - argument_spec.update(ce_argument_spec) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - - changed = False - proposed = dict() - existing = dict() - end_state = dict() - updates = [] - - state = module.params['state'] - vrf_name = module.params['vrf_name'] - af_type = module.params['af_type'] - remote_address = module.params['remote_address'] - advertise_irb = module.params['advertise_irb'] - advertise_arp = module.params['advertise_arp'] - advertise_remote_nexthop = module.params['advertise_remote_nexthop'] - advertise_community = module.params['advertise_community'] - advertise_ext_community = module.params['advertise_ext_community'] - discard_ext_community = module.params['discard_ext_community'] - allow_as_loop_enable = module.params['allow_as_loop_enable'] - allow_as_loop_limit = module.params['allow_as_loop_limit'] - keep_all_routes = module.params['keep_all_routes'] - nexthop_configure = module.params['nexthop_configure'] - preferred_value = module.params['preferred_value'] - public_as_only = module.params['public_as_only'] - public_as_only_force = module.params['public_as_only_force'] - public_as_only_limited = module.params['public_as_only_limited'] - public_as_only_replace = module.params['public_as_only_replace'] - public_as_only_skip_peer_as = module.params['public_as_only_skip_peer_as'] - route_limit = module.params['route_limit'] - route_limit_percent = module.params['route_limit_percent'] - route_limit_type = module.params['route_limit_type'] - route_limit_idle_timeout = module.params['route_limit_idle_timeout'] - rt_updt_interval = module.params['rt_updt_interval'] - redirect_ip = module.params['redirect_ip'] - redirect_ip_validation = module.params['redirect_ip_validation'] - reflect_client = module.params['reflect_client'] - substitute_as_enable = module.params['substitute_as_enable'] - import_rt_policy_name = module.params['import_rt_policy_name'] - export_rt_policy_name = module.params['export_rt_policy_name'] - import_pref_filt_name = module.params['import_pref_filt_name'] - export_pref_filt_name = module.params['export_pref_filt_name'] - import_as_path_filter = module.params['import_as_path_filter'] - export_as_path_filter = module.params['export_as_path_filter'] - import_as_path_name_or_num = module.params['import_as_path_name_or_num'] - export_as_path_name_or_num = module.params['export_as_path_name_or_num'] - import_acl_name_or_num = module.params['import_acl_name_or_num'] - export_acl_name_or_num = module.params['export_acl_name_or_num'] - ipprefix_orf_enable = module.params['ipprefix_orf_enable'] - is_nonstd_ipprefix_mod = module.params['is_nonstd_ipprefix_mod'] - orftype = module.params['orftype'] - orf_mode = module.params['orf_mode'] - soostring = module.params['soostring'] - default_rt_adv_enable = module.params['default_rt_adv_enable'] - default_rt_adv_policy = module.params['default_rt_adv_policy'] - default_rt_match_mode = module.params['default_rt_match_mode'] - add_path_mode = module.params['add_path_mode'] - adv_add_path_num = module.params['adv_add_path_num'] - origin_as_valid = module.params['origin_as_valid'] - vpls_enable = module.params['vpls_enable'] - vpls_ad_disable = module.params['vpls_ad_disable'] - update_pkt_standard_compatible = module.params[ - 'update_pkt_standard_compatible'] - - ce_bgp_peer_af_obj = BgpNeighborAf() - - # get proposed - proposed["state"] = state - if vrf_name: - proposed["vrf_name"] = vrf_name - if af_type: - proposed["af_type"] = af_type - if remote_address: - proposed["remote_address"] = remote_address - if advertise_irb != 'no_use': - proposed["advertise_irb"] = advertise_irb - if advertise_arp != 'no_use': - proposed["advertise_arp"] = advertise_arp - if advertise_remote_nexthop != 'no_use': - proposed["advertise_remote_nexthop"] = advertise_remote_nexthop - if advertise_community != 'no_use': - proposed["advertise_community"] = advertise_community - if advertise_ext_community != 'no_use': - proposed["advertise_ext_community"] = advertise_ext_community - if discard_ext_community != 'no_use': - proposed["discard_ext_community"] = discard_ext_community - if allow_as_loop_enable != 'no_use': - proposed["allow_as_loop_enable"] = allow_as_loop_enable - if allow_as_loop_limit: - proposed["allow_as_loop_limit"] = allow_as_loop_limit - if keep_all_routes != 'no_use': - proposed["keep_all_routes"] = keep_all_routes - if nexthop_configure: - proposed["nexthop_configure"] = nexthop_configure - if preferred_value: - proposed["preferred_value"] = preferred_value - if public_as_only != 'no_use': - proposed["public_as_only"] = public_as_only - if public_as_only_force != 'no_use': - proposed["public_as_only_force"] = public_as_only_force - if public_as_only_limited != 'no_use': - proposed["public_as_only_limited"] = public_as_only_limited - if public_as_only_replace != 'no_use': - proposed["public_as_only_replace"] = public_as_only_replace - if public_as_only_skip_peer_as != 'no_use': - proposed["public_as_only_skip_peer_as"] = public_as_only_skip_peer_as - if route_limit: - proposed["route_limit"] = route_limit - if route_limit_percent: - proposed["route_limit_percent"] = route_limit_percent - if route_limit_type: - proposed["route_limit_type"] = route_limit_type - if route_limit_idle_timeout: - proposed["route_limit_idle_timeout"] = route_limit_idle_timeout - if rt_updt_interval: - proposed["rt_updt_interval"] = rt_updt_interval - if redirect_ip != 'no_use': - proposed["redirect_ip"] = redirect_ip - if redirect_ip_validation != 'no_use': - proposed["redirect_ip_validation"] = redirect_ip_validation - if reflect_client != 'no_use': - proposed["reflect_client"] = reflect_client - if substitute_as_enable != 'no_use': - proposed["substitute_as_enable"] = substitute_as_enable - if import_rt_policy_name: - proposed["import_rt_policy_name"] = import_rt_policy_name - if export_rt_policy_name: - proposed["export_rt_policy_name"] = export_rt_policy_name - if import_pref_filt_name: - proposed["import_pref_filt_name"] = import_pref_filt_name - if export_pref_filt_name: - proposed["export_pref_filt_name"] = export_pref_filt_name - if import_as_path_filter: - proposed["import_as_path_filter"] = import_as_path_filter - if export_as_path_filter: - proposed["export_as_path_filter"] = export_as_path_filter - if import_as_path_name_or_num: - proposed["import_as_path_name_or_num"] = import_as_path_name_or_num - if export_as_path_name_or_num: - proposed["export_as_path_name_or_num"] = export_as_path_name_or_num - if import_acl_name_or_num: - proposed["import_acl_name_or_num"] = import_acl_name_or_num - if export_acl_name_or_num: - proposed["export_acl_name_or_num"] = export_acl_name_or_num - if ipprefix_orf_enable != 'no_use': - proposed["ipprefix_orf_enable"] = ipprefix_orf_enable - if is_nonstd_ipprefix_mod != 'no_use': - proposed["is_nonstd_ipprefix_mod"] = is_nonstd_ipprefix_mod - if orftype: - proposed["orftype"] = orftype - if orf_mode: - proposed["orf_mode"] = orf_mode - if soostring: - proposed["soostring"] = soostring - if default_rt_adv_enable != 'no_use': - proposed["default_rt_adv_enable"] = default_rt_adv_enable - if default_rt_adv_policy: - proposed["default_rt_adv_policy"] = default_rt_adv_policy - if default_rt_match_mode: - proposed["default_rt_match_mode"] = default_rt_match_mode - if add_path_mode: - proposed["add_path_mode"] = add_path_mode - if adv_add_path_num: - proposed["adv_add_path_num"] = adv_add_path_num - if origin_as_valid != 'no_use': - proposed["origin_as_valid"] = origin_as_valid - if vpls_enable != 'no_use': - proposed["vpls_enable"] = vpls_enable - if vpls_ad_disable != 'no_use': - proposed["vpls_ad_disable"] = vpls_ad_disable - if update_pkt_standard_compatible != 'no_use': - proposed["update_pkt_standard_compatible"] = update_pkt_standard_compatible - - if not ce_bgp_peer_af_obj: - module.fail_json(msg='Error: Init module failed.') - - bgp_peer_af_rst = ce_bgp_peer_af_obj.check_bgp_neighbor_af_args( - module=module) - bgp_peer_af_other_rst = ce_bgp_peer_af_obj.check_bgp_neighbor_af_other( - module=module) - - # state exist bgp peer address family config - exist_tmp = dict() - for item in bgp_peer_af_rst: - if item != "need_cfg": - exist_tmp[item] = bgp_peer_af_rst[item] - if exist_tmp: - existing["bgp neighbor af"] = exist_tmp - # state exist bgp peer address family other config - exist_tmp = dict() - for item in bgp_peer_af_other_rst: - if item != "need_cfg": - exist_tmp[item] = bgp_peer_af_other_rst[item] - if exist_tmp: - existing["bgp neighbor af other"] = exist_tmp - - if state == "present": - if bgp_peer_af_rst["need_cfg"]: - if "remote_address" in bgp_peer_af_rst.keys(): - cmd = ce_bgp_peer_af_obj.merge_bgp_peer_af(module=module) - changed = True - for item in cmd: - updates.append(item) - else: - cmd = ce_bgp_peer_af_obj.create_bgp_peer_af(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_peer_af_other_rst["need_cfg"]: - cmd = ce_bgp_peer_af_obj.merge_bgp_peer_af_other(module=module) - changed = True - for item in cmd: - updates.append(item) - - else: - if bgp_peer_af_rst["need_cfg"]: - cmd = ce_bgp_peer_af_obj.delete_bgp_peer_af(module=module) - changed = True - for item in cmd: - updates.append(item) - - if bgp_peer_af_other_rst["need_cfg"]: - pass - - # state end bgp peer address family config - bgp_peer_af_rst = ce_bgp_peer_af_obj.check_bgp_neighbor_af_args( - module=module) - end_tmp = dict() - for item in bgp_peer_af_rst: - if item != "need_cfg": - end_tmp[item] = bgp_peer_af_rst[item] - if end_tmp: - end_state["bgp neighbor af"] = end_tmp - # state end bgp peer address family other config - bgp_peer_af_other_rst = ce_bgp_peer_af_obj.check_bgp_neighbor_af_other( - module=module) - end_tmp = dict() - for item in bgp_peer_af_other_rst: - if item != "need_cfg": - end_tmp[item] = bgp_peer_af_other_rst[item] - if end_tmp: - end_state["bgp neighbor af other"] = end_tmp - if end_state == existing: - changed = False - updates = list() - - results = dict() - results['proposed'] = proposed - results['existing'] = existing - results['changed'] = changed - results['end_state'] = end_state - results['updates'] = updates - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_command.py b/plugins/modules/network/cloudengine/ce_command.py deleted file mode 100644 index c7aaf16919..0000000000 --- a/plugins/modules/network/cloudengine/ce_command.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- - -module: ce_command -author: "JackyGao2016 (@CloudEngine-Ansible)" -short_description: Run arbitrary command on HUAWEI CloudEngine devices. -description: - - Sends an arbitrary command to an HUAWEI CloudEngine node and returns - the results read from the device. The ce_command module includes an - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - commands: - description: - - The commands to send to the remote HUAWEI CloudEngine 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 I(retries) has been exceeded. - required: true - wait_for: - description: - - Specifies what to evaluate from the output of the command - and what conditionals to apply. This argument will cause - the task to wait for a particular conditional to be true - before moving forward. If the conditional is not true - by the configured retries, the task fails. See examples. - 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 I(wait_for) must be satisfied. If - the value is set to C(any) then only one of the values must be - satisfied. - default: all - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the I(wait_for) - conditionals. - default: 10 - interval: - description: - - Configures the interval in seconds to wait between retries - of the command. If the command does not pass the specified - conditional, the interval indicates how to long to wait before - trying the command again. - default: 1 -''' - -EXAMPLES = """ -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. - -- name: CloudEngine command test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: "Run display version on remote devices" - ce_command: - commands: display version - provider: "{{ cli }}" - - - name: "Run display version and check to see if output contains HUAWEI" - ce_command: - commands: display version - wait_for: result[0] contains HUAWEI - provider: "{{ cli }}" - - - name: "Run multiple commands on remote nodes" - ce_command: - commands: - - display version - - display device - provider: "{{ cli }}" - - - name: "Run multiple commands and evaluate the output" - ce_command: - commands: - - display version - - display device - wait_for: - - result[0] contains HUAWEI - - result[1] contains Device - provider: "{{ cli }}" -""" - -RETURN = """ -stdout: - description: the set of responses from the commands - returned: always - type: list - sample: ['...', '...'] - -stdout_lines: - description: The value of stdout split into a list - returned: always - type: list - sample: [['...', '...'], ['...'], ['...']] - -failed_conditions: - description: the conditionals that failed - returned: failed - type: list - sample: ['...', '...'] -""" - - -import time -import traceback - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, check_args -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import run_commands -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList -from ansible.module_utils.six import string_types -from ansible.module_utils._text import to_native - - -def to_lines(stdout): - lines = list() - for item in stdout: - if isinstance(item, string_types): - item = str(item).split('\n') - lines.append(item) - return lines - - -def parse_commands(module, warnings): - transform = ComplexList(dict( - command=dict(key=True), - output=dict(), - prompt=dict(), - answer=dict() - ), module) - - commands = transform(module.params['commands']) - - for _, item in enumerate(commands): - if module.check_mode and not item['command'].startswith('dis'): - warnings.append( - 'Only display commands are supported when using check_mode, not ' - 'executing %s' % item['command'] - ) - - return commands - - -def to_cli(obj): - cmd = obj['command'] - return cmd - - -def main(): - """entry point for module execution - """ - argument_spec = dict( - # { command: , output: , prompt: , response: } - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['any', 'all']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - argument_spec.update(ce_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() - - try: - conditionals = [Conditional(c) for c in wait_for] - except AttributeError as exc: - module.fail_json(msg=to_native(exc), exception=traceback.format_exc()) - - 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({ - 'stdout': responses, - 'stdout_lines': to_lines(responses) - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_config.py b/plugins/modules/network/cloudengine/ce_config.py deleted file mode 100644 index d4b3ab9652..0000000000 --- a/plugins/modules/network/cloudengine/ce_config.py +++ /dev/null @@ -1,496 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_config -author: "QijunPan (@QijunPan)" -short_description: Manage Huawei CloudEngine configuration sections. -description: - - Huawei CloudEngine configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with CloudEngine configuration sections in - a deterministic way. This module works with CLI transports. -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -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 current-configuration. Be sure to note the configuration - command syntax as some commands are automatically modified by the - device config parser. - 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. - src: - description: - - The I(src) argument provides a path to the configuration file - to load into the remote system. The path can either be a full - system path to the configuration file if the value starts with / - or relative to the root of the implemented role or playbook. - This argument is mutually exclusive with the I(lines) and - I(parents) arguments. - 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. - 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. - 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 current-configuration on the remote device. - 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. - default: line - choices: ['line', 'block'] - backup: - description: - - This argument will cause the module to create a full backup of - the current C(current-configuration) 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' - config: - description: - - The module, by default, will connect to the remote device and - retrieve the current current-configuration 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-configuration 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. - defaults: - description: - - The I(defaults) argument will influence how the current-configuration - is collected from the device. When the value is set to true, - the command used to collect the current-configuration is append with - the all keyword. When the value is set to false, the command - is issued without the all keyword. - type: bool - default: 'no' - save: - description: - - The C(save) argument instructs the module to save the - current-configuration to saved-configuration. This operation is performed - after any changes are made to the current running config. If - no changes are made, the configuration is still saved to the - startup config. This option will always cause the module to - return changed. - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. - -- name: CloudEngine config test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: "Configure top level configuration and save it" - ce_config: - lines: sysname {{ inventory_hostname }} - save: yes - provider: "{{ cli }}" - - - name: "Configure acl configuration and save it" - ce_config: - lines: - - rule 10 permit source 1.1.1.1 32 - - rule 20 permit source 2.2.2.2 32 - - rule 30 permit source 3.3.3.3 32 - - rule 40 permit source 4.4.4.4 32 - - rule 50 permit source 5.5.5.5 32 - parents: acl 2000 - before: undo acl 2000 - match: exact - provider: "{{ cli }}" - - - name: "Configure acl configuration and save it" - ce_config: - lines: - - rule 10 permit source 1.1.1.1 32 - - rule 20 permit source 2.2.2.2 32 - - rule 30 permit source 3.3.3.3 32 - - rule 40 permit source 4.4.4.4 32 - parents: acl 2000 - before: undo acl 2000 - replace: block - provider: "{{ cli }}" - - - name: configurable backup path - ce_config: - lines: sysname {{ inventory_hostname }} - provider: "{{ cli }}" - 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: Only when lines is specified. - type: list - sample: ['...', '...'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/ce_config.2016-07-16@22:28:34 -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import ConnectionError, Connection -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig as _NetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import dumps, ConfigLine, ignore_line -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_config, run_commands, exec_command, cli_err_msg -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import check_args as ce_check_args -import re - - -def check_args(module, warnings): - ce_check_args(module, warnings) - - -def not_user_view(prompt): - return prompt is not None and prompt.strip().startswith("[") - - -def command_level(command): - regex_level = re.search(r"^(\s*)\S+", command) - if regex_level is not None: - level = str(regex_level.group(1)) - return len(level) - return 0 - - -def _load_config(module, config): - """Sends configuration commands to the remote device - """ - connection = Connection(module._socket_path) - rc, out, err = exec_command(module, 'mmi-mode enable') - if rc != 0: - module.fail_json(msg='unable to set mmi-mode enable', output=err) - rc, out, err = exec_command(module, 'system-view immediately') - if rc != 0: - module.fail_json(msg='unable to enter system-view', output=err) - current_view_prompt = system_view_prompt = connection.get_prompt() - - for index, cmd in enumerate(config): - level = command_level(cmd) - current_view_prompt = connection.get_prompt() - rc, out, err = exec_command(module, cmd) - if rc != 0: - print_msg = cli_err_msg(cmd.strip(), err) - # re-try command max 3 times - for i in (1, 2, 3): - current_view_prompt = connection.get_prompt() - if current_view_prompt != system_view_prompt and not_user_view(current_view_prompt): - exec_command(module, "quit") - current_view_prompt = connection.get_prompt() - # if current view is system-view, break. - if current_view_prompt == system_view_prompt and level > 0: - break - elif current_view_prompt == system_view_prompt or not not_user_view(current_view_prompt): - break - rc, out, err = exec_command(module, cmd) - if rc == 0: - print_msg = None - break - if print_msg is not None: - module.fail_json(msg=print_msg) - - -def get_running_config(module): - contents = module.params['config'] - if not contents: - command = "display current-configuration " - if module.params['defaults']: - command += 'include-default' - resp = run_commands(module, command) - contents = resp[0] - return NetworkConfig(indent=1, contents=contents) - - -def get_candidate(module): - candidate = NetworkConfig(indent=1) - if module.params['src']: - config = module.params['src'] - candidate.load(config) - elif module.params['lines']: - parents = module.params['parents'] or list() - candidate.add(module.params['lines'], parents=parents) - return candidate - - -def run(module, result): - match = module.params['match'] - replace = module.params['replace'] - - candidate = get_candidate(module) - - if match != 'none': - before = get_running_config(module) - path = module.params['parents'] - configobjs = candidate.difference(before, match=match, replace=replace, path=path) - else: - configobjs = candidate.items - - if configobjs: - out_type = "commands" - if module.params["src"] is not None: - out_type = "raw" - commands = dumps(configobjs, out_type).split('\n') - - if module.params['lines']: - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - command_display = [] - for per_command in commands: - if per_command.strip() not in ['quit', 'return', 'system-view']: - command_display.append(per_command) - - result['commands'] = command_display - result['updates'] = command_display - - if not module.check_mode: - if module.params['parents'] is not None: - load_config(module, commands) - else: - _load_config(module, commands) - if match != "none": - after = get_running_config(module) - path = module.params["parents"] - if path is not None and match != 'line': - before_objs = before.get_block(path) - after_objs = after.get_block(path) - update = [] - if len(before_objs) == len(after_objs): - for b_item, a_item in zip(before_objs, after_objs): - if b_item != a_item: - update.append(a_item.text) - else: - update = [item.text for item in after_objs] - if len(update) == 0: - result["changed"] = False - result['updates'] = [] - else: - result["changed"] = True - result['updates'] = update - else: - configobjs = after.difference(before, match=match, replace=replace, path=path) - if len(configobjs) > 0: - result["changed"] = True - else: - result["changed"] = False - result['updates'] = [] - else: - result['changed'] = True - - -class NetworkConfig(_NetworkConfig): - - def add(self, lines, parents=None): - ancestors = list() - offset = 0 - obj = None - - # global config command - if not parents: - for line in lines: - # handle ignore lines - if ignore_line(line): - continue - - item = ConfigLine(line) - item.raw = line - self.items.append(item) - - else: - for index, p in enumerate(parents): - try: - i = index + 1 - obj = self.get_block(parents[:i])[0] - ancestors.append(obj) - - except ValueError: - # add parent to config - offset = index * self._indent - obj = ConfigLine(p) - obj.raw = p.rjust(len(p) + offset) - if ancestors: - obj._parents = list(ancestors) - ancestors[-1]._children.append(obj) - self.items.append(obj) - ancestors.append(obj) - - # add child objects - for line in lines: - # handle ignore lines - if ignore_line(line): - continue - - # check if child already exists - for child in ancestors[-1]._children: - if child.text == line: - break - else: - offset = len(parents) * self._indent - item = ConfigLine(line) - item.raw = line.rjust(len(line) + offset) - item._parents = ancestors - ancestors[-1]._children.append(item) - self.items.append(item) - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - config=dict(), - defaults=dict(type='bool', default=False), - - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - save=dict(type='bool', default=False), - ) - - argument_spec.update(ce_argument_spec) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - warnings = list() - check_args(module, warnings) - - result = dict(changed=False, warnings=warnings) - - if module.params['backup']: - result['__backup__'] = get_config(module) - - if any((module.params['src'], module.params['lines'])): - run(module, result) - - if module.params['save']: - if not module.check_mode: - run_commands(module, ['return', 'mmi-mode enable', 'save']) - result["changed"] = True - run_commands(module, ['return', 'undo mmi-mode enable']) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_dldp.py b/plugins/modules/network/cloudengine/ce_dldp.py deleted file mode 100644 index 926e9e50ba..0000000000 --- a/plugins/modules/network/cloudengine/ce_dldp.py +++ /dev/null @@ -1,553 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- - -module: ce_dldp -short_description: Manages global DLDP configuration on HUAWEI CloudEngine switches. -description: - - Manages global DLDP configuration on HUAWEI CloudEngine switches. -author: - - Zhijin Zhou (@QijunPan) -notes: - - The relevant configurations will be deleted if DLDP is disabled using enable=disable. - - When using auth_mode=none, it will restore the default DLDP authentication mode. By default, - DLDP packets are not authenticated. - - By default, the working mode of DLDP is enhance, so you are advised to use work_mode=enhance to restore default - DLDP working mode. - - The default interval for sending Advertisement packets is 5 seconds, so you are advised to use time_interval=5 to - restore default DLDP interval. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - enable: - description: - - Set global DLDP enable state. - choices: ['enable', 'disable'] - work_mode: - description: - - Set global DLDP work-mode. - choices: ['enhance', 'normal'] - time_internal: - description: - - Specifies the interval for sending Advertisement packets. - The value is an integer ranging from 1 to 100, in seconds. - The default interval for sending Advertisement packets is 5 seconds. - auth_mode: - description: - - Specifies authentication algorithm of DLDP. - choices: ['md5', 'simple', 'sha', 'hmac-sha256', 'none'] - auth_pwd: - description: - - Specifies authentication password. - The value is a string of 1 to 16 case-sensitive plaintexts or 24/32/48/108/128 case-sensitive encrypted - characters. The string excludes a question mark (?). - reset: - description: - - Specify whether reset DLDP state of disabled interfaces. - choices: ['enable', 'disable'] -''' - -EXAMPLES = ''' -- name: DLDP test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Configure global DLDP enable state" - ce_dldp: - enable: enable - provider: "{{ cli }}" - - - name: "Configure DLDP work-mode and ensure global DLDP state is already enabled" - ce_dldp: - enable: enable - work_mode: normal - provider: "{{ cli }}" - - - name: "Configure advertisement message time interval in seconds and ensure global DLDP state is already enabled" - ce_dldp: - enable: enable - time_interval: 6 - provider: "{{ cli }}" - - - name: "Configure a DLDP authentication mode and ensure global DLDP state is already enabled" - ce_dldp: - enable: enable - auth_mode: md5 - auth_pwd: abc - provider: "{{ cli }}" - - - name: "Reset DLDP state of disabled interfaces and ensure global DLDP state is already enabled" - ce_dldp: - enable: enable - reset: enable - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "enable": "enable", - "reset": "enable", - "time_internal": "12", - "work_mode": "normal" - } -existing: - description: k/v pairs of existing global DLDP configuration - returned: always - type: dict - sample: { - "enable": "disable", - "reset": "disable", - "time_internal": "5", - "work_mode": "enhance" - } -end_state: - description: k/v pairs of global DLDP configuration after module execution - returned: always - type: dict - sample: { - "enable": "enable", - "reset": "enable", - "time_internal": "12", - "work_mode": "normal" - } -updates: - description: command sent to the device - returned: always - type: list - sample: [ - "dldp enable", - "dldp work-mode normal", - "dldp interval 12", - "dldp reset" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import copy -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, set_nc_config, get_nc_config, execute_nc_action - -CE_NC_ACTION_RESET_DLDP = """ - - - - - -""" - -CE_NC_GET_GLOBAL_DLDP_CONFIG = """ - - - - - - - - - - -""" - -CE_NC_MERGE_DLDP_GLOBAL_CONFIG_HEAD = """ - - - - %s - %s - %s -""" - -CE_NC_MERGE_DLDP_GLOBAL_CONFIG_TAIL = """ - - - -""" - - -class Dldp(object): - """Manage global dldp configuration""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # DLDP global configuration info - self.enable = self.module.params['enable'] or None - self.work_mode = self.module.params['work_mode'] or None - self.internal = self.module.params['time_interval'] or None - self.reset = self.module.params['reset'] or None - self.auth_mode = self.module.params['auth_mode'] - self.auth_pwd = self.module.params['auth_pwd'] - - self.dldp_conf = dict() - self.same_conf = False - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = list() - self.end_state = list() - - def check_config_if_same(self): - """Judge whether current config is the same as what we excepted""" - - if self.enable and self.enable != self.dldp_conf['dldpEnable']: - return False - - if self.internal and self.internal != self.dldp_conf['dldpInterval']: - return False - - work_mode = 'normal' - if self.dldp_conf['dldpWorkMode'] == 'dldpEnhance': - work_mode = 'enhance' - if self.work_mode and self.work_mode != work_mode: - return False - - if self.auth_mode: - if self.auth_mode != 'none': - return False - - if self.auth_mode == 'none' and self.dldp_conf['dldpAuthMode'] != 'dldpAuthNone': - return False - - if self.reset and self.reset == 'enable': - return False - - return True - - def check_params(self): - """Check all input params""" - - if (self.auth_mode and self.auth_mode != 'none' and not self.auth_pwd) \ - or (self.auth_pwd and not self.auth_mode): - self.module.fail_json(msg="Error: auth_mode and auth_pwd must both exist or not exist.") - - if self.dldp_conf['dldpEnable'] == 'disable' and not self.enable: - if self.work_mode or self.reset or self.internal or self.auth_mode: - self.module.fail_json(msg="Error: when DLDP is already disabled globally, " - "work_mode, time_internal auth_mode and reset parameters are not " - "expected to configure.") - - if self.enable == 'disable' and (self.work_mode or self.internal or self.reset or self.auth_mode): - self.module.fail_json(msg="Error: when using enable=disable, work_mode, " - "time_internal auth_mode and reset parameters are not expected " - "to configure.") - - if self.internal: - if not self.internal.isdigit(): - self.module.fail_json( - msg='Error: time_interval must be digit.') - - if int(self.internal) < 1 or int(self.internal) > 100: - self.module.fail_json( - msg='Error: The value of time_internal should be between 1 and 100.') - - if self.auth_pwd: - if '?' in self.auth_pwd: - self.module.fail_json( - msg='Error: The auth_pwd string excludes a question mark (?).') - if (len(self.auth_pwd) != 24) and (len(self.auth_pwd) != 32) and (len(self.auth_pwd) != 48) and \ - (len(self.auth_pwd) != 108) and (len(self.auth_pwd) != 128): - if (len(self.auth_pwd) < 1) or (len(self.auth_pwd) > 16): - self.module.fail_json( - msg='Error: The value is a string of 1 to 16 case-sensitive plaintexts or 24/32/48/108/128 ' - 'case-sensitive encrypted characters.') - - def init_module(self): - """Init module object""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_dldp_exist_config(self): - """Get current dldp existed configuration""" - - dldp_conf = dict() - xml_str = CE_NC_GET_GLOBAL_DLDP_CONFIG - con_obj = get_nc_config(self.module, xml_str) - if "" in con_obj: - return dldp_conf - - xml_str = con_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get global DLDP info - root = ElementTree.fromstring(xml_str) - topo = root.find("dldp/dldpSys") - if not topo: - self.module.fail_json( - msg="Error: Get current DLDP configuration failed.") - - for eles in topo: - if eles.tag in ["dldpEnable", "dldpInterval", "dldpWorkMode", "dldpAuthMode"]: - if eles.tag == 'dldpEnable': - if eles.text == 'true': - value = 'enable' - else: - value = 'disable' - else: - value = eles.text - dldp_conf[eles.tag] = value - - return dldp_conf - - def config_global_dldp(self): - """Config global dldp""" - - if self.same_conf: - return - - enable = self.enable - if not self.enable: - enable = self.dldp_conf['dldpEnable'] - if enable == 'enable': - enable = 'true' - else: - enable = 'false' - - internal = self.internal - if not self.internal: - internal = self.dldp_conf['dldpInterval'] - - work_mode = self.work_mode - if not self.work_mode: - work_mode = self.dldp_conf['dldpWorkMode'] - - if work_mode == 'enhance' or work_mode == 'dldpEnhance': - work_mode = 'dldpEnhance' - else: - work_mode = 'dldpNormal' - - auth_mode = self.auth_mode - if not self.auth_mode: - auth_mode = self.dldp_conf['dldpAuthMode'] - if auth_mode == 'md5': - auth_mode = 'dldpAuthMD5' - elif auth_mode == 'simple': - auth_mode = 'dldpAuthSimple' - elif auth_mode == 'sha': - auth_mode = 'dldpAuthSHA' - elif auth_mode == 'hmac-sha256': - auth_mode = 'dldpAuthHMAC-SHA256' - elif auth_mode == 'none': - auth_mode = 'dldpAuthNone' - - xml_str = CE_NC_MERGE_DLDP_GLOBAL_CONFIG_HEAD % ( - enable, internal, work_mode) - if self.auth_mode: - if self.auth_mode == 'none': - xml_str += "dldpAuthNone" - else: - xml_str += "%s" % auth_mode - xml_str += "%s" % self.auth_pwd - - xml_str += CE_NC_MERGE_DLDP_GLOBAL_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "MERGE_DLDP_GLOBAL_CONFIG") - - if self.reset == 'enable': - xml_str = CE_NC_ACTION_RESET_DLDP - ret_xml = execute_nc_action(self.module, xml_str) - self.check_response(ret_xml, "ACTION_RESET_DLDP") - - self.changed = True - - def get_existing(self): - """Get existing info""" - - dldp_conf = dict() - - dldp_conf['enable'] = self.dldp_conf.get('dldpEnable', None) - dldp_conf['time_interval'] = self.dldp_conf.get('dldpInterval', None) - work_mode = self.dldp_conf.get('dldpWorkMode', None) - if work_mode == 'dldpEnhance': - dldp_conf['work_mode'] = 'enhance' - else: - dldp_conf['work_mode'] = 'normal' - - auth_mode = self.dldp_conf.get('dldpAuthMode', None) - if auth_mode == 'dldpAuthNone': - dldp_conf['auth_mode'] = 'none' - elif auth_mode == 'dldpAuthSimple': - dldp_conf['auth_mode'] = 'simple' - elif auth_mode == 'dldpAuthMD5': - dldp_conf['auth_mode'] = 'md5' - elif auth_mode == 'dldpAuthSHA': - dldp_conf['auth_mode'] = 'sha' - else: - dldp_conf['auth_mode'] = 'hmac-sha256' - - dldp_conf['reset'] = 'disable' - - self.existing = copy.deepcopy(dldp_conf) - - def get_proposed(self): - """Get proposed result""" - - self.proposed = dict(enable=self.enable, work_mode=self.work_mode, - time_interval=self.internal, reset=self.reset, - auth_mode=self.auth_mode, auth_pwd=self.auth_pwd) - - def get_update_cmd(self): - """Get update commands""" - if self.same_conf: - return - - if self.enable and self.enable != self.dldp_conf['dldpEnable']: - if self.enable == 'enable': - self.updates_cmd.append("dldp enable") - elif self.enable == 'disable': - self.updates_cmd.append("undo dldp enable") - return - - work_mode = 'normal' - if self.dldp_conf['dldpWorkMode'] == 'dldpEnhance': - work_mode = 'enhance' - if self.work_mode and self.work_mode != work_mode: - if self.work_mode == 'enhance': - self.updates_cmd.append("dldp work-mode enhance") - else: - self.updates_cmd.append("dldp work-mode normal") - - if self.internal and self.internal != self.dldp_conf['dldpInterval']: - self.updates_cmd.append("dldp interval %s" % self.internal) - - if self.auth_mode: - if self.auth_mode == 'none': - self.updates_cmd.append("undo dldp authentication-mode") - else: - self.updates_cmd.append("dldp authentication-mode %s %s" % (self.auth_mode, self.auth_pwd)) - - if self.reset and self.reset == 'enable': - self.updates_cmd.append('dldp reset') - - def get_end_state(self): - """Get end state info""" - - dldp_conf = dict() - self.dldp_conf = self.get_dldp_exist_config() - - dldp_conf['enable'] = self.dldp_conf.get('dldpEnable', None) - dldp_conf['time_interval'] = self.dldp_conf.get('dldpInterval', None) - work_mode = self.dldp_conf.get('dldpWorkMode', None) - if work_mode == 'dldpEnhance': - dldp_conf['work_mode'] = 'enhance' - else: - dldp_conf['work_mode'] = 'normal' - - auth_mode = self.dldp_conf.get('dldpAuthMode', None) - if auth_mode == 'dldpAuthNone': - dldp_conf['auth_mode'] = 'none' - elif auth_mode == 'dldpAuthSimple': - dldp_conf['auth_mode'] = 'simple' - elif auth_mode == 'dldpAuthMD5': - dldp_conf['auth_mode'] = 'md5' - elif auth_mode == 'dldpAuthSHA': - dldp_conf['auth_mode'] = 'sha' - else: - dldp_conf['auth_mode'] = 'hmac-sha256' - - dldp_conf['reset'] = 'disable' - if self.reset == 'enable': - dldp_conf['reset'] = 'enable' - self.end_state = copy.deepcopy(dldp_conf) - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def work(self): - """Worker""" - - self.dldp_conf = self.get_dldp_exist_config() - self.check_params() - self.same_conf = self.check_config_if_same() - self.get_existing() - self.get_proposed() - self.config_global_dldp() - self.get_update_cmd() - self.get_end_state() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - enable=dict(choices=['enable', 'disable'], type='str'), - work_mode=dict(choices=['enhance', 'normal'], type='str'), - time_interval=dict(type='str'), - reset=dict(choices=['enable', 'disable'], type='str'), - auth_mode=dict(choices=['md5', 'simple', 'sha', 'hmac-sha256', 'none'], type='str'), - auth_pwd=dict(type='str', no_log=True), - ) - argument_spec.update(ce_argument_spec) - dldp_obj = Dldp(argument_spec) - dldp_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_dldp_interface.py b/plugins/modules/network/cloudengine/ce_dldp_interface.py deleted file mode 100644 index 3efdd21a3b..0000000000 --- a/plugins/modules/network/cloudengine/ce_dldp_interface.py +++ /dev/null @@ -1,662 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- - -module: ce_dldp_interface -short_description: Manages interface DLDP configuration on HUAWEI CloudEngine switches. -description: - - Manages interface DLDP configuration on HUAWEI CloudEngine switches. -author: - - Zhou Zhijin (@QijunPan) -notes: - - If C(state=present, enable=disable), interface DLDP enable will be turned off and - related interface DLDP configuration will be cleared. - - If C(state=absent), only local_mac is supported to configure. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - interface: - description: - - Must be fully qualified interface name, i.e. GE1/0/1, 10GE1/0/1, 40GE1/0/22, 100GE1/0/1. - required: true - enable: - description: - - Set interface DLDP enable state. - choices: ['enable', 'disable'] - mode_enable: - description: - - Set DLDP compatible-mode enable state. - choices: ['enable', 'disable'] - local_mac: - description: - - Set the source MAC address for DLDP packets sent in the DLDP-compatible mode. - The value of MAC address is in H-H-H format. H contains 1 to 4 hexadecimal digits. - reset: - description: - - Specify whether reseting interface DLDP state. - choices: ['enable', 'disable'] - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: DLDP interface test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Configure interface DLDP enable state and ensure global dldp enable is turned on" - ce_dldp_interface: - interface: 40GE2/0/1 - enable: enable - provider: "{{ cli }}" - - - name: "Configuire interface DLDP compatible-mode enable state and ensure interface DLDP state is already enabled" - ce_dldp_interface: - interface: 40GE2/0/1 - enable: enable - mode_enable: enable - provider: "{{ cli }}" - - - name: "Configuire the source MAC address for DLDP packets sent in the DLDP-compatible mode and - ensure interface DLDP state and compatible-mode enable state is already enabled" - ce_dldp_interface: - interface: 40GE2/0/1 - enable: enable - mode_enable: enable - local_mac: aa-aa-aa - provider: "{{ cli }}" - - - name: "Reset DLDP state of specified interface and ensure interface DLDP state is already enabled" - ce_dldp_interface: - interface: 40GE2/0/1 - enable: enable - reset: enable - provider: "{{ cli }}" - - - name: "Unconfigure interface DLDP local mac address when C(state=absent)" - ce_dldp_interface: - interface: 40GE2/0/1 - state: absent - local_mac: aa-aa-aa - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "enable": "enalbe", - "interface": "40GE2/0/22", - "local_mac": "aa-aa-aa", - "mode_enable": "enable", - "reset": "enable" - } -existing: - description: k/v pairs of existing interface DLDP configuration - returned: always - type: dict - sample: { - "enable": "disable", - "interface": "40GE2/0/22", - "local_mac": null, - "mode_enable": null, - "reset": "disable" - } -end_state: - description: k/v pairs of interface DLDP configuration after module execution - returned: always - type: dict - sample: { - "enable": "enable", - "interface": "40GE2/0/22", - "local_mac": "00aa-00aa-00aa", - "mode_enable": "enable", - "reset": "enable" - } -updates: - description: command sent to the device - returned: always - type: list - sample: [ - "dldp enable", - "dldp compatible-mode enable", - "dldp compatible-mode local-mac aa-aa-aa", - "dldp reset" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import copy -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, set_nc_config, get_nc_config, execute_nc_action - - -CE_NC_ACTION_RESET_INTF_DLDP = """ - - - - %s - - - -""" - -CE_NC_GET_INTF_DLDP_CONFIG = """ - - - - - %s - - - - - - - -""" - -CE_NC_MERGE_DLDP_INTF_CONFIG = """ - - - - - %s - %s - %s - %s - - - - -""" - -CE_NC_CREATE_DLDP_INTF_CONFIG = """ - - - - - %s - %s - %s - %s - - - - -""" - -CE_NC_DELETE_DLDP_INTF_CONFIG = """ - - - - - %s - - - - -""" - - -def judge_is_mac_same(mac1, mac2): - """Judge whether two macs are the same""" - - if mac1 == mac2: - return True - - list1 = re.findall(r'([0-9A-Fa-f]+)', mac1) - list2 = re.findall(r'([0-9A-Fa-f]+)', mac2) - if len(list1) != len(list2): - return False - - for index, value in enumerate(list1, start=0): - if value.lstrip('0').lower() != list2[index].lstrip('0').lower(): - return False - - return True - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-Port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class DldpInterface(object): - """Manage interface dldp configuration""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # DLDP interface configuration info - self.interface = self.module.params['interface'] - self.enable = self.module.params['enable'] or None - self.reset = self.module.params['reset'] or None - self.mode_enable = self.module.params['mode_enable'] or None - self.local_mac = self.module.params['local_mac'] or None - self.state = self.module.params['state'] - - self.dldp_intf_conf = dict() - self.same_conf = False - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = list() - self.end_state = list() - - def check_config_if_same(self): - """Judge whether current config is the same as what we excepted""" - - if self.state == 'absent': - return False - else: - if self.enable and self.enable != self.dldp_intf_conf['dldpEnable']: - return False - - if self.mode_enable and self.mode_enable != self.dldp_intf_conf['dldpCompatibleEnable']: - return False - - if self.local_mac: - flag = judge_is_mac_same( - self.local_mac, self.dldp_intf_conf['dldpLocalMac']) - if not flag: - return False - - if self.reset and self.reset == 'enable': - return False - return True - - def check_macaddr(self): - """Check mac-address whether valid""" - - valid_char = '0123456789abcdef-' - mac = self.local_mac - - if len(mac) > 16: - return False - - mac_list = re.findall(r'([0-9a-fA-F]+)', mac) - if len(mac_list) != 3: - return False - - if mac.count('-') != 2: - return False - - for _, value in enumerate(mac, start=0): - if value.lower() not in valid_char: - return False - - return True - - def check_params(self): - """Check all input params""" - - if not self.interface: - self.module.fail_json(msg='Error: Interface name cannot be empty.') - - if self.interface: - intf_type = get_interface_type(self.interface) - if not intf_type: - self.module.fail_json( - msg='Error: Interface name of %s ' - 'is error.' % self.interface) - - if (self.state == 'absent') and (self.reset or self.mode_enable or self.enable): - self.module.fail_json(msg="Error: It's better to use state=present when " - "configuring or unconfiguring enable, mode_enable " - "or using reset flag. state=absent is just for " - "when using local_mac param.") - - if self.state == 'absent' and not self.local_mac: - self.module.fail_json( - msg="Error: Please specify local_mac parameter.") - - if self.state == 'present': - if (self.dldp_intf_conf['dldpEnable'] == 'disable' and not self.enable and - (self.mode_enable or self.local_mac or self.reset)): - self.module.fail_json(msg="Error: when DLDP is already disabled on this port, " - "mode_enable, local_mac and reset parameters are not " - "expected to configure.") - - if self.enable == 'disable' and (self.mode_enable or self.local_mac or self.reset): - self.module.fail_json(msg="Error: when using enable=disable, " - "mode_enable, local_mac and reset parameters " - "are not expected to configure.") - - if self.local_mac and (self.mode_enable == 'disable' or - (self.dldp_intf_conf['dldpCompatibleEnable'] == 'disable' and self.mode_enable != 'enable')): - self.module.fail_json(msg="Error: when DLDP compatible-mode is disabled on this port, " - "Configuring local_mac is not allowed.") - - if self.local_mac: - if not self.check_macaddr(): - self.module.fail_json( - msg="Error: local_mac has invalid value %s." % self.local_mac) - - def init_module(self): - """Init module object""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_dldp_intf_exist_config(self): - """Get current dldp existed config""" - - dldp_conf = dict() - xml_str = CE_NC_GET_INTF_DLDP_CONFIG % self.interface - con_obj = get_nc_config(self.module, xml_str) - if "" in con_obj: - dldp_conf['dldpEnable'] = 'disable' - dldp_conf['dldpCompatibleEnable'] = "" - dldp_conf['dldpLocalMac'] = "" - return dldp_conf - - xml_str = con_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get global DLDP info - root = ElementTree.fromstring(xml_str) - topo = root.find("dldp/dldpInterfaces/dldpInterface") - if topo is None: - self.module.fail_json( - msg="Error: Get current DLDP configuration failed.") - for eles in topo: - if eles.tag in ["dldpEnable", "dldpCompatibleEnable", "dldpLocalMac"]: - if not eles.text: - dldp_conf[eles.tag] = "" - else: - if eles.tag == "dldpEnable" or eles.tag == "dldpCompatibleEnable": - if eles.text == 'true': - value = 'enable' - else: - value = 'disable' - else: - value = eles.text - dldp_conf[eles.tag] = value - - return dldp_conf - - def config_intf_dldp(self): - """Config global dldp""" - - if self.same_conf: - return - - if self.state == "present": - enable = self.enable - if not self.enable: - enable = self.dldp_intf_conf['dldpEnable'] - if enable == 'enable': - enable = 'true' - else: - enable = 'false' - - mode_enable = self.mode_enable - if not self.mode_enable: - mode_enable = self.dldp_intf_conf['dldpCompatibleEnable'] - if mode_enable == 'enable': - mode_enable = 'true' - else: - mode_enable = 'false' - - local_mac = self.local_mac - if not self.local_mac: - local_mac = self.dldp_intf_conf['dldpLocalMac'] - - if self.enable == 'disable' and self.enable != self.dldp_intf_conf['dldpEnable']: - xml_str = CE_NC_DELETE_DLDP_INTF_CONFIG % self.interface - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "DELETE_DLDP_INTF_CONFIG") - elif self.dldp_intf_conf['dldpEnable'] == 'disable' and self.enable == 'enable': - xml_str = CE_NC_CREATE_DLDP_INTF_CONFIG % ( - self.interface, 'true', mode_enable, local_mac) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "CREATE_DLDP_INTF_CONFIG") - elif self.dldp_intf_conf['dldpEnable'] == 'enable': - if mode_enable == 'false': - local_mac = '' - xml_str = CE_NC_MERGE_DLDP_INTF_CONFIG % ( - self.interface, enable, mode_enable, local_mac) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "MERGE_DLDP_INTF_CONFIG") - - if self.reset == 'enable': - xml_str = CE_NC_ACTION_RESET_INTF_DLDP % self.interface - ret_xml = execute_nc_action(self.module, xml_str) - self.check_response(ret_xml, "ACTION_RESET_INTF_DLDP") - - self.changed = True - else: - if self.local_mac and judge_is_mac_same(self.local_mac, self.dldp_intf_conf['dldpLocalMac']): - if self.dldp_intf_conf['dldpEnable'] == 'enable': - dldp_enable = 'true' - else: - dldp_enable = 'false' - if self.dldp_intf_conf['dldpCompatibleEnable'] == 'enable': - dldp_compat_enable = 'true' - else: - dldp_compat_enable = 'false' - xml_str = CE_NC_MERGE_DLDP_INTF_CONFIG % (self.interface, dldp_enable, dldp_compat_enable, '') - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "UNDO_DLDP_INTF_LOCAL_MAC_CONFIG") - self.changed = True - - def get_existing(self): - """Get existing info""" - - dldp_conf = dict() - - dldp_conf['interface'] = self.interface - dldp_conf['enable'] = self.dldp_intf_conf.get('dldpEnable', None) - dldp_conf['mode_enable'] = self.dldp_intf_conf.get( - 'dldpCompatibleEnable', None) - dldp_conf['local_mac'] = self.dldp_intf_conf.get('dldpLocalMac', None) - dldp_conf['reset'] = 'disable' - - self.existing = copy.deepcopy(dldp_conf) - - def get_proposed(self): - """Get proposed result """ - - self.proposed = dict(interface=self.interface, enable=self.enable, - mode_enable=self.mode_enable, local_mac=self.local_mac, - reset=self.reset, state=self.state) - - def get_update_cmd(self): - """Get updated commands""" - - if self.same_conf: - return - - if self.state == "present": - if self.enable and self.enable != self.dldp_intf_conf['dldpEnable']: - if self.enable == 'enable': - self.updates_cmd.append("dldp enable") - elif self.enable == 'disable': - self.updates_cmd.append("undo dldp enable") - - if self.mode_enable and self.mode_enable != self.dldp_intf_conf['dldpCompatibleEnable']: - if self.mode_enable == 'enable': - self.updates_cmd.append("dldp compatible-mode enable") - else: - self.updates_cmd.append("undo dldp compatible-mode enable") - - if self.local_mac: - flag = judge_is_mac_same( - self.local_mac, self.dldp_intf_conf['dldpLocalMac']) - if not flag: - self.updates_cmd.append( - "dldp compatible-mode local-mac %s" % self.local_mac) - - if self.reset and self.reset == 'enable': - self.updates_cmd.append('dldp reset') - else: - if self.changed: - self.updates_cmd.append("undo dldp compatible-mode local-mac") - - def get_end_state(self): - """Get end state info""" - - dldp_conf = dict() - self.dldp_intf_conf = self.get_dldp_intf_exist_config() - - dldp_conf['interface'] = self.interface - dldp_conf['enable'] = self.dldp_intf_conf.get('dldpEnable', None) - dldp_conf['mode_enable'] = self.dldp_intf_conf.get( - 'dldpCompatibleEnable', None) - dldp_conf['local_mac'] = self.dldp_intf_conf.get('dldpLocalMac', None) - dldp_conf['reset'] = 'disable' - if self.reset == 'enable': - dldp_conf['reset'] = 'enable' - - self.end_state = copy.deepcopy(dldp_conf) - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def work(self): - """Execute task""" - - self.dldp_intf_conf = self.get_dldp_intf_exist_config() - self.check_params() - self.same_conf = self.check_config_if_same() - self.get_existing() - self.get_proposed() - self.config_intf_dldp() - self.get_update_cmd() - self.get_end_state() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - interface=dict(required=True, type='str'), - enable=dict(choices=['enable', 'disable'], type='str'), - reset=dict(choices=['enable', 'disable'], type='str'), - mode_enable=dict(choices=['enable', 'disable'], type='str'), - local_mac=dict(type='str'), - state=dict(choices=['absent', 'present'], default='present'), - ) - argument_spec.update(ce_argument_spec) - dldp_intf_obj = DldpInterface(argument_spec) - dldp_intf_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_eth_trunk.py b/plugins/modules/network/cloudengine/ce_eth_trunk.py deleted file mode 100644 index 3f2dde7ec4..0000000000 --- a/plugins/modules/network/cloudengine/ce_eth_trunk.py +++ /dev/null @@ -1,676 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_eth_trunk -short_description: Manages Eth-Trunk interfaces on HUAWEI CloudEngine switches. -description: - - Manages Eth-Trunk specific configuration parameters on HUAWEI CloudEngine switches. -author: QijunPan (@QijunPan) -notes: - - C(state=absent) removes the Eth-Trunk config and interface if it - already exists. If members to be removed are not explicitly - passed, all existing members (if any), are removed, - and Eth-Trunk removed. - - Members must be a list. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - trunk_id: - description: - - Eth-Trunk interface number. - The value is an integer. - The value range depends on the assign forward eth-trunk mode command. - When 256 is specified, the value ranges from 0 to 255. - When 512 is specified, the value ranges from 0 to 511. - When 1024 is specified, the value ranges from 0 to 1023. - required: true - mode: - description: - - Specifies the working mode of an Eth-Trunk interface. - choices: ['manual','lacp-dynamic','lacp-static'] - min_links: - description: - - Specifies the minimum number of Eth-Trunk member links in the Up state. - The value is an integer ranging from 1 to the maximum number of interfaces - that can be added to a Eth-Trunk interface. - hash_type: - description: - - Hash algorithm used for load balancing among Eth-Trunk member interfaces. - choices: ['src-dst-ip', 'src-dst-mac', 'enhanced', 'dst-ip', 'dst-mac', 'src-ip', 'src-mac'] - members: - description: - - List of interfaces that will be managed in a given Eth-Trunk. - The interface name must be full name. - force: - description: - - When true it forces Eth-Trunk members to match what is - declared in the members param. This can be used to remove - members. - type: bool - default: 'no' - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' -EXAMPLES = ''' -- name: eth_trunk module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Ensure Eth-Trunk100 is created, add two members, and set to mode lacp-static - ce_eth_trunk: - trunk_id: 100 - members: ['10GE1/0/24','10GE1/0/25'] - mode: 'lacp-static' - state: present - provider: '{{ cli }}' -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"trunk_id": "100", "members": ['10GE1/0/24','10GE1/0/25'], "mode": "lacp-static"} -existing: - description: k/v pairs of existing Eth-Trunk - returned: always - type: dict - sample: {"trunk_id": "100", "hash_type": "mac", "members_detail": [ - {"memberIfName": "10GE1/0/25", "memberIfState": "Down"}], - "min_links": "1", "mode": "manual"} -end_state: - description: k/v pairs of Eth-Trunk info after module execution - returned: always - type: dict - sample: {"trunk_id": "100", "hash_type": "mac", "members_detail": [ - {"memberIfName": "10GE1/0/24", "memberIfState": "Down"}, - {"memberIfName": "10GE1/0/25", "memberIfState": "Down"}], - "min_links": "1", "mode": "lacp-static"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["interface Eth-Trunk 100", - "mode lacp-static", - "interface 10GE1/0/25", - "eth-trunk 100"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_TRUNK = """ - - - - - Eth-Trunk%s - - - - - - - - - - - - - - - - - -""" - -CE_NC_XML_BUILD_TRUNK_CFG = """ - - - %s - - -""" - -CE_NC_XML_DELETE_TRUNK = """ - - Eth-Trunk%s - -""" - -CE_NC_XML_CREATE_TRUNK = """ - - Eth-Trunk%s - -""" - -CE_NC_XML_MERGE_MINUPNUM = """ - - Eth-Trunk%s - %s - -""" - -CE_NC_XML_MERGE_HASHTYPE = """ - - Eth-Trunk%s - %s - -""" - -CE_NC_XML_MERGE_WORKMODE = """ - - Eth-Trunk%s - %s - -""" - -CE_NC_XML_BUILD_MEMBER_CFG = """ - - Eth-Trunk%s - %s - -""" - -CE_NC_XML_MERGE_MEMBER = """ - - %s - -""" - -CE_NC_XML_DELETE_MEMBER = """ - - %s - -""" - -MODE_XML2CLI = {"Manual": "manual", "Dynamic": "lacp-dynamic", "Static": "lacp-static"} -MODE_CLI2XML = {"manual": "Manual", "lacp-dynamic": "Dynamic", "lacp-static": "Static"} -HASH_XML2CLI = {"IP": "src-dst-ip", "MAC": "src-dst-mac", "Enhanced": "enhanced", - "Desip": "dst-ip", "Desmac": "dst-mac", "Sourceip": "src-ip", "Sourcemac": "src-mac"} -HASH_CLI2XML = {"src-dst-ip": "IP", "src-dst-mac": "MAC", "enhanced": "Enhanced", - "dst-ip": "Desip", "dst-mac": "Desmac", "src-ip": "Sourceip", "src-mac": "Sourcemac"} - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -def mode_xml_to_cli_str(mode): - """convert mode to cli format string""" - - if not mode: - return "" - - return MODE_XML2CLI.get(mode) - - -def hash_type_xml_to_cli_str(hash_type): - """convert trunk hash type netconf xml to cli format string""" - - if not hash_type: - return "" - - return HASH_XML2CLI.get(hash_type) - - -class EthTrunk(object): - """ - Manages Eth-Trunk interfaces. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.trunk_id = self.module.params['trunk_id'] - self.mode = self.module.params['mode'] - self.min_links = self.module.params['min_links'] - self.hash_type = self.module.params['hash_type'] - self.members = self.module.params['members'] - self.state = self.module.params['state'] - self.force = self.module.params['force'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - # interface info - self.trunk_info = dict() - - def __init_module__(self): - """ init module """ - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def netconf_set_config(self, xml_str, xml_name): - """ netconf set config """ - - recv_xml = set_nc_config(self.module, xml_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_trunk_dict(self, trunk_id): - """ get one interface attributes dict.""" - - trunk_info = dict() - conf_str = CE_NC_GET_TRUNK % trunk_id - recv_xml = get_nc_config(self.module, conf_str) - - if "" in recv_xml: - return trunk_info - - # get trunk base info - base = re.findall( - r'.*(.*).*\s*' - r'(.*).*\s*' - r'(.*).*\s*' - r'(.*).*\s*' - r'(.*).*\s*' - r'(.*).*\s*' - r'(.*).*\s*' - r'(.*).*', recv_xml) - - if base: - trunk_info = dict(ifName=base[0][0], - trunkId=base[0][0].lower().replace("eth-trunk", "").replace(" ", ""), - minUpNum=base[0][1], - maxUpNum=base[0][2], - trunkType=base[0][3], - hashType=base[0][4], - workMode=base[0][5], - upMemberIfNum=base[0][6], - memberIfNum=base[0][7]) - - # get trunk member interface info - member = re.findall( - r'.*(.*).*\s*' - r'(.*).*', recv_xml) - trunk_info["TrunkMemberIfs"] = list() - - for mem in member: - trunk_info["TrunkMemberIfs"].append( - dict(memberIfName=mem[0], memberIfState=mem[1])) - - return trunk_info - - def is_member_exist(self, ifname): - """is trunk member exist""" - - if not self.trunk_info["TrunkMemberIfs"]: - return False - - for mem in self.trunk_info["TrunkMemberIfs"]: - if ifname.replace(" ", "").upper() == mem["memberIfName"].replace(" ", "").upper(): - return True - - return False - - def get_mode_xml_str(self): - """trunk mode netconf xml format string""" - - return MODE_CLI2XML.get(self.mode) - - def get_hash_type_xml_str(self): - """trunk hash type netconf xml format string""" - - return HASH_CLI2XML.get(self.hash_type) - - def create_eth_trunk(self): - """Create Eth-Trunk interface""" - - xml_str = CE_NC_XML_CREATE_TRUNK % self.trunk_id - self.updates_cmd.append("interface Eth-Trunk %s" % self.trunk_id) - - if self.hash_type: - self.updates_cmd.append("load-balance %s" % self.hash_type) - xml_str += CE_NC_XML_MERGE_HASHTYPE % (self.trunk_id, self.get_hash_type_xml_str()) - - if self.mode: - self.updates_cmd.append("mode %s" % self.mode) - xml_str += CE_NC_XML_MERGE_WORKMODE % (self.trunk_id, self.get_mode_xml_str()) - - if self.min_links: - self.updates_cmd.append("least active-linknumber %s" % self.min_links) - xml_str += CE_NC_XML_MERGE_MINUPNUM % (self.trunk_id, self.min_links) - - if self.members: - mem_xml = "" - for mem in self.members: - mem_xml += CE_NC_XML_MERGE_MEMBER % mem.upper() - self.updates_cmd.append("interface %s" % mem) - self.updates_cmd.append("eth-trunk %s" % self.trunk_id) - xml_str += CE_NC_XML_BUILD_MEMBER_CFG % (self.trunk_id, mem_xml) - cfg_xml = CE_NC_XML_BUILD_TRUNK_CFG % xml_str - self.netconf_set_config(cfg_xml, "CREATE_TRUNK") - self.changed = True - - def delete_eth_trunk(self): - """Delete Eth-Trunk interface and remove all member""" - - if not self.trunk_info: - return - - xml_str = "" - mem_str = "" - if self.trunk_info["TrunkMemberIfs"]: - for mem in self.trunk_info["TrunkMemberIfs"]: - mem_str += CE_NC_XML_DELETE_MEMBER % mem["memberIfName"] - self.updates_cmd.append("interface %s" % mem["memberIfName"]) - self.updates_cmd.append("undo eth-trunk") - if mem_str: - xml_str += CE_NC_XML_BUILD_MEMBER_CFG % (self.trunk_id, mem_str) - - xml_str += CE_NC_XML_DELETE_TRUNK % self.trunk_id - self.updates_cmd.append("undo interface Eth-Trunk %s" % self.trunk_id) - cfg_xml = CE_NC_XML_BUILD_TRUNK_CFG % xml_str - self.netconf_set_config(cfg_xml, "DELETE_TRUNK") - self.changed = True - - def remove_member(self): - """delete trunk member""" - - if not self.members: - return - - change = False - mem_xml = "" - xml_str = "" - for mem in self.members: - if self.is_member_exist(mem): - mem_xml += CE_NC_XML_DELETE_MEMBER % mem.upper() - self.updates_cmd.append("interface %s" % mem) - self.updates_cmd.append("undo eth-trunk") - if mem_xml: - xml_str += CE_NC_XML_BUILD_MEMBER_CFG % (self.trunk_id, mem_xml) - change = True - - if not change: - return - - cfg_xml = CE_NC_XML_BUILD_TRUNK_CFG % xml_str - self.netconf_set_config(cfg_xml, "REMOVE_TRUNK_MEMBER") - self.changed = True - - def merge_eth_trunk(self): - """Create or merge Eth-Trunk""" - - change = False - xml_str = "" - self.updates_cmd.append("interface Eth-Trunk %s" % self.trunk_id) - if self.hash_type and self.get_hash_type_xml_str() != self.trunk_info["hashType"]: - self.updates_cmd.append("load-balance %s" % - self.hash_type) - xml_str += CE_NC_XML_MERGE_HASHTYPE % ( - self.trunk_id, self.get_hash_type_xml_str()) - change = True - if self.min_links and self.min_links != self.trunk_info["minUpNum"]: - self.updates_cmd.append( - "least active-linknumber %s" % self.min_links) - xml_str += CE_NC_XML_MERGE_MINUPNUM % ( - self.trunk_id, self.min_links) - change = True - if self.mode and self.get_mode_xml_str() != self.trunk_info["workMode"]: - self.updates_cmd.append("mode %s" % self.mode) - xml_str += CE_NC_XML_MERGE_WORKMODE % ( - self.trunk_id, self.get_mode_xml_str()) - change = True - - if not change: - self.updates_cmd.pop() # remove 'interface Eth-Trunk' command - - # deal force: - # When true it forces Eth-Trunk members to match - # what is declared in the members param. - if self.force and self.trunk_info["TrunkMemberIfs"]: - mem_xml = "" - for mem in self.trunk_info["TrunkMemberIfs"]: - if not self.members or mem["memberIfName"].replace(" ", "").upper() not in self.members: - mem_xml += CE_NC_XML_DELETE_MEMBER % mem["memberIfName"] - self.updates_cmd.append("interface %s" % mem["memberIfName"]) - self.updates_cmd.append("undo eth-trunk") - if mem_xml: - xml_str += CE_NC_XML_BUILD_MEMBER_CFG % (self.trunk_id, mem_xml) - change = True - - if self.members: - mem_xml = "" - for mem in self.members: - if not self.is_member_exist(mem): - mem_xml += CE_NC_XML_MERGE_MEMBER % mem.upper() - self.updates_cmd.append("interface %s" % mem) - self.updates_cmd.append("eth-trunk %s" % self.trunk_id) - if mem_xml: - xml_str += CE_NC_XML_BUILD_MEMBER_CFG % ( - self.trunk_id, mem_xml) - change = True - - if not change: - return - - cfg_xml = CE_NC_XML_BUILD_TRUNK_CFG % xml_str - self.netconf_set_config(cfg_xml, "MERGE_TRUNK") - self.changed = True - - def check_params(self): - """Check all input params""" - - # trunk_id check - if not self.trunk_id.isdigit(): - self.module.fail_json(msg='The parameter of trunk_id is invalid.') - - # min_links check - if self.min_links and not self.min_links.isdigit(): - self.module.fail_json(msg='The parameter of min_links is invalid.') - - # members check and convert members to upper - if self.members: - for mem in self.members: - if not get_interface_type(mem.replace(" ", "")): - self.module.fail_json( - msg='The parameter of members is invalid.') - - for mem_id in range(len(self.members)): - self.members[mem_id] = self.members[mem_id].replace(" ", "").upper() - - def get_proposed(self): - """get proposed info""" - - self.proposed["trunk_id"] = self.trunk_id - self.proposed["mode"] = self.mode - if self.min_links: - self.proposed["min_links"] = self.min_links - self.proposed["hash_type"] = self.hash_type - if self.members: - self.proposed["members"] = self.members - self.proposed["state"] = self.state - self.proposed["force"] = self.force - - def get_existing(self): - """get existing info""" - - if not self.trunk_info: - return - - self.existing["trunk_id"] = self.trunk_info["trunkId"] - self.existing["min_links"] = self.trunk_info["minUpNum"] - self.existing["hash_type"] = hash_type_xml_to_cli_str(self.trunk_info["hashType"]) - self.existing["mode"] = mode_xml_to_cli_str(self.trunk_info["workMode"]) - self.existing["members_detail"] = self.trunk_info["TrunkMemberIfs"] - - def get_end_state(self): - """get end state info""" - - trunk_info = self.get_trunk_dict(self.trunk_id) - if not trunk_info: - return - - self.end_state["trunk_id"] = trunk_info["trunkId"] - self.end_state["min_links"] = trunk_info["minUpNum"] - self.end_state["hash_type"] = hash_type_xml_to_cli_str(trunk_info["hashType"]) - self.end_state["mode"] = mode_xml_to_cli_str(trunk_info["workMode"]) - self.end_state["members_detail"] = trunk_info["TrunkMemberIfs"] - - def work(self): - """worker""" - - self.check_params() - self.trunk_info = self.get_trunk_dict(self.trunk_id) - self.get_existing() - self.get_proposed() - - # deal present or absent - if self.state == "present": - if not self.trunk_info: - # create - self.create_eth_trunk() - else: - # merge trunk - self.merge_eth_trunk() - else: - if self.trunk_info: - if not self.members: - # remove all members and delete trunk - self.delete_eth_trunk() - else: - # remove some trunk members - self.remove_member() - else: - self.module.fail_json(msg='Error: Eth-Trunk does not exist.') - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - trunk_id=dict(required=True), - mode=dict(required=False, - choices=['manual', 'lacp-dynamic', 'lacp-static'], - type='str'), - min_links=dict(required=False, type='str'), - hash_type=dict(required=False, - choices=['src-dst-ip', 'src-dst-mac', 'enhanced', - 'dst-ip', 'dst-mac', 'src-ip', 'src-mac'], - type='str'), - members=dict(required=False, default=None, type='list'), - force=dict(required=False, default=False, type='bool'), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - - argument_spec.update(ce_argument_spec) - module = EthTrunk(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_evpn_bd_vni.py b/plugins/modules/network/cloudengine/ce_evpn_bd_vni.py deleted file mode 100644 index caf396d912..0000000000 --- a/plugins/modules/network/cloudengine/ce_evpn_bd_vni.py +++ /dev/null @@ -1,1057 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_evpn_bd_vni -short_description: Manages EVPN VXLAN Network Identifier (VNI) on HUAWEI CloudEngine switches. -description: - - Manages Ethernet Virtual Private Network (EVPN) VXLAN Network - Identifier (VNI) configurations on HUAWEI CloudEngine switches. -author: Zhijin Zhou (@QijunPan) -notes: - - Ensure that EVPN has been configured to serve as the VXLAN control plane when state is present. - - Ensure that a bridge domain (BD) has existed when state is present. - - Ensure that a VNI has been created and associated with a broadcast domain (BD) when state is present. - - If you configure evpn:false to delete an EVPN instance, all configurations in the EVPN instance are deleted. - - After an EVPN instance has been created in the BD view, you can configure an RD using route_distinguisher - parameter in BD-EVPN instance view. - - Before configuring VPN targets for a BD EVPN instance, ensure that an RD has been configured - for the BD EVPN instance - - If you unconfigure route_distinguisher, all VPN target attributes for the BD EVPN instance will be removed at the same time. - - When using state:absent, evpn is not supported and it will be ignored. - - When using state:absent to delete VPN target attributes, ensure the configuration of VPN target attributes has - existed and otherwise it will report an error. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - bridge_domain_id: - description: - - Specify an existed bridge domain (BD).The value is an integer ranging from 1 to 16777215. - required: true - evpn: - description: - - Create or delete an EVPN instance for a VXLAN in BD view. - choices: ['enable','disable'] - default: 'enable' - route_distinguisher: - description: - - Configures a route distinguisher (RD) for a BD EVPN instance. - The format of an RD can be as follows - - 1) 2-byte AS number:4-byte user-defined number, for example, 1:3. An AS number is an integer ranging from - 0 to 65535, and a user-defined number is an integer ranging from 0 to 4294967295. The AS and user-defined - numbers cannot be both 0s. This means that an RD cannot be 0:0. - - 2) Integral 4-byte AS number:2-byte user-defined number, for example, 65537:3. An AS number is an integer - ranging from 65536 to 4294967295, and a user-defined number is an integer ranging from 0 to 65535. - - 3) 4-byte AS number in dotted notation:2-byte user-defined number, for example, 0.0:3 or 0.1:0. A 4-byte - AS number in dotted notation is in the format of x.y, where x and y are integers ranging from 0 to 65535. - - 4) A user-defined number is an integer ranging from 0 to 65535. The AS and user-defined numbers cannot be - both 0s. This means that an RD cannot be 0.0:0. - - 5) 32-bit IP address:2-byte user-defined number. For example, 192.168.122.15:1. An IP address ranges from - 0.0.0.0 to 255.255.255.255, and a user-defined number is an integer ranging from 0 to 65535. - - 6) 'auto' specifies the RD that is automatically generated. - vpn_target_both: - description: - - Add VPN targets to both the import and export VPN target lists of a BD EVPN instance. - The format is the same as route_distinguisher. - vpn_target_import: - description: - - Add VPN targets to the import VPN target list of a BD EVPN instance. - The format is the same as route_distinguisher. - required: true - vpn_target_export: - description: - - Add VPN targets to the export VPN target list of a BD EVPN instance. - The format is the same as route_distinguisher. - state: - description: - - Manage the state of the resource. - choices: ['present','absent'] - default: 'present' -''' - -EXAMPLES = ''' -- name: EVPN BD VNI test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Configure an EVPN instance for a VXLAN in BD view" - ce_evpn_bd_vni: - bridge_domain_id: 20 - evpn: enable - provider: "{{ cli }}" - - - name: "Configure a route distinguisher (RD) for a BD EVPN instance" - ce_evpn_bd_vni: - bridge_domain_id: 20 - route_distinguisher: '22:22' - provider: "{{ cli }}" - - - name: "Configure VPN targets to both the import and export VPN target lists of a BD EVPN instance" - ce_evpn_bd_vni: - bridge_domain_id: 20 - vpn_target_both: 22:100,22:101 - provider: "{{ cli }}" - - - name: "Configure VPN targets to the import VPN target list of a BD EVPN instance" - ce_evpn_bd_vni: - bridge_domain_id: 20 - vpn_target_import: 22:22,22:23 - provider: "{{ cli }}" - - - name: "Configure VPN targets to the export VPN target list of a BD EVPN instance" - ce_evpn_bd_vni: - bridge_domain_id: 20 - vpn_target_export: 22:38,22:39 - provider: "{{ cli }}" - - - name: "Unconfigure VPN targets to both the import and export VPN target lists of a BD EVPN instance" - ce_evpn_bd_vni: - bridge_domain_id: 20 - vpn_target_both: '22:100' - state: absent - provider: "{{ cli }}" - - - name: "Unconfigure VPN targets to the import VPN target list of a BD EVPN instance" - ce_evpn_bd_vni: - bridge_domain_id: 20 - vpn_target_import: '22:22' - state: absent - provider: "{{ cli }}" - - - name: "Unconfigure VPN targets to the export VPN target list of a BD EVPN instance" - ce_evpn_bd_vni: - bridge_domain_id: 20 - vpn_target_export: '22:38' - state: absent - provider: "{{ cli }}" - - - name: "Unconfigure a route distinguisher (RD) of a BD EVPN instance" - ce_evpn_bd_vni: - bridge_domain_id: 20 - route_distinguisher: '22:22' - state: absent - provider: "{{ cli }}" - - - name: "Unconfigure an EVPN instance for a VXLAN in BD view" - ce_evpn_bd_vni: - bridge_domain_id: 20 - evpn: disable - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "bridge_domain_id": "2", - "evpn": "enable", - "route_distinguisher": "22:22", - "state": "present", - "vpn_target_both": [ - "22:100", - "22:101" - ], - "vpn_target_export": [ - "22:38", - "22:39" - ], - "vpn_target_import": [ - "22:22", - "22:23" - ] - } -existing: - description: k/v pairs of existing attributes on the device - returned: always - type: dict - sample: { - "bridge_domain_id": "2", - "evpn": "disable", - "route_distinguisher": null, - "vpn_target_both": [], - "vpn_target_export": [], - "vpn_target_import": [] - } -end_state: - description: k/v pairs of end attributes on the device - returned: always - type: dict - sample: { - "bridge_domain_id": "2", - "evpn": "enable", - "route_distinguisher": "22:22", - "vpn_target_both": [ - "22:100", - "22:101" - ], - "vpn_target_export": [ - "22:38", - "22:39" - ], - "vpn_target_import": [ - "22:22", - "22:23" - ] - } -updates: - description: command list sent to the device - returned: always - type: list - sample: [ - "bridge-domain 2", - " evpn", - " route-distinguisher 22:22", - " vpn-target 22:38 export-extcommunity", - " vpn-target 22:39 export-extcommunity", - " vpn-target 22:100 export-extcommunity", - " vpn-target 22:101 export-extcommunity", - " vpn-target 22:22 import-extcommunity", - " vpn-target 22:23 import-extcommunity", - " vpn-target 22:100 import-extcommunity", - " vpn-target 22:101 import-extcommunity" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -import copy -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_VNI_BD = """ - - - - - - - - - - -""" - -CE_NC_GET_EVPN_CONFIG = """ - - - - - %s - %s - - - - - - - - - - - - - - - - - -""" - -CE_NC_DELETE_EVPN_CONFIG = """ - - - - - %s - %s - - - - -""" - -CE_NC_DELETE_EVPN_CONFIG_HEAD = """ - - - - - %s - %s -""" - -CE_NC_MERGE_EVPN_CONFIG_HEAD = """ - - - - - %s - %s -""" - -CE_NC_MERGE_EVPN_AUTORTS_HEAD = """ - -""" - -CE_NC_MERGE_EVPN_AUTORTS_TAIL = """ - -""" - -CE_NC_DELETE_EVPN_AUTORTS_CONTEXT = """ - - %s - -""" - -CE_NC_MERGE_EVPN_AUTORTS_CONTEXT = """ - - %s - -""" - -CE_NC_MERGE_EVPN_RTS_HEAD = """ - -""" - -CE_NC_MERGE_EVPN_RTS_TAIL = """ - -""" - -CE_NC_DELETE_EVPN_RTS_CONTEXT = """ - - %s - %s - -""" - -CE_NC_MERGE_EVPN_RTS_CONTEXT = """ - - %s - %s - -""" - -CE_NC_MERGE_EVPN_CONFIG_TAIL = """ - - - - -""" - - -def is_valid_value(vrf_targe_value): - """check whether VPN target value is valid""" - - each_num = None - if len(vrf_targe_value) > 21 or len(vrf_targe_value) < 3: - return False - if vrf_targe_value.find(':') == -1: - return False - elif vrf_targe_value == '0:0': - return False - elif vrf_targe_value == '0.0:0': - return False - else: - value_list = vrf_targe_value.split(':') - if value_list[0].find('.') != -1: - if not value_list[1].isdigit(): - return False - if int(value_list[1]) > 65535: - return False - value = value_list[0].split('.') - if len(value) == 4: - for each_num in value: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - elif len(value) == 2: - for each_num in value: - if not each_num.isdigit(): - return False - if int(each_num) > 65535: - return False - return True - else: - return False - elif not value_list[0].isdigit(): - return False - elif not value_list[1].isdigit(): - return False - elif int(value_list[0]) < 65536 and int(value_list[1]) < 4294967296: - return True - elif int(value_list[0]) > 65535 and int(value_list[0]) < 4294967296: - return bool(int(value_list[1]) < 65536) - else: - return False - - -class EvpnBd(object): - """Manage evpn instance in BD view""" - - def __init__(self, argument_spec, ): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # EVPN instance info - self.bridge_domain_id = self.module.params['bridge_domain_id'] - self.evpn = self.module.params['evpn'] - self.route_distinguisher = self.module.params['route_distinguisher'] - self.vpn_target_both = self.module.params['vpn_target_both'] or list() - self.vpn_target_import = self.module.params[ - 'vpn_target_import'] or list() - self.vpn_target_export = self.module.params[ - 'vpn_target_export'] or list() - self.state = self.module.params['state'] - self.__string_to_lowercase__() - - self.commands = list() - self.evpn_info = dict() - self.conf_exist = False - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """Init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def __check_response__(self, xml_str, xml_name): - """Check if response message is already succeed""" - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def __string_to_lowercase__(self): - """Convert string to lowercase""" - - if self.route_distinguisher: - self.route_distinguisher = self.route_distinguisher.lower() - - if self.vpn_target_export: - for index, ele in enumerate(self.vpn_target_export): - self.vpn_target_export[index] = ele.lower() - - if self.vpn_target_import: - for index, ele in enumerate(self.vpn_target_import): - self.vpn_target_import[index] = ele.lower() - - if self.vpn_target_both: - for index, ele in enumerate(self.vpn_target_both): - self.vpn_target_both[index] = ele.lower() - - def get_all_evpn_rts(self, evpn_rts): - """Get all EVPN RTS""" - - rts = evpn_rts.findall("evpnRT") - if not rts: - return - - for ele in rts: - vrf_rttype = ele.find('vrfRTType') - vrf_rtvalue = ele.find('vrfRTValue') - - if vrf_rttype.text == 'export_extcommunity': - self.evpn_info['vpn_target_export'].append(vrf_rtvalue.text) - elif vrf_rttype.text == 'import_extcommunity': - self.evpn_info['vpn_target_import'].append(vrf_rtvalue.text) - - def get_all_evpn_autorts(self, evpn_autorts): - """"Get all EVPN AUTORTS""" - - autorts = evpn_autorts.findall("evpnAutoRT") - if not autorts: - return - - for autort in autorts: - vrf_rttype = autort.find('vrfRTType') - - if vrf_rttype.text == 'export_extcommunity': - self.evpn_info['vpn_target_export'].append('auto') - elif vrf_rttype.text == 'import_extcommunity': - self.evpn_info['vpn_target_import'].append('auto') - - def process_rts_info(self): - """Process RTS information""" - - if not self.evpn_info['vpn_target_export'] or\ - not self.evpn_info['vpn_target_import']: - return - - vpn_target_export = copy.deepcopy(self.evpn_info['vpn_target_export']) - for ele in vpn_target_export: - if ele in self.evpn_info['vpn_target_import']: - self.evpn_info['vpn_target_both'].append(ele) - self.evpn_info['vpn_target_export'].remove(ele) - self.evpn_info['vpn_target_import'].remove(ele) - - def get_evpn_instance_info(self): - """Get current EVPN instance information""" - - if not self.bridge_domain_id: - self.module.fail_json(msg='Error: The value of bridge_domain_id cannot be empty.') - - self.evpn_info['route_distinguisher'] = None - self.evpn_info['vpn_target_import'] = list() - self.evpn_info['vpn_target_export'] = list() - self.evpn_info['vpn_target_both'] = list() - self.evpn_info['evpn_inst'] = 'enable' - - xml_str = CE_NC_GET_EVPN_CONFIG % ( - self.bridge_domain_id, self.bridge_domain_id) - xml_str = get_nc_config(self.module, xml_str) - if "" in xml_str: - self.evpn_info['evpn_inst'] = 'disable' - return - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - evpn_inst = root.find("evpn/evpnInstances/evpnInstance") - if evpn_inst: - for eles in evpn_inst: - if eles.tag in ["evpnAutoRD", "evpnRD", "evpnRTs", "evpnAutoRTs"]: - if eles.tag == 'evpnAutoRD' and eles.text == 'true': - self.evpn_info['route_distinguisher'] = 'auto' - elif eles.tag == 'evpnRD' and self.evpn_info['route_distinguisher'] != 'auto': - self.evpn_info['route_distinguisher'] = eles.text - elif eles.tag == 'evpnRTs': - self.get_all_evpn_rts(eles) - elif eles.tag == 'evpnAutoRTs': - self.get_all_evpn_autorts(eles) - self.process_rts_info() - - def get_existing(self): - """Get existing config""" - - self.existing = dict(bridge_domain_id=self.bridge_domain_id, - evpn=self.evpn_info['evpn_inst'], - route_distinguisher=self.evpn_info[ - 'route_distinguisher'], - vpn_target_both=self.evpn_info['vpn_target_both'], - vpn_target_import=self.evpn_info[ - 'vpn_target_import'], - vpn_target_export=self.evpn_info['vpn_target_export']) - - def get_proposed(self): - """Get proposed config""" - - self.proposed = dict(bridge_domain_id=self.bridge_domain_id, - evpn=self.evpn, - route_distinguisher=self.route_distinguisher, - vpn_target_both=self.vpn_target_both, - vpn_target_import=self.vpn_target_import, - vpn_target_export=self.vpn_target_export, - state=self.state) - - def get_end_state(self): - """Get end config""" - - self.get_evpn_instance_info() - self.end_state = dict(bridge_domain_id=self.bridge_domain_id, - evpn=self.evpn_info['evpn_inst'], - route_distinguisher=self.evpn_info[ - 'route_distinguisher'], - vpn_target_both=self.evpn_info[ - 'vpn_target_both'], - vpn_target_import=self.evpn_info[ - 'vpn_target_import'], - vpn_target_export=self.evpn_info['vpn_target_export']) - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def judge_if_vpn_target_exist(self, vpn_target_type): - """Judge whether proposed vpn target has existed""" - - vpn_target = list() - if vpn_target_type == 'vpn_target_import': - vpn_target.extend(self.existing['vpn_target_both']) - vpn_target.extend(self.existing['vpn_target_import']) - return set(self.proposed['vpn_target_import']).issubset(vpn_target) - elif vpn_target_type == 'vpn_target_export': - vpn_target.extend(self.existing['vpn_target_both']) - vpn_target.extend(self.existing['vpn_target_export']) - return set(self.proposed['vpn_target_export']).issubset(vpn_target) - - return False - - def judge_if_config_exist(self): - """Judge whether configuration has existed""" - - if self.state == 'absent': - if self.route_distinguisher or self.vpn_target_import or self.vpn_target_export or self.vpn_target_both: - return False - else: - return True - - if self.evpn_info['evpn_inst'] != self.evpn: - return False - - if self.evpn == 'disable' and self.evpn_info['evpn_inst'] == 'disable': - return True - - if self.proposed['bridge_domain_id'] != self.existing['bridge_domain_id']: - return False - - if self.proposed['route_distinguisher']: - if self.proposed['route_distinguisher'] != self.existing['route_distinguisher']: - return False - - if self.proposed['vpn_target_both']: - if not self.existing['vpn_target_both']: - return False - if not set(self.proposed['vpn_target_both']).issubset(self.existing['vpn_target_both']): - return False - - if self.proposed['vpn_target_import']: - if not self.judge_if_vpn_target_exist('vpn_target_import'): - return False - - if self.proposed['vpn_target_export']: - if not self.judge_if_vpn_target_exist('vpn_target_export'): - return False - - return True - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def unconfig_evpn_instance(self): - """Unconfigure EVPN instance""" - - self.updates_cmd.append("bridge-domain %s" % self.bridge_domain_id) - xml_str = CE_NC_MERGE_EVPN_CONFIG_HEAD % ( - self.bridge_domain_id, self.bridge_domain_id) - self.updates_cmd.append(" evpn") - - # unconfigure RD - if self.route_distinguisher: - if self.route_distinguisher.lower() == 'auto': - xml_str += 'false' - self.updates_cmd.append(" undo route-distinguisher auto") - else: - xml_str += '' - self.updates_cmd.append( - " undo route-distinguisher %s" % self.route_distinguisher) - xml_str += CE_NC_MERGE_EVPN_CONFIG_TAIL - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "UNDO_EVPN_BD_RD") - self.changed = True - return - - # process VPN target list - vpn_target_export = copy.deepcopy(self.vpn_target_export) - vpn_target_import = copy.deepcopy(self.vpn_target_import) - if self.vpn_target_both: - for ele in self.vpn_target_both: - if ele not in vpn_target_export: - vpn_target_export.append(ele) - if ele not in vpn_target_import: - vpn_target_import.append(ele) - - # unconfig EVPN auto RTS - head_flag = False - if vpn_target_export: - for ele in vpn_target_export: - if ele.lower() == 'auto': - if not head_flag: - xml_str += CE_NC_MERGE_EVPN_AUTORTS_HEAD - head_flag = True - xml_str += CE_NC_DELETE_EVPN_AUTORTS_CONTEXT % ( - 'export_extcommunity') - self.updates_cmd.append( - " undo vpn-target auto export-extcommunity") - if vpn_target_import: - for ele in vpn_target_import: - if ele.lower() == 'auto': - if not head_flag: - xml_str += CE_NC_MERGE_EVPN_AUTORTS_HEAD - head_flag = True - xml_str += CE_NC_DELETE_EVPN_AUTORTS_CONTEXT % ( - 'import_extcommunity') - self.updates_cmd.append( - " undo vpn-target auto import-extcommunity") - - if head_flag: - xml_str += CE_NC_MERGE_EVPN_AUTORTS_TAIL - - # unconfig EVPN RTS - head_flag = False - if vpn_target_export: - for ele in vpn_target_export: - if ele.lower() != 'auto': - if not head_flag: - xml_str += CE_NC_MERGE_EVPN_RTS_HEAD - head_flag = True - xml_str += CE_NC_DELETE_EVPN_RTS_CONTEXT % ( - 'export_extcommunity', ele) - self.updates_cmd.append( - " undo vpn-target %s export-extcommunity" % ele) - - if vpn_target_import: - for ele in vpn_target_import: - if ele.lower() != 'auto': - if not head_flag: - xml_str += CE_NC_MERGE_EVPN_RTS_HEAD - head_flag = True - xml_str += CE_NC_DELETE_EVPN_RTS_CONTEXT % ( - 'import_extcommunity', ele) - self.updates_cmd.append( - " undo vpn-target %s import-extcommunity" % ele) - - if head_flag: - xml_str += CE_NC_MERGE_EVPN_RTS_TAIL - - xml_str += CE_NC_MERGE_EVPN_CONFIG_TAIL - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "MERGE_EVPN_BD_VPN_TARGET_CONFIG") - self.changed = True - - def config_evpn_instance(self): - """Configure EVPN instance""" - - self.updates_cmd.append("bridge-domain %s" % self.bridge_domain_id) - - if self.evpn == 'disable': - xml_str = CE_NC_DELETE_EVPN_CONFIG % ( - self.bridge_domain_id, self.bridge_domain_id) - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "MERGE_EVPN_BD_CONFIG") - self.updates_cmd.append(" undo evpn") - self.changed = True - return - - xml_str = CE_NC_MERGE_EVPN_CONFIG_HEAD % ( - self.bridge_domain_id, self.bridge_domain_id) - self.updates_cmd.append(" evpn") - - # configure RD - if self.route_distinguisher: - if not self.existing['route_distinguisher']: - if self.route_distinguisher.lower() == 'auto': - xml_str += 'true' - self.updates_cmd.append(" route-distinguisher auto") - else: - xml_str += '%s' % self.route_distinguisher - self.updates_cmd.append( - " route-distinguisher %s" % self.route_distinguisher) - - # process VPN target list - vpn_target_export = copy.deepcopy(self.vpn_target_export) - vpn_target_import = copy.deepcopy(self.vpn_target_import) - if self.vpn_target_both: - for ele in self.vpn_target_both: - if ele not in vpn_target_export: - vpn_target_export.append(ele) - if ele not in vpn_target_import: - vpn_target_import.append(ele) - - # config EVPN auto RTS - head_flag = False - if vpn_target_export: - for ele in vpn_target_export: - if ele.lower() == 'auto' and \ - (not self.is_vpn_target_exist('export_extcommunity', ele.lower())): - if not head_flag: - xml_str += CE_NC_MERGE_EVPN_AUTORTS_HEAD - head_flag = True - xml_str += CE_NC_MERGE_EVPN_AUTORTS_CONTEXT % ( - 'export_extcommunity') - self.updates_cmd.append( - " vpn-target auto export-extcommunity") - if vpn_target_import: - for ele in vpn_target_import: - if ele.lower() == 'auto' and \ - (not self.is_vpn_target_exist('import_extcommunity', ele.lower())): - if not head_flag: - xml_str += CE_NC_MERGE_EVPN_AUTORTS_HEAD - head_flag = True - xml_str += CE_NC_MERGE_EVPN_AUTORTS_CONTEXT % ( - 'import_extcommunity') - self.updates_cmd.append( - " vpn-target auto import-extcommunity") - - if head_flag: - xml_str += CE_NC_MERGE_EVPN_AUTORTS_TAIL - - # config EVPN RTS - head_flag = False - if vpn_target_export: - for ele in vpn_target_export: - if ele.lower() != 'auto' and \ - (not self.is_vpn_target_exist('export_extcommunity', ele.lower())): - if not head_flag: - xml_str += CE_NC_MERGE_EVPN_RTS_HEAD - head_flag = True - xml_str += CE_NC_MERGE_EVPN_RTS_CONTEXT % ( - 'export_extcommunity', ele) - self.updates_cmd.append( - " vpn-target %s export-extcommunity" % ele) - - if vpn_target_import: - for ele in vpn_target_import: - if ele.lower() != 'auto' and \ - (not self.is_vpn_target_exist('import_extcommunity', ele.lower())): - if not head_flag: - xml_str += CE_NC_MERGE_EVPN_RTS_HEAD - head_flag = True - xml_str += CE_NC_MERGE_EVPN_RTS_CONTEXT % ( - 'import_extcommunity', ele) - self.updates_cmd.append( - " vpn-target %s import-extcommunity" % ele) - - if head_flag: - xml_str += CE_NC_MERGE_EVPN_RTS_TAIL - - xml_str += CE_NC_MERGE_EVPN_CONFIG_TAIL - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "MERGE_EVPN_BD_CONFIG") - self.changed = True - - def is_vpn_target_exist(self, target_type, value): - """Judge whether VPN target has existed""" - - if target_type == 'export_extcommunity': - if (value not in self.existing['vpn_target_export']) and\ - (value not in self.existing['vpn_target_both']): - return False - return True - - if target_type == 'import_extcommunity': - if (value not in self.existing['vpn_target_import']) and\ - (value not in self.existing['vpn_target_both']): - return False - return True - - return False - - def config_evnp_bd(self): - """Configure EVPN in BD view""" - - if not self.conf_exist: - if self.state == 'present': - self.config_evpn_instance() - else: - self.unconfig_evpn_instance() - - def process_input_params(self): - """Process input parameters""" - - if self.state == 'absent': - self.evpn = None - else: - if self.evpn == 'disable': - return - - if self.vpn_target_both: - for ele in self.vpn_target_both: - if ele in self.vpn_target_export: - self.vpn_target_export.remove(ele) - if ele in self.vpn_target_import: - self.vpn_target_import.remove(ele) - - if self.vpn_target_export and self.vpn_target_import: - vpn_target_export = copy.deepcopy(self.vpn_target_export) - for ele in vpn_target_export: - if ele in self.vpn_target_import: - self.vpn_target_both.append(ele) - self.vpn_target_import.remove(ele) - self.vpn_target_export.remove(ele) - - def check_vpn_target_para(self): - """Check whether VPN target value is valid""" - - if self.route_distinguisher: - if self.route_distinguisher.lower() != 'auto' and\ - not is_valid_value(self.route_distinguisher): - self.module.fail_json( - msg='Error: Route distinguisher has invalid value %s.' % self.route_distinguisher) - - if self.vpn_target_export: - for ele in self.vpn_target_export: - if ele.lower() != 'auto' and not is_valid_value(ele): - self.module.fail_json( - msg='Error: VPN target extended community attribute has invalid value %s.' % ele) - - if self.vpn_target_import: - for ele in self.vpn_target_import: - if ele.lower() != 'auto' and not is_valid_value(ele): - self.module.fail_json( - msg='Error: VPN target extended community attribute has invalid value %s.' % ele) - - if self.vpn_target_both: - for ele in self.vpn_target_both: - if ele.lower() != 'auto' and not is_valid_value(ele): - self.module.fail_json( - msg='Error: VPN target extended community attribute has invalid value %s.' % ele) - - def check_undo_params_if_exist(self): - """Check whether all undo parameters is existed""" - - if self.vpn_target_import: - for ele in self.vpn_target_import: - if ele not in self.evpn_info['vpn_target_import'] and ele not in self.evpn_info['vpn_target_both']: - self.module.fail_json( - msg='Error: VPN target import attribute value %s does not exist.' % ele) - - if self.vpn_target_export: - for ele in self.vpn_target_export: - if ele not in self.evpn_info['vpn_target_export'] and ele not in self.evpn_info['vpn_target_both']: - self.module.fail_json( - msg='Error: VPN target export attribute value %s does not exist.' % ele) - - if self.vpn_target_both: - for ele in self.vpn_target_both: - if ele not in self.evpn_info['vpn_target_both']: - self.module.fail_json( - msg='Error: VPN target export and import attribute value %s does not exist.' % ele) - - def check_params(self): - """Check all input params""" - - # bridge_domain_id check - if self.bridge_domain_id: - if not self.bridge_domain_id.isdigit(): - self.module.fail_json( - msg='Error: The parameter of bridge domain id is invalid.') - if int(self.bridge_domain_id) > 16777215 or int(self.bridge_domain_id) < 1: - self.module.fail_json( - msg='Error: The bridge domain id must be an integer between 1 and 16777215.') - - if self.state == 'absent': - self.check_undo_params_if_exist() - - # check bd whether binding the vxlan vni - self.check_vni_bd() - self.check_vpn_target_para() - - if self.state == 'absent': - if self.route_distinguisher: - if not self.evpn_info['route_distinguisher']: - self.module.fail_json( - msg='Error: Route distinguisher has not been configured.') - else: - if self.route_distinguisher != self.evpn_info['route_distinguisher']: - self.module.fail_json( - msg='Error: Current route distinguisher value is %s.' % - self.evpn_info['route_distinguisher']) - - if self.state == 'present': - if self.route_distinguisher: - if self.evpn_info['route_distinguisher'] and\ - self.route_distinguisher != self.evpn_info['route_distinguisher']: - self.module.fail_json( - msg='Error: Route distinguisher has already been configured.') - - def check_vni_bd(self): - """Check whether vxlan vni is configured in BD view""" - - xml_str = CE_NC_GET_VNI_BD - xml_str = get_nc_config(self.module, xml_str) - if "" in xml_str or not re.findall(r'\S+\s+%s' % self.bridge_domain_id, xml_str): - self.module.fail_json( - msg='Error: The vxlan vni is not configured or the bridge domain id is invalid.') - - def work(self): - """Execute task""" - - self.get_evpn_instance_info() - self.process_input_params() - self.check_params() - self.get_existing() - self.get_proposed() - self.conf_exist = self.judge_if_config_exist() - - self.config_evnp_bd() - - self.get_end_state() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - bridge_domain_id=dict(required=True, type='str'), - evpn=dict(required=False, type='str', - default='enable', choices=['enable', 'disable']), - route_distinguisher=dict(required=False, type='str'), - vpn_target_both=dict(required=False, type='list'), - vpn_target_import=dict(required=False, type='list'), - vpn_target_export=dict(required=False, type='list'), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - evpn_bd = EvpnBd(argument_spec) - evpn_bd.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_evpn_bgp.py b/plugins/modules/network/cloudengine/ce_evpn_bgp.py deleted file mode 100644 index 4789a1594b..0000000000 --- a/plugins/modules/network/cloudengine/ce_evpn_bgp.py +++ /dev/null @@ -1,731 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_evpn_bgp -short_description: Manages BGP EVPN configuration on HUAWEI CloudEngine switches. -description: - - This module offers the ability to configure a BGP EVPN peer relationship on HUAWEI CloudEngine switches. -author: - - Li Yanfeng (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - bgp_instance: - description: - - Name of a BGP instance. The value is a string of 1 to 31 case-sensitive characters, spaces not supported. - required: True - as_number: - description: - - Specifies integral AS number. The value is an integer ranging from 1 to 4294967295. - peer_address: - description: - - Specifies the IPv4 address of a BGP EVPN peer. The value is in dotted decimal notation. - peer_group_name: - description: - - Specify the name of a peer group that BGP peers need to join. - The value is a string of 1 to 47 case-sensitive characters, spaces not supported. - peer_enable: - description: - - Enable or disable a BGP device to exchange routes with a specified peer or peer group in the address - family view. - choices: ['true','false'] - advertise_router_type: - description: - - Configures a device to advertise routes to its BGP EVPN peers. - choices: ['arp','irb'] - vpn_name: - description: - - Associates a specified VPN instance with the IPv4 address family. - The value is a string of 1 to 31 case-sensitive characters, spaces not supported. - advertise_l2vpn_evpn: - description: - - Enable or disable a device to advertise IP routes imported to a VPN instance to its EVPN instance. - choices: ['enable','disable'] - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' -EXAMPLES = ''' -- name: evpn bgp module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Enable peer address. - ce_evpn_bgp: - bgp_instance: 100 - peer_address: 1.1.1.1 - as_number: 100 - peer_enable: true - provider: "{{ cli }}" - - - name: Enable peer group arp. - ce_evpn_bgp: - bgp_instance: 100 - peer_group_name: aaa - advertise_router_type: arp - provider: "{{ cli }}" - - - name: Enable advertise l2vpn evpn. - ce_evpn_bgp: - bgp_instance: 100 - vpn_name: aaa - advertise_l2vpn_evpn: enable - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"advertise_router_type": "arp", "bgp_instance": "100", "peer_group_name": "aaa", "state": "present"} -existing: - description: k/v pairs of existing rollback - returned: always - type: dict - sample: {"bgp_instance": "100", "peer_group_advertise_type": []} - -updates: - description: command sent to the device - returned: always - type: list - sample: ["peer 1.1.1.1 enable", - "peer aaa advertise arp"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"advertise_l2vpn_evpn": "enable", "bgp_instance": "100", "vpn_name": "aaa"} -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import exec_command, load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec - - -def is_config_exist(cmp_cfg, test_cfg): - """check configuration is exist""" - - if not cmp_cfg or not test_cfg: - return False - - return bool(test_cfg in cmp_cfg) - - -def is_valid_address(address): - """check ip-address is valid""" - - if address.find('.') != -1: - addr_list = address.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - -def is_valid_as_number(as_number): - """check as-number is valid""" - - if as_number.isdigit(): - if int(as_number) > 4294967295 or int(as_number) < 1: - return False - return True - else: - if as_number.find('.') != -1: - number_list = as_number.split('.') - if len(number_list) != 2: - return False - if number_list[1] == 0: - return False - for each_num in number_list: - if not each_num.isdigit(): - return False - if int(each_num) > 65535: - return False - return True - - return False - - -class EvpnBgp(object): - """ - Manages evpn bgp configuration. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.netconf = None - self.init_module() - - # module input info - self.as_number = self.module.params['as_number'] - self.bgp_instance = self.module.params['bgp_instance'] - self.peer_address = self.module.params['peer_address'] - self.peer_group_name = self.module.params['peer_group_name'] - self.peer_enable = self.module.params['peer_enable'] - self.advertise_router_type = self.module.params[ - 'advertise_router_type'] - self.vpn_name = self.module.params['vpn_name'] - self.advertise_l2vpn_evpn = self.module.params['advertise_l2vpn_evpn'] - self.state = self.module.params['state'] - - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.config = "" - self.config_list = list() - self.l2vpn_evpn_exist = False - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.existing = dict() - self.proposed = dict() - self.end_state = dict() - - def init_module(self): - """ init module """ - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def get_evpn_overlay_config(self): - """get evpn-overlay enable configuration""" - - cmd = "display current-configuration | include ^evpn-overlay enable" - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - return out - - def get_current_config(self): - """get current configuration""" - - cmd = "display current-configuration | section include bgp %s" % self.bgp_instance - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - return out - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) # show updates result - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def check_params(self): - """Check all input params""" - - # as_number check - if not self.bgp_instance: - self.module.fail_json( - msg='Error: The bgp_instance can not be none.') - if not self.peer_enable and not self.advertise_router_type and not self.advertise_l2vpn_evpn: - self.module.fail_json( - msg='Error: The peer_enable, advertise_router_type, advertise_l2vpn_evpn ' - 'can not be none at the same time.') - if self.as_number: - if not is_valid_as_number(self.as_number): - self.module.fail_json( - msg='Error: The parameter of as_number %s is invalid.' % self.as_number) - # bgp_instance check - if self.bgp_instance: - if not is_valid_as_number(self.bgp_instance): - self.module.fail_json( - msg='Error: The parameter of bgp_instance %s is invalid.' % self.bgp_instance) - - # peer_address check - if self.peer_address: - if not is_valid_address(self.peer_address): - self.module.fail_json( - msg='Error: The %s is not a valid ip address.' % self.peer_address) - - # peer_group_name check - if self.peer_group_name: - if len(self.peer_group_name) > 47 \ - or len(self.peer_group_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: peer group name is not in the range from 1 to 47.') - - # vpn_name check - if self.vpn_name: - if len(self.vpn_name) > 31 \ - or len(self.vpn_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: peer group name is not in the range from 1 to 31.') - - def get_proposed(self): - """get proposed info""" - - if self.as_number: - self.proposed["as_number"] = self.as_number - if self.bgp_instance: - self.proposed["bgp_instance"] = self.bgp_instance - if self.peer_address: - self.proposed["peer_address"] = self.peer_address - if self.peer_group_name: - self.proposed["peer_group_name"] = self.peer_group_name - if self.peer_enable: - self.proposed["peer_enable"] = self.peer_enable - if self.advertise_router_type: - self.proposed["advertise_router_type"] = self.advertise_router_type - if self.vpn_name: - self.proposed["vpn_name"] = self.vpn_name - if self.advertise_l2vpn_evpn: - self.proposed["advertise_l2vpn_evpn"] = self.advertise_l2vpn_evpn - if not self.peer_enable or not self.advertise_l2vpn_evpn: - if self.state: - self.proposed["state"] = self.state - - def get_peers_enable(self): - """get evpn peer address enable list""" - - if len(self.config_list) != 2: - return None - self.config_list = self.config.split('l2vpn-family evpn') - get = re.findall( - r"peer ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\s?as-number\s?(\S*)", self.config_list[0]) - if not get: - return None - else: - peers = list() - for item in get: - cmd = "peer %s enable" % item[0] - exist = is_config_exist(self.config_list[1], cmd) - if exist: - peers.append( - dict(peer_address=item[0], as_number=item[1], peer_enable='true')) - else: - peers.append(dict(peer_address=item[0], as_number=item[1], peer_enable='false')) - return peers - - def get_peers_advertise_type(self): - """get evpn peer address advertise type list""" - - if len(self.config_list) != 2: - return None - self.config_list = self.config.split('l2vpn-family evpn') - get = re.findall( - r"peer ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\s?as-number\s?(\S*)", self.config_list[0]) - if not get: - return None - else: - peers = list() - for item in get: - cmd = "peer %s advertise arp" % item[0] - exist1 = is_config_exist(self.config_list[1], cmd) - cmd = "peer %s advertise irb" % item[0] - exist2 = is_config_exist(self.config_list[1], cmd) - if exist1: - peers.append(dict(peer_address=item[0], as_number=item[1], advertise_type='arp')) - if exist2: - peers.append(dict(peer_address=item[0], as_number=item[1], advertise_type='irb')) - return peers - - def get_peers_group_enable(self): - """get evpn peer group name enable list""" - - if len(self.config_list) != 2: - return None - self.config_list = self.config.split('l2vpn-family evpn') - get1 = re.findall( - r"group (\S+) external", self.config_list[0]) - - get2 = re.findall( - r"group (\S+) internal", self.config_list[0]) - - if not get1 and not get2: - return None - else: - peer_groups = list() - for item in get1: - cmd = "peer %s enable" % item - exist = is_config_exist(self.config_list[1], cmd) - if exist: - peer_groups.append( - dict(peer_group_name=item, peer_enable='true')) - else: - peer_groups.append( - dict(peer_group_name=item, peer_enable='false')) - - for item in get2: - cmd = "peer %s enable" % item - exist = is_config_exist(self.config_list[1], cmd) - if exist: - peer_groups.append( - dict(peer_group_name=item, peer_enable='true')) - else: - peer_groups.append( - dict(peer_group_name=item, peer_enable='false')) - return peer_groups - - def get_peer_groups_advertise_type(self): - """get evpn peer group name advertise type list""" - - if len(self.config_list) != 2: - return None - self.config_list = self.config.split('l2vpn-family evpn') - get1 = re.findall( - r"group (\S+) external", self.config_list[0]) - - get2 = re.findall( - r"group (\S+) internal", self.config_list[0]) - if not get1 and not get2: - return None - else: - peer_groups = list() - for item in get1: - cmd = "peer %s advertise arp" % item - exist1 = is_config_exist(self.config_list[1], cmd) - cmd = "peer %s advertise irb" % item - exist2 = is_config_exist(self.config_list[1], cmd) - if exist1: - peer_groups.append( - dict(peer_group_name=item, advertise_type='arp')) - if exist2: - peer_groups.append( - dict(peer_group_name=item, advertise_type='irb')) - - for item in get2: - cmd = "peer %s advertise arp" % item - exist1 = is_config_exist(self.config_list[1], cmd) - cmd = "peer %s advertise irb" % item - exist2 = is_config_exist(self.config_list[1], cmd) - if exist1: - peer_groups.append( - dict(peer_group_name=item, advertise_type='arp')) - if exist2: - peer_groups.append( - dict(peer_group_name=item, advertise_type='irb')) - return peer_groups - - def get_existing(self): - """get existing info""" - - if not self.config: - return - if self.bgp_instance: - self.existing["bgp_instance"] = self.bgp_instance - - if self.peer_address and self.peer_enable: - if self.l2vpn_evpn_exist: - self.existing["peer_address_enable"] = self.get_peers_enable() - - if self.peer_group_name and self.peer_enable: - if self.l2vpn_evpn_exist: - self.existing[ - "peer_group_enable"] = self.get_peers_group_enable() - - if self.peer_address and self.advertise_router_type: - if self.l2vpn_evpn_exist: - self.existing[ - "peer_address_advertise_type"] = self.get_peers_advertise_type() - - if self.peer_group_name and self.advertise_router_type: - if self.l2vpn_evpn_exist: - self.existing[ - "peer_group_advertise_type"] = self.get_peer_groups_advertise_type() - - if self.advertise_l2vpn_evpn and self.vpn_name: - cmd = " ipv4-family vpn-instance %s" % self.vpn_name - exist = is_config_exist(self.config, cmd) - if exist: - self.existing["vpn_name"] = self.vpn_name - l2vpn_cmd = "advertise l2vpn evpn" - l2vpn_exist = is_config_exist(self.config, l2vpn_cmd) - if l2vpn_exist: - self.existing["advertise_l2vpn_evpn"] = 'enable' - else: - self.existing["advertise_l2vpn_evpn"] = 'disable' - - def get_end_state(self): - """get end state info""" - - self.config = self.get_current_config() - if not self.config: - return - - self.config_list = self.config.split('l2vpn-family evpn') - if len(self.config_list) == 2: - self.l2vpn_evpn_exist = True - - if self.bgp_instance: - self.end_state["bgp_instance"] = self.bgp_instance - - if self.peer_address and self.peer_enable: - if self.l2vpn_evpn_exist: - self.end_state["peer_address_enable"] = self.get_peers_enable() - - if self.peer_group_name and self.peer_enable: - if self.l2vpn_evpn_exist: - self.end_state[ - "peer_group_enable"] = self.get_peers_group_enable() - - if self.peer_address and self.advertise_router_type: - if self.l2vpn_evpn_exist: - self.end_state[ - "peer_address_advertise_type"] = self.get_peers_advertise_type() - - if self.peer_group_name and self.advertise_router_type: - if self.l2vpn_evpn_exist: - self.end_state[ - "peer_group_advertise_type"] = self.get_peer_groups_advertise_type() - - if self.advertise_l2vpn_evpn and self.vpn_name: - cmd = " ipv4-family vpn-instance %s" % self.vpn_name - exist = is_config_exist(self.config, cmd) - if exist: - self.end_state["vpn_name"] = self.vpn_name - l2vpn_cmd = "advertise l2vpn evpn" - l2vpn_exist = is_config_exist(self.config, l2vpn_cmd) - if l2vpn_exist: - self.end_state["advertise_l2vpn_evpn"] = 'enable' - else: - self.end_state["advertise_l2vpn_evpn"] = 'disable' - - def config_peer(self): - """configure evpn bgp peer command""" - - if self.as_number and self.peer_address: - cmd = "peer %s as-number %s" % (self.peer_address, self.as_number) - exist = is_config_exist(self.config, cmd) - if not exist: - self.module.fail_json( - msg='Error: The peer session %s does not exist or the peer already ' - 'exists in another as-number.' % self.peer_address) - cmd = "bgp %s" % self.bgp_instance - self.cli_add_command(cmd) - cmd = "l2vpn-family evpn" - self.cli_add_command(cmd) - exist_l2vpn = is_config_exist(self.config, cmd) - if self.peer_enable: - cmd = "peer %s enable" % self.peer_address - if exist_l2vpn: - exist = is_config_exist(self.config_list[1], cmd) - if self.peer_enable == "true" and not exist: - self.cli_add_command(cmd) - self.changed = True - elif self.peer_enable == "false" and exist: - self.cli_add_command(cmd, undo=True) - self.changed = True - else: - self.cli_add_command(cmd) - self.changed = True - - if self.advertise_router_type: - cmd = "peer %s advertise %s" % ( - self.peer_address, self.advertise_router_type) - exist = is_config_exist(self.config, cmd) - if self.state == "present" and not exist: - self.cli_add_command(cmd) - self.changed = True - elif self.state == "absent" and exist: - self.cli_add_command(cmd, undo=True) - self.changed = True - elif self.peer_group_name: - cmd_1 = "group %s external" % self.peer_group_name - exist_1 = is_config_exist(self.config, cmd_1) - cmd_2 = "group %s internal" % self.peer_group_name - exist_2 = is_config_exist(self.config, cmd_2) - exist = False - if exist_1: - exist = True - if exist_2: - exist = True - if not exist: - self.module.fail_json( - msg='Error: The peer-group %s does not exist.' % self.peer_group_name) - cmd = "bgp %s" % self.bgp_instance - self.cli_add_command(cmd) - cmd = "l2vpn-family evpn" - self.cli_add_command(cmd) - exist_l2vpn = is_config_exist(self.config, cmd) - if self.peer_enable: - cmd = "peer %s enable" % self.peer_group_name - if exist_l2vpn: - exist = is_config_exist(self.config_list[1], cmd) - if self.peer_enable == "true" and not exist: - self.cli_add_command(cmd) - self.changed = True - elif self.peer_enable == "false" and exist: - self.cli_add_command(cmd, undo=True) - self.changed = True - else: - self.cli_add_command(cmd) - self.changed = True - - if self.advertise_router_type: - cmd = "peer %s advertise %s" % ( - self.peer_group_name, self.advertise_router_type) - exist = is_config_exist(self.config, cmd) - if self.state == "present" and not exist: - self.cli_add_command(cmd) - self.changed = True - elif self.state == "absent" and exist: - self.cli_add_command(cmd, undo=True) - self.changed = True - - def config_advertise_l2vpn_evpn(self): - """configure advertise l2vpn evpn""" - - cmd = "ipv4-family vpn-instance %s" % self.vpn_name - exist = is_config_exist(self.config, cmd) - if not exist: - self.module.fail_json( - msg='Error: The VPN instance name %s does not exist.' % self.vpn_name) - config_vpn_list = self.config.split(cmd) - cmd = "ipv4-family vpn-instance" - exist_vpn = is_config_exist(config_vpn_list[1], cmd) - cmd_l2vpn = "advertise l2vpn evpn" - if exist_vpn: - config_vpn = config_vpn_list[1].split('ipv4-family vpn-instance') - exist_l2vpn = is_config_exist(config_vpn[0], cmd_l2vpn) - else: - exist_l2vpn = is_config_exist(config_vpn_list[1], cmd_l2vpn) - cmd = "advertise l2vpn evpn" - if self.advertise_l2vpn_evpn == "enable" and not exist_l2vpn: - cmd = "bgp %s" % self.bgp_instance - self.cli_add_command(cmd) - cmd = "ipv4-family vpn-instance %s" % self.vpn_name - self.cli_add_command(cmd) - cmd = "advertise l2vpn evpn" - self.cli_add_command(cmd) - self.changed = True - elif self.advertise_l2vpn_evpn == "disable" and exist_l2vpn: - cmd = "bgp %s" % self.bgp_instance - self.cli_add_command(cmd) - cmd = "ipv4-family vpn-instance %s" % self.vpn_name - self.cli_add_command(cmd) - cmd = "advertise l2vpn evpn" - self.cli_add_command(cmd, undo=True) - self.changed = True - - def work(self): - """worker""" - - self.check_params() - evpn_config = self.get_evpn_overlay_config() - if not evpn_config: - self.module.fail_json( - msg="Error: evpn-overlay enable is not configured.") - self.config = self.get_current_config() - if not self.config: - self.module.fail_json( - msg="Error: Bgp instance %s does not exist." % self.bgp_instance) - - self.config_list = self.config.split('l2vpn-family evpn') - if len(self.config_list) == 2: - self.l2vpn_evpn_exist = True - self.get_existing() - self.get_proposed() - - if self.peer_enable or self.advertise_router_type: - self.config_peer() - - if self.advertise_l2vpn_evpn: - self.config_advertise_l2vpn_evpn() - if self.commands: - self.cli_load_config(self.commands) - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - bgp_instance=dict(required=True, type='str'), - as_number=dict(required=False, type='str'), - peer_address=dict(required=False, type='str'), - peer_group_name=dict(required=False, type='str'), - peer_enable=dict(required=False, type='str', choices=[ - 'true', 'false']), - advertise_router_type=dict(required=False, type='str', choices=[ - 'arp', 'irb']), - - vpn_name=dict(required=False, type='str'), - advertise_l2vpn_evpn=dict(required=False, type='str', choices=[ - 'enable', 'disable']), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = EvpnBgp(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_evpn_bgp_rr.py b/plugins/modules/network/cloudengine/ce_evpn_bgp_rr.py deleted file mode 100644 index 2e86d0f4e8..0000000000 --- a/plugins/modules/network/cloudengine/ce_evpn_bgp_rr.py +++ /dev/null @@ -1,531 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_evpn_bgp_rr -short_description: Manages RR for the VXLAN Network on HUAWEI CloudEngine switches. -description: - - Configure an RR in BGP-EVPN address family view on HUAWEI CloudEngine switches. -author: Zhijin Zhou (@QijunPan) -notes: - - Ensure that BGP view is existed. - - The peer, peer_type, and reflect_client arguments must all exist or not exist. - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - as_number: - description: - - Specifies the number of the AS, in integer format. - The value is an integer that ranges from 1 to 4294967295. - required: true - bgp_instance: - description: - - Specifies the name of a BGP instance. - The value of instance-name can be an integer 1 or a string of 1 to 31. - bgp_evpn_enable: - description: - - Enable or disable the BGP-EVPN address family. - choices: ['enable','disable'] - default: 'enable' - peer_type: - description: - - Specify the peer type. - choices: ['group_name','ipv4_address'] - peer: - description: - - Specifies the IPv4 address or the group name of a peer. - reflect_client: - description: - - Configure the local device as the route reflector and the peer or peer group as the client of the route reflector. - choices: ['enable','disable'] - policy_vpn_target: - description: - - Enable or disable the VPN-Target filtering. - choices: ['enable','disable'] -''' - -EXAMPLES = ''' -- name: BGP RR test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Configure BGP-EVPN address family view and ensure that BGP view has existed." - ce_evpn_bgp_rr: - as_number: 20 - bgp_evpn_enable: enable - provider: "{{ cli }}" - - - name: "Configure reflect client and ensure peer has existed." - ce_evpn_bgp_rr: - as_number: 20 - peer_type: ipv4_address - peer: 192.8.3.3 - reflect_client: enable - provider: "{{ cli }}" - - - name: "Configure the VPN-Target filtering." - ce_evpn_bgp_rr: - as_number: 20 - policy_vpn_target: enable - provider: "{{ cli }}" - - - name: "Configure an RR in BGP-EVPN address family view." - ce_evpn_bgp_rr: - as_number: 20 - bgp_evpn_enable: enable - peer_type: ipv4_address - peer: 192.8.3.3 - reflect_client: enable - policy_vpn_target: disable - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "as_number": "20", - "bgp_evpn_enable": "enable", - "bgp_instance": null, - "peer": "192.8.3.3", - "peer_type": "ipv4_address", - "policy_vpn_target": "disable", - "reflect_client": "enable" - } -existing: - description: k/v pairs of existing attributes on the device - returned: always - type: dict - sample: { - "as_number": "20", - "bgp_evpn_enable": "disable", - "bgp_instance": null, - "peer": null, - "peer_type": null, - "policy_vpn_target": "disable", - "reflect_client": "disable" - } -end_state: - description: k/v pairs of end attributes on the device - returned: always - type: dict - sample: { - "as_number": "20", - "bgp_evpn_enable": "enable", - "bgp_instance": null, - "peer": "192.8.3.3", - "peer_type": "ipv4_address", - "policy_vpn_target": "disable", - "reflect_client": "enable" - } -updates: - description: command list sent to the device - returned: always - type: list - sample: [ - "bgp 20", - " l2vpn-family evpn", - " peer 192.8.3.3 enable", - " peer 192.8.3.3 reflect-client", - " undo policy vpn-target" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import exec_command, load_config, ce_argument_spec - - -def is_config_exist(cmp_cfg, test_cfg): - """is configuration exist""" - - if not cmp_cfg or not test_cfg: - return False - - return bool(test_cfg in cmp_cfg) - - -class EvpnBgpRr(object): - """Manage RR in BGP-EVPN address family view""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # RR configuration parameters - self.as_number = self.module.params['as_number'] - self.bgp_instance = self.module.params['bgp_instance'] - self.peer_type = self.module.params['peer_type'] - self.peer = self.module.params['peer'] - self.bgp_evpn_enable = self.module.params['bgp_evpn_enable'] - self.reflect_client = self.module.params['reflect_client'] - self.policy_vpn_target = self.module.params['policy_vpn_target'] - - self.commands = list() - self.config = None - self.bgp_evpn_config = "" - self.cur_config = dict() - self.conf_exist = False - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """Init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def cli_load_config(self, commands): - """Load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def is_bgp_view_exist(self): - """Judge whether BGP view has existed""" - - if self.bgp_instance: - view_cmd = "bgp %s instance %s" % ( - self.as_number, self.bgp_instance) - else: - view_cmd = "bgp %s" % self.as_number - - return is_config_exist(self.config, view_cmd) - - def is_l2vpn_family_evpn_exist(self): - """Judge whether BGP-EVPN address family view has existed""" - - view_cmd = "l2vpn-family evpn" - return is_config_exist(self.config, view_cmd) - - def is_reflect_client_exist(self): - """Judge whether reflect client is configured""" - - view_cmd = "peer %s reflect-client" % self.peer - return is_config_exist(self.bgp_evpn_config, view_cmd) - - def is_policy_vpn_target_exist(self): - """Judge whether the VPN-Target filtering is enabled""" - - view_cmd = "undo policy vpn-target" - if is_config_exist(self.bgp_evpn_config, view_cmd): - return False - else: - return True - - def get_config_in_bgp_view(self): - """Get configuration in BGP view""" - - cmd = "display current-configuration | section include" - if self.as_number: - if self.bgp_instance: - cmd += " bgp %s instance %s" % (self.as_number, - self.bgp_instance) - else: - cmd += " bgp %s" % self.as_number - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - config = out.strip() if out else "" - if cmd == config: - return '' - - return config - - def get_config_in_bgp_evpn_view(self): - """Get configuration in BGP_EVPN view""" - - self.bgp_evpn_config = "" - if not self.config: - return "" - - index = self.config.find("l2vpn-family evpn") - if index == -1: - return "" - - return self.config[index:] - - def get_current_config(self): - """Get current configuration""" - - if not self.as_number: - self.module.fail_json(msg='Error: The value of as-number cannot be empty.') - - self.cur_config['bgp_exist'] = False - self.cur_config['bgp_evpn_enable'] = 'disable' - self.cur_config['reflect_client'] = 'disable' - self.cur_config['policy_vpn_target'] = 'disable' - self.cur_config['peer_type'] = None - self.cur_config['peer'] = None - - self.config = self.get_config_in_bgp_view() - - if not self.is_bgp_view_exist(): - return - self.cur_config['bgp_exist'] = True - - if not self.is_l2vpn_family_evpn_exist(): - return - self.cur_config['bgp_evpn_enable'] = 'enable' - - self.bgp_evpn_config = self.get_config_in_bgp_evpn_view() - if self.is_reflect_client_exist(): - self.cur_config['reflect_client'] = 'enable' - self.cur_config['peer_type'] = self.peer_type - self.cur_config['peer'] = self.peer - - if self.is_policy_vpn_target_exist(): - self.cur_config['policy_vpn_target'] = 'enable' - - def get_existing(self): - """Get existing config""" - - self.existing = dict(as_number=self.as_number, - bgp_instance=self.bgp_instance, - peer_type=self.cur_config['peer_type'], - peer=self.cur_config['peer'], - bgp_evpn_enable=self.cur_config[ - 'bgp_evpn_enable'], - reflect_client=self.cur_config['reflect_client'], - policy_vpn_target=self.cur_config[ - 'policy_vpn_target']) - - def get_proposed(self): - """Get proposed config""" - - self.proposed = dict(as_number=self.as_number, - bgp_instance=self.bgp_instance, - peer_type=self.peer_type, - peer=self.peer, - bgp_evpn_enable=self.bgp_evpn_enable, - reflect_client=self.reflect_client, - policy_vpn_target=self.policy_vpn_target) - - def get_end_state(self): - """Get end config""" - - self.get_current_config() - self.end_state = dict(as_number=self.as_number, - bgp_instance=self.bgp_instance, - peer_type=self.cur_config['peer_type'], - peer=self.cur_config['peer'], - bgp_evpn_enable=self.cur_config[ - 'bgp_evpn_enable'], - reflect_client=self.cur_config['reflect_client'], - policy_vpn_target=self.cur_config['policy_vpn_target']) - if self.end_state == self.existing: - self.changed = False - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def judge_if_config_exist(self): - """Judge whether configuration has existed""" - - if self.bgp_evpn_enable and self.bgp_evpn_enable != self.cur_config['bgp_evpn_enable']: - return False - - if self.bgp_evpn_enable == 'disable' and self.cur_config['bgp_evpn_enable'] == 'disable': - return True - - if self.reflect_client and self.reflect_client == 'enable': - if self.peer_type and self.peer_type != self.cur_config['peer_type']: - return False - if self.peer and self.peer != self.cur_config['peer']: - return False - if self.reflect_client and self.reflect_client != self.cur_config['reflect_client']: - return False - - if self.policy_vpn_target and self.policy_vpn_target != self.cur_config['policy_vpn_target']: - return False - - return True - - def cli_add_command(self, command, undo=False): - """Add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) # show updates result - - def config_rr(self): - """Configure RR""" - - if self.conf_exist: - return - - if self.bgp_instance: - view_cmd = "bgp %s instance %s" % ( - self.as_number, self.bgp_instance) - else: - view_cmd = "bgp %s" % self.as_number - self.cli_add_command(view_cmd) - - if self.bgp_evpn_enable == 'disable': - self.cli_add_command("undo l2vpn-family evpn") - else: - self.cli_add_command("l2vpn-family evpn") - if self.reflect_client and self.reflect_client != self.cur_config['reflect_client']: - if self.reflect_client == 'enable': - self.cli_add_command("peer %s enable" % self.peer) - self.cli_add_command( - "peer %s reflect-client" % self.peer) - else: - self.cli_add_command( - "undo peer %s reflect-client" % self.peer) - self.cli_add_command("undo peer %s enable" % self.peer) - if self.cur_config['bgp_evpn_enable'] == 'enable': - if self.policy_vpn_target and self.policy_vpn_target != self.cur_config['policy_vpn_target']: - if self.policy_vpn_target == 'enable': - self.cli_add_command("policy vpn-target") - else: - self.cli_add_command("undo policy vpn-target") - else: - if self.policy_vpn_target and self.policy_vpn_target == 'disable': - self.cli_add_command("undo policy vpn-target") - - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - - def check_is_ipv4_addr(self): - """Check ipaddress validate""" - - rule1 = r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.' - rule2 = r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' - ipv4_regex = '%s%s%s%s%s%s' % ('^', rule1, rule1, rule1, rule2, '$') - - return bool(re.match(ipv4_regex, self.peer)) - - def check_params(self): - """Check all input params""" - - if self.cur_config['bgp_exist'] == 'false': - self.module.fail_json(msg="Error: BGP view does not exist.") - - if self.bgp_instance: - if len(self.bgp_instance) < 1 or len(self.bgp_instance) > 31: - self.module.fail_json( - msg="Error: The length of BGP instance-name must be between 1 or a string of 1 to and 31.") - - if self.as_number: - if len(self.as_number) > 11 or len(self.as_number) == 0: - self.module.fail_json( - msg='Error: The len of as_number %s is out of [1 - 11].' % self.as_number) - - tmp_dict1 = dict(peer_type=self.peer_type, - peer=self.peer, - reflect_client=self.reflect_client) - tmp_dict2 = dict((k, v) - for k, v in tmp_dict1.items() if v is not None) - if len(tmp_dict2) != 0 and len(tmp_dict2) != 3: - self.module.fail_json( - msg='Error: The peer, peer_type, and reflect_client arguments must all exist or not exist.') - - if self.peer_type: - if self.peer_type == 'ipv4_address' and not self.check_is_ipv4_addr(): - self.module.fail_json(msg='Error: Illegal IPv4 address.') - elif self.peer_type == 'group_name' and self.check_is_ipv4_addr(): - self.module.fail_json( - msg='Error: Ip address cannot be configured as group-name.') - - def work(self): - """Execute task""" - - self.get_current_config() - self.check_params() - self.get_existing() - self.get_proposed() - self.conf_exist = self.judge_if_config_exist() - - self.config_rr() - - self.get_end_state() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - as_number=dict(required=True, type='str'), - bgp_instance=dict(required=False, type='str'), - bgp_evpn_enable=dict(required=False, type='str', - default='enable', choices=['enable', 'disable']), - peer_type=dict(required=False, type='str', choices=[ - 'group_name', 'ipv4_address']), - peer=dict(required=False, type='str'), - reflect_client=dict(required=False, type='str', - choices=['enable', 'disable']), - policy_vpn_target=dict(required=False, choices=['enable', 'disable']), - ) - argument_spec.update(ce_argument_spec) - evpn_bgp_rr = EvpnBgpRr(argument_spec) - evpn_bgp_rr.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_evpn_global.py b/plugins/modules/network/cloudengine/ce_evpn_global.py deleted file mode 100644 index 8f17b2139a..0000000000 --- a/plugins/modules/network/cloudengine/ce_evpn_global.py +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_evpn_global -short_description: Manages global configuration of EVPN on HUAWEI CloudEngine switches. -description: - - Manages global configuration of EVPN on HUAWEI CloudEngine switches. -author: Zhijin Zhou (@QijunPan) -notes: - - Before configuring evpn_overlay_enable=disable, delete other EVPN configurations. - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - evpn_overlay_enable: - description: - - Configure EVPN as the VXLAN control plane. - required: true - choices: ['enable','disable'] -''' - -EXAMPLES = ''' -- name: evpn global module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Configure EVPN as the VXLAN control plan - ce_evpn_global: - evpn_overlay_enable: enable - provider: "{{ cli }}" - - - name: Undo EVPN as the VXLAN control plan - ce_evpn_global: - evpn_overlay_enable: disable - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "evpn_overlay_enable": "enable" - } -existing: - description: k/v pairs of existing attributes on the device - returned: always - type: dict - sample: { - "evpn_overlay_enable": "disable" - } -end_state: - description: k/v pairs of end attributes on the interface - returned: always - type: dict - sample: { - "evpn_overlay_enable": "enable" - } -updates: - description: command list sent to the device - returned: always - type: list - sample: [ - "evpn-overlay enable", - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import exec_command, load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec - - -class EvpnGlobal(object): - """Manage global configuration of EVPN""" - - def __init__(self, argument_spec, ): - self.spec = argument_spec - self.module = None - self.init_module() - - # EVPN global configuration parameters - self.overlay_enable = self.module.params['evpn_overlay_enable'] - - self.commands = list() - self.global_info = dict() - self.conf_exist = False - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """init_module""" - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def cli_load_config(self, commands): - """load config by cli""" - if not self.module.check_mode: - load_config(self.module, commands) - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) # show updates result - - def get_evpn_global_info(self): - """ get current EVPN global configuration""" - - self.global_info['evpnOverLay'] = 'disable' - cmd = "display current-configuration | include ^evpn-overlay enable" - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - if out: - self.global_info['evpnOverLay'] = 'enable' - - def get_existing(self): - """get existing config""" - self.existing = dict( - evpn_overlay_enable=self.global_info['evpnOverLay']) - - def get_proposed(self): - """get proposed config""" - self.proposed = dict(evpn_overlay_enable=self.overlay_enable) - - def get_end_state(self): - """get end config""" - self.get_evpn_global_info() - self.end_state = dict( - evpn_overlay_enable=self.global_info['evpnOverLay']) - - def show_result(self): - """ show result""" - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def judge_if_config_exist(self): - """ judge whether configuration has existed""" - if self.overlay_enable == self.global_info['evpnOverLay']: - return True - - return False - - def config_evnp_global(self): - """ set global EVPN configuration""" - if not self.conf_exist: - if self.overlay_enable == 'enable': - self.cli_add_command('evpn-overlay enable') - else: - self.cli_add_command('evpn-overlay enable', True) - - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - - def work(self): - """execute task""" - self.get_evpn_global_info() - self.get_existing() - self.get_proposed() - self.conf_exist = self.judge_if_config_exist() - - self.config_evnp_global() - - self.get_end_state() - self.show_result() - - -def main(): - """main function entry""" - - argument_spec = dict( - evpn_overlay_enable=dict( - required=True, type='str', choices=['enable', 'disable']), - ) - argument_spec.update(ce_argument_spec) - evpn_global = EvpnGlobal(argument_spec) - evpn_global.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_facts.py b/plugins/modules/network/cloudengine/ce_facts.py deleted file mode 100644 index 004bbce294..0000000000 --- a/plugins/modules/network/cloudengine/ce_facts.py +++ /dev/null @@ -1,418 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_facts -author: "wangdezhuang (@QijunPan)" -short_description: Gets facts about HUAWEI CloudEngine switches. -description: - - Collects facts from CloudEngine devices running the CloudEngine - operating system. Fact collection is supported over Cli - transport. This module prepends all of the base network fact keys - with C(ansible_net_). The facts module will always collect a - base set of facts from the device and can enable or disable - collection of additional facts. -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a - list of values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: '!config' -''' - -EXAMPLES = """ -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. - -- name: CloudEngine facts test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Gather_subset is all" - ce_facts: - gather_subset: all - provider: "{{ cli }}" - - - name: "Collect only the config facts" - ce_facts: - gather_subset: config - provider: "{{ cli }}" - - - name: "Do not collect hardware facts" - ce_facts: - gather_subset: "!hardware" - provider: "{{ cli }}" -""" - -RETURN = """ -gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -BIOS Version: - description: The BIOS version running on the remote device - returned: always - type: str -Board Type: - description: The board type of the remote device - returned: always - type: str -CPLD1 Version: - description: The CPLD1 Version running the remote device - returned: always - type: str -CPLD2 Version: - description: The CPLD2 Version running the remote device - returned: always - type: str -MAB Version: - description: The MAB Version running the remote device - returned: always - type: str -PCB Version: - description: The PCB Version running the remote device - returned: always - type: str -hostname: - description: The hostname of the remote device - returned: always - type: str - -# hardware -FAN: - description: The fan state on the device - returned: when hardware is configured - type: str -PWR: - description: The power state on the device - returned: when hardware is configured - type: str -filesystems: - description: The filesystems on the device - returned: when hardware is configured - type: str -flash_free: - description: The flash free space on the device - returned: when hardware is configured - type: str -flash_total: - description: The flash total space on the device - returned: when hardware is configured - type: str -memory_free: - description: The memory free space on the remote device - returned: when hardware is configured - type: str -memory_total: - description: The memory total space on the remote device - returned: when hardware is configured - type: str - -# config -config: - description: The current system configuration on the device - returned: when config is configured - type: str - -# interfaces -all_ipv4_addresses: - description: All IPv4 addresses configured on the device - returned: when interfaces is configured - type: list -interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -""" - -import re - -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import run_commands -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, check_args -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = frozenset() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, list(self.COMMANDS)) - - -class Default(FactsBase): - """ Class default """ - - COMMANDS = [ - 'display version', - 'display current-configuration | include sysname' - ] - - def populate(self): - """ Populate method """ - - super(Default, self).populate() - - data = self.responses[0] - if data: - version = data.split("\n") - for item in version: - if re.findall(r"^\d+\S\s+", item.strip()): - tmp_item = item.split() - tmp_key = tmp_item[1] + " " + tmp_item[2] - if len(tmp_item) > 5: - self.facts[tmp_key] = " ".join(tmp_item[4:]) - else: - self.facts[tmp_key] = tmp_item[4] - - data = self.responses[1] - if data: - tmp_value = re.findall(r'sysname (.*)', data) - self.facts['hostname'] = tmp_value[0] - - -class Config(FactsBase): - """ Class config """ - - COMMANDS = [ - 'display current-configuration configuration system' - ] - - def populate(self): - """ Populate method """ - - super(Config, self).populate() - - data = self.responses[0] - if data: - self.facts['config'] = data.split("\n") - - -class Hardware(FactsBase): - """ Class hardware """ - - COMMANDS = [ - 'dir', - 'display memory', - 'display device' - ] - - def populate(self): - """ Populate method """ - - super(Hardware, self).populate() - - data = self.responses[0] - if data: - self.facts['filesystems'] = re.findall(r'Directory of (.*)/', data)[0] - self.facts['flash_total'] = re.findall(r'(.*) total', data)[0].replace(",", "") - self.facts['flash_free'] = re.findall(r'total \((.*) free\)', data)[0].replace(",", "") - - data = self.responses[1] - if data: - memory_total = re.findall(r'Total Memory Used: (.*) Kbytes', data)[0] - use_percent = re.findall(r'Memory Using Percentage: (.*)%', data)[0] - memory_free = str(int(memory_total) - int(memory_total) * int(use_percent) / 100) - self.facts['memory_total'] = memory_total + " Kb" - self.facts['memory_free'] = memory_free + " Kb" - - data = self.responses[2] - if data: - device_info = data.split("\n") - tmp_device_info = device_info[4:-1] - for item in tmp_device_info: - tmp_item = item.split() - if len(tmp_item) == 8: - self.facts[tmp_item[2]] = tmp_item[6] - elif len(tmp_item) == 7: - self.facts[tmp_item[0]] = tmp_item[5] - - -class Interfaces(FactsBase): - """ Class interfaces """ - - COMMANDS = [ - 'display interface brief', - 'display ip interface brief', - 'display lldp neighbor brief' - ] - - def populate(self): - """ Populate method""" - - interface_dict = dict() - ipv4_addr_dict = dict() - neighbors_dict = dict() - - super(Interfaces, self).populate() - - data = self.responses[0] - begin = False - if data: - interface_info = data.split("\n") - for item in interface_info: - if begin: - tmp_item = item.split() - interface_dict[tmp_item[0]] = tmp_item[1] - - if re.findall(r"^Interface", item.strip()): - begin = True - - self.facts['interfaces'] = interface_dict - - data = self.responses[1] - if data: - ipv4_addr = data.split("\n") - tmp_ipv4 = ipv4_addr[11:] - for item in tmp_ipv4: - tmp_item = item.split() - ipv4_addr_dict[tmp_item[0]] = tmp_item[1] - self.facts['all_ipv4_addresses'] = ipv4_addr_dict - - data = self.responses[2] - if data: - neighbors = data.split("\n") - tmp_neighbors = neighbors[2:] - for item in tmp_neighbors: - tmp_item = item.split() - if len(tmp_item) > 3: - neighbors_dict[tmp_item[0]] = tmp_item[3] - else: - neighbors_dict[tmp_item[0]] = None - self.facts['neighbors'] = neighbors_dict - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - """ Module main """ - - spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - spec.update(ce_argument_spec) - - module = AnsibleModule(argument_spec=spec, supports_check_mode=True) - - warnings = list() - check_args(module, warnings) - - 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): - # this is to maintain capability with nxos_facts 2.1 - if key.startswith('_'): - ansible_facts[key[1:]] = value - else: - ansible_facts[key] = value - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_file_copy.py b/plugins/modules/network/cloudengine/ce_file_copy.py deleted file mode 100644 index 70e1ae74c0..0000000000 --- a/plugins/modules/network/cloudengine/ce_file_copy.py +++ /dev/null @@ -1,416 +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) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_file_copy -short_description: Copy a file to a remote cloudengine device over SCP on HUAWEI CloudEngine switches. -description: - - Copy a file to a remote cloudengine device over SCP on HUAWEI CloudEngine switches. -author: - - Zhou Zhijin (@QijunPan) -notes: - - The feature must be enabled with feature scp-server. - - If the file is already present, no transfer will take place. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -requirements: - - paramiko -options: - local_file: - description: - - Path to local file. Local directory must exist. - The maximum length of I(local_file) is C(4096). - required: true - remote_file: - description: - - Remote file path of the copy. Remote directories must exist. - If omitted, the name of the local file will be used. - The maximum length of I(remote_file) is C(4096). - file_system: - description: - - The remote file system of the device. If omitted, - devices that support a I(file_system) parameter will use - their default values. - File system indicates the storage medium and can be set to as follows, - 1) C(flash) is root directory of the flash memory on the master MPU. - 2) C(slave#flash) is root directory of the flash memory on the slave MPU. - If no slave MPU exists, this drive is unavailable. - 3) C(chassis ID/slot number#flash) is root directory of the flash memory on - a device in a stack. For example, C(1/5#flash) indicates the flash memory - whose chassis ID is 1 and slot number is 5. - default: 'flash:' -''' - -EXAMPLES = ''' -- name: File copy test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Copy a local file to remote device" - ce_file_copy: - local_file: /usr/vrpcfg.cfg - remote_file: /vrpcfg.cfg - file_system: 'flash:' - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -transfer_result: - description: information about transfer result. - returned: always - type: str - sample: 'The local file has been successfully transferred to the device.' -local_file: - description: The path of the local file. - returned: always - type: str - sample: '/usr/work/vrpcfg.zip' -remote_file: - description: The path of the remote file. - returned: always - type: str - sample: '/vrpcfg.zip' -''' - -import re -import os -import sys -import time -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, get_nc_config -from ansible.module_utils.connection import ConnectionError -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import validate_ip_v6_address - -try: - import paramiko - HAS_PARAMIKO = True -except ImportError: - HAS_PARAMIKO = False - -try: - from scp import SCPClient - HAS_SCP = True -except ImportError: - HAS_SCP = False - -CE_NC_GET_DISK_INFO = """ - - - - - - - - - - - - -""" - -CE_NC_GET_FILE_INFO = """ - - - - - %s - %s - - - - - -""" - -CE_NC_GET_SCP_ENABLE = """ - - - - -""" - - -def get_cli_exception(exc=None): - """Get cli exception message""" - - msg = list() - if not exc: - exc = sys.exc_info[1] - if exc: - errs = str(exc).split("\r\n") - for err in errs: - if not err: - continue - if "matched error in response:" in err: - continue - if " at '^' position" in err: - err = err.replace(" at '^' position", "") - if err.replace(" ", "") == "^": - continue - if len(err) > 2 and err[0] in ["<", "["] and err[-1] in [">", "]"]: - continue - if err[-1] == ".": - err = err[:-1] - if err.replace(" ", "") == "": - continue - msg.append(err) - else: - msg = ["Error: Fail to get cli exception message."] - - while msg[-1][-1] == ' ': - msg[-1] = msg[-1][:-1] - - if msg[-1][-1] != ".": - msg[-1] += "." - - return ", ".join(msg).capitalize() - - -class FileCopy(object): - """File copy function class""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # file copy parameters - self.local_file = self.module.params['local_file'] - self.remote_file = self.module.params['remote_file'] - self.file_system = self.module.params['file_system'] - self.host_is_ipv6 = validate_ip_v6_address(self.module.params['provider']['host']) - - # state - self.transfer_result = None - self.changed = False - - def init_module(self): - """Init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def remote_file_exists(self, dst, file_system='flash:'): - """Remote file whether exists""" - - full_path = file_system + dst - file_name = os.path.basename(full_path) - file_path = os.path.dirname(full_path) - file_path = file_path + '/' - xml_str = CE_NC_GET_FILE_INFO % (file_name, file_path) - ret_xml = get_nc_config(self.module, xml_str) - if "" in ret_xml: - return False, 0 - - xml_str = ret_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get file info - root = ElementTree.fromstring(xml_str) - topo = root.find("vfm/dirs/dir") - if topo is None: - return False, 0 - - for eles in topo: - if eles.tag in ["DirSize"]: - return True, int(eles.text.replace(',', '')) - - return False, 0 - - def local_file_exists(self): - """Local file whether exists""" - - return os.path.isfile(self.local_file) - - def enough_space(self): - """Whether device has enough space""" - - xml_str = CE_NC_GET_DISK_INFO - ret_xml = get_nc_config(self.module, xml_str) - if "" in ret_xml: - return - - xml_str = ret_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - topo = root.find("vfm/dfs/df/freeSize") - kbytes_free = topo.text - - file_size = os.path.getsize(self.local_file) - if int(kbytes_free) * 1024 > file_size: - return True - - return False - - def transfer_file(self, dest): - """Begin to transfer file by scp""" - - if not self.local_file_exists(): - self.module.fail_json( - msg='Could not transfer file. Local file doesn\'t exist.') - - if not self.enough_space(): - self.module.fail_json( - msg='Could not transfer file. Not enough space on device.') - - hostname = self.module.params['provider']['host'] - username = self.module.params['provider']['username'] - password = self.module.params['provider']['password'] - port = self.module.params['provider']['port'] - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect(hostname=hostname, username=username, password=password, port=port) - full_remote_path = '{0}{1}'.format(self.file_system, dest) - scp = SCPClient(ssh.get_transport()) - try: - scp.put(self.local_file, full_remote_path) - except Exception: - time.sleep(10) - file_exists, temp_size = self.remote_file_exists( - dest, self.file_system) - file_size = os.path.getsize(self.local_file) - if file_exists and int(temp_size) == int(file_size): - pass - else: - scp.close() - self.module.fail_json(msg='Could not transfer file. There was an error ' - 'during transfer. Please make sure the format of ' - 'input parameters is right.') - scp.close() - return True - - def get_scp_enable(self): - """Get scp enable state""" - - ret_xml = '' - try: - ret_xml = get_nc_config(self.module, CE_NC_GET_SCP_ENABLE) - except ConnectionError: - self.module.fail_json(msg='Error: The NETCONF API of scp_enable is not supported.') - - if "" in ret_xml: - return False - - xml_str = ret_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get file info - root = ElementTree.fromstring(xml_str) - topo1 = root.find("sshs/sshServer/scpEnable") - topo2 = root.find("sshs/sshServerEnable/scpIpv4Enable") - topo3 = root.find("sshs/sshServerEnable/scpIpv6Enable") - if topo1 is not None: - return str(topo1.text).strip().lower() == 'enable' - elif self.host_is_ipv6 and topo3 is not None: - return str(topo3.text).strip().lower() == 'enable' - elif topo2 is not None: - return str(topo2.text).strip().lower() == 'enable' - return False - - def work(self): - """Execute task """ - - if not HAS_SCP: - self.module.fail_json( - msg="'Error: No scp package, please install it.'") - - if not HAS_PARAMIKO: - self.module.fail_json( - msg="'Error: No paramiko package, please install it.'") - - if self.local_file and len(self.local_file) > 4096: - self.module.fail_json( - msg="'Error: The maximum length of local_file is 4096.'") - - if self.remote_file and len(self.remote_file) > 4096: - self.module.fail_json( - msg="'Error: The maximum length of remote_file is 4096.'") - - scp_enable = self.get_scp_enable() - if not scp_enable: - if self.host_is_ipv6: - self.module.fail_json( - msg="'Error: Please ensure ipv6 SCP server are enabled.'") - else: - self.module.fail_json( - msg="'Error: Please ensure ipv4 SCP server are enabled.'") - - if not os.path.isfile(self.local_file): - self.module.fail_json( - msg="Local file {0} not found".format(self.local_file)) - - dest = self.remote_file or ('/' + os.path.basename(self.local_file)) - remote_exists, file_size = self.remote_file_exists( - dest, file_system=self.file_system) - if remote_exists and (os.path.getsize(self.local_file) != file_size): - remote_exists = False - - if not remote_exists: - self.changed = True - file_exists = False - else: - file_exists = True - self.transfer_result = 'The local file already exists on the device.' - - if not file_exists: - self.transfer_file(dest) - self.transfer_result = 'The local file has been successfully transferred to the device.' - - if self.remote_file is None: - self.remote_file = '/' + os.path.basename(self.local_file) - - self.module.exit_json( - changed=self.changed, - transfer_result=self.transfer_result, - local_file=self.local_file, - remote_file=self.remote_file, - file_system=self.file_system) - - -def main(): - """Main function entry""" - - argument_spec = dict( - local_file=dict(required=True), - remote_file=dict(required=False), - file_system=dict(required=False, default='flash:') - ) - argument_spec.update(ce_argument_spec) - filecopy_obj = FileCopy(argument_spec) - filecopy_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_info_center_debug.py b/plugins/modules/network/cloudengine/ce_info_center_debug.py deleted file mode 100644 index 57487c3d70..0000000000 --- a/plugins/modules/network/cloudengine/ce_info_center_debug.py +++ /dev/null @@ -1,617 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_info_center_debug -short_description: Manages information center debug configuration on HUAWEI CloudEngine switches. -description: - - Manages information center debug configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] - debug_time_stamp: - description: - - Timestamp type of debugging information. - choices: ['date_boot', 'date_second', 'date_tenthsecond', 'date_millisecond', 'shortdate_second', - 'shortdate_tenthsecond', 'shortdate_millisecond', 'formatdate_second', 'formatdate_tenthsecond', - 'formatdate_millisecond'] - module_name: - description: - - Module name of the rule. - The value is a string of 1 to 31 case-insensitive characters. The default value is default. - Please use lower-case letter, such as [aaa, acl, arp, bfd]. - channel_id: - description: - - Number of a channel. - The value is an integer ranging from 0 to 9. The default value is 0. - debug_enable: - description: - - Whether a device is enabled to output debugging information. - default: no_use - choices: ['no_use','true','false'] - debug_level: - description: - - Debug level permitted to output. - choices: ['emergencies', 'alert', 'critical', 'error', 'warning', 'notification', - 'informational', 'debugging'] -''' - -EXAMPLES = ''' - -- name: CloudEngine info center debug test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config debug time stamp" - ce_info_center_debug: - state: present - debug_time_stamp: date_boot - provider: "{{ cli }}" - - - name: "Undo debug time stamp" - ce_info_center_debug: - state: absent - debug_time_stamp: date_boot - provider: "{{ cli }}" - - - name: "Config debug module log level" - ce_info_center_debug: - state: present - module_name: aaa - channel_id: 1 - debug_enable: true - debug_level: error - provider: "{{ cli }}" - - - name: "Undo debug module log level" - ce_info_center_debug: - state: absent - module_name: aaa - channel_id: 1 - debug_enable: true - debug_level: error - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"state": "present", "debug_time_stamp": "date_boot"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"debugTimeStamp": "DATE_MILLISECOND"} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"debugTimeStamp": "DATE_BOOT"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["info-center timestamp debugging boot"] -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -# get info center debug global -CE_GET_DEBUG_GLOBAL_HEADER = """ - - - -""" -CE_GET_DEBUG_GLOBAL_TAIL = """ - - - -""" -# merge info center debug global -CE_MERGE_DEBUG_GLOBAL_HEADER = """ - - - -""" -CE_MERGE_DEBUG_GLOBAL_TAIL = """ - - - -""" - -# get info center debug source -CE_GET_DEBUG_SOURCE_HEADER = """ - - - - -""" -CE_GET_DEBUG_SOURCE_TAIL = """ - - - - -""" -# merge info center debug source -CE_MERGE_DEBUG_SOURCE_HEADER = """ - - - - -""" -CE_MERGE_DEBUG_SOURCE_TAIL = """ - - - - -""" -# delete info center debug source -CE_DELETE_DEBUG_SOURCE_HEADER = """ - - - - -""" -CE_DELETE_DEBUG_SOURCE_TAIL = """ - - - - -""" - -TIME_STAMP_DICT = {"date_boot": "boot", - "date_second": "date precision-time second", - "date_tenthsecond": "date precision-time tenth-second", - "date_millisecond": "date precision-time millisecond", - "shortdate_second": "short-date precision-time second", - "shortdate_tenthsecond": "short-date precision-time tenth-second", - "shortdate_millisecond": "short-date precision-time millisecond", - "formatdate_second": "format-date precision-time second", - "formatdate_tenthsecond": "format-date precision-time tenth-second", - "formatdate_millisecond": "format-date precision-time millisecond"} - -CHANNEL_DEFAULT_DBG_STATE = {"0": "true", - "1": "true", - "2": "false", - "3": "false", - "4": "false", - "5": "false", - "6": "false", - "7": "false", - "8": "false", - "9": "false"} - -CHANNEL_DEFAULT_DBG_LEVEL = {"0": "debugging", - "1": "debugging", - "2": "debugging", - "3": "debugging", - "4": "debugging", - "5": "debugging", - "6": "debugging", - "7": "debugging", - "8": "debugging", - "9": "debugging"} - - -class InfoCenterDebug(object): - """ Manages info center debug configuration """ - - def __init__(self, **kwargs): - """ Init function """ - - # argument spec - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # module args - self.state = self.module.params['state'] - self.debug_time_stamp = self.module.params['debug_time_stamp'] or None - self.module_name = self.module.params['module_name'] or None - self.channel_id = self.module.params['channel_id'] or None - self.debug_enable = self.module.params['debug_enable'] - self.debug_level = self.module.params['debug_level'] or None - - # cur config - self.cur_global_cfg = dict() - self.cur_source_cfg = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def check_global_args(self): - """ Check global args """ - - need_cfg = False - find_flag = False - self.cur_global_cfg["global_cfg"] = [] - - if self.debug_time_stamp: - - conf_str = CE_GET_DEBUG_GLOBAL_HEADER - conf_str += "" - conf_str += CE_GET_DEBUG_GLOBAL_TAIL - - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - find_flag = False - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - global_cfg = root.findall("syslog/globalParam") - if global_cfg: - for tmp in global_cfg: - tmp_dict = dict() - for site in tmp: - if site.tag in ["debugTimeStamp"]: - tmp_dict[site.tag] = site.text - - self.cur_global_cfg["global_cfg"].append(tmp_dict) - - if self.cur_global_cfg["global_cfg"]: - for tmp in self.cur_global_cfg["global_cfg"]: - find_flag = True - - if tmp.get("debugTimeStamp").lower() != self.debug_time_stamp: - find_flag = False - - if find_flag: - break - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - else: - need_cfg = bool(find_flag) - - self.cur_global_cfg["need_cfg"] = need_cfg - - def check_source_args(self): - """ Check source args """ - - need_cfg = False - find_flag = False - self.cur_source_cfg["source_cfg"] = [] - - if self.module_name: - if len(self.module_name) < 1 or len(self.module_name) > 31: - self.module.fail_json( - msg='Error: The module_name is out of [1 - 31].') - - if not self.channel_id: - self.module.fail_json( - msg='Error: Please input channel_id at the same time.') - - if self.channel_id: - if self.channel_id.isdigit(): - if int(self.channel_id) < 0 or int(self.channel_id) > 9: - self.module.fail_json( - msg='Error: The value of channel_id is out of [0 - 9].') - else: - self.module.fail_json( - msg='Error: The channel_id is not digit.') - - conf_str = CE_GET_DEBUG_SOURCE_HEADER - - if self.module_name != "default": - conf_str += "%s" % self.module_name.upper() - else: - conf_str += "default" - - if self.channel_id: - conf_str += "" - if self.debug_enable != 'no_use': - conf_str += "" - if self.debug_level: - conf_str += "" - - conf_str += CE_GET_DEBUG_SOURCE_TAIL - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - find_flag = False - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - source_cfg = root.findall("syslog/icSources/icSource") - if source_cfg: - for tmp in source_cfg: - tmp_dict = dict() - for site in tmp: - if site.tag in ["moduleName", "icChannelId", "dbgEnFlg", "dbgEnLevel"]: - tmp_dict[site.tag] = site.text - - self.cur_source_cfg["source_cfg"].append(tmp_dict) - - if self.cur_source_cfg["source_cfg"]: - for tmp in self.cur_source_cfg["source_cfg"]: - find_flag = True - - if self.module_name and tmp.get("moduleName").lower() != self.module_name.lower(): - find_flag = False - if self.channel_id and tmp.get("icChannelId") != self.channel_id: - find_flag = False - if self.debug_enable != 'no_use' and tmp.get("dbgEnFlg") != self.debug_enable: - find_flag = False - if self.debug_level and tmp.get("dbgEnLevel") != self.debug_level: - find_flag = False - - if find_flag: - break - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - else: - need_cfg = bool(find_flag) - - self.cur_source_cfg["need_cfg"] = need_cfg - - def get_proposed(self): - """ Get proposed """ - - self.proposed["state"] = self.state - - if self.debug_time_stamp: - self.proposed["debug_time_stamp"] = self.debug_time_stamp - if self.module_name: - self.proposed["module_name"] = self.module_name - if self.channel_id: - self.proposed["channel_id"] = self.channel_id - if self.debug_enable != 'no_use': - self.proposed["debug_enable"] = self.debug_enable - if self.debug_level: - self.proposed["debug_level"] = self.debug_level - - def get_existing(self): - """ Get existing """ - - if self.cur_global_cfg["global_cfg"]: - self.existing["global_cfg"] = self.cur_global_cfg["global_cfg"] - if self.cur_source_cfg["source_cfg"]: - self.existing["source_cfg"] = self.cur_source_cfg["source_cfg"] - - def get_end_state(self): - """ Get end state """ - - self.check_global_args() - if self.cur_global_cfg["global_cfg"]: - self.end_state["global_cfg"] = self.cur_global_cfg["global_cfg"] - - self.check_source_args() - if self.cur_source_cfg["source_cfg"]: - self.end_state["source_cfg"] = self.cur_source_cfg["source_cfg"] - - def merge_debug_global(self): - """ Merge debug global """ - - conf_str = CE_MERGE_DEBUG_GLOBAL_HEADER - - if self.debug_time_stamp: - conf_str += "%s" % self.debug_time_stamp.upper() - - conf_str += CE_MERGE_DEBUG_GLOBAL_TAIL - - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge debug global failed.') - - if self.debug_time_stamp: - cmd = "info-center timestamp debugging " + TIME_STAMP_DICT.get(self.debug_time_stamp) - self.updates_cmd.append(cmd) - - self.changed = True - - def delete_debug_global(self): - """ Delete debug global """ - - conf_str = CE_MERGE_DEBUG_GLOBAL_HEADER - - if self.debug_time_stamp: - conf_str += "DATE_MILLISECOND" - - conf_str += CE_MERGE_DEBUG_GLOBAL_TAIL - - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json(msg='Error: delete debug global failed.') - - if self.debug_time_stamp: - cmd = "undo info-center timestamp debugging" - self.updates_cmd.append(cmd) - - self.changed = True - - def merge_debug_source(self): - """ Merge debug source """ - - conf_str = CE_MERGE_DEBUG_SOURCE_HEADER - - if self.module_name: - conf_str += "%s" % self.module_name - if self.channel_id: - conf_str += "%s" % self.channel_id - if self.debug_enable != 'no_use': - conf_str += "%s" % self.debug_enable - if self.debug_level: - conf_str += "%s" % self.debug_level - - conf_str += CE_MERGE_DEBUG_SOURCE_TAIL - - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge debug source failed.') - - cmd = "info-center source" - if self.module_name: - cmd += " %s" % self.module_name - if self.channel_id: - cmd += " channel %s" % self.channel_id - if self.debug_enable != 'no_use': - if self.debug_enable == "true": - cmd += " debug state on" - else: - cmd += " debug state off" - if self.debug_level: - cmd += " level %s" % self.debug_level - - self.updates_cmd.append(cmd) - self.changed = True - - def delete_debug_source(self): - """ Delete debug source """ - - if self.debug_enable == 'no_use' and not self.debug_level: - conf_str = CE_DELETE_DEBUG_SOURCE_HEADER - if self.module_name: - conf_str += "%s" % self.module_name - if self.channel_id: - conf_str += "%s" % self.channel_id - conf_str += CE_DELETE_DEBUG_SOURCE_TAIL - else: - conf_str = CE_MERGE_DEBUG_SOURCE_HEADER - if self.module_name: - conf_str += "%s" % self.module_name - if self.channel_id: - conf_str += "%s" % self.channel_id - if self.debug_enable != 'no_use': - conf_str += "%s" % CHANNEL_DEFAULT_DBG_STATE.get(self.channel_id) - if self.debug_level: - conf_str += "%s" % CHANNEL_DEFAULT_DBG_LEVEL.get(self.channel_id) - conf_str += CE_MERGE_DEBUG_SOURCE_TAIL - - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json(msg='Error: Delete debug source failed.') - - cmd = "undo info-center source" - if self.module_name: - cmd += " %s" % self.module_name - if self.channel_id: - cmd += " channel %s" % self.channel_id - if self.debug_enable != 'no_use': - cmd += " debug state" - if self.debug_level: - cmd += " level" - - self.updates_cmd.append(cmd) - self.changed = True - - def work(self): - """ work function """ - - self.check_global_args() - self.check_source_args() - self.get_proposed() - self.get_existing() - - if self.state == "present": - if self.cur_global_cfg["need_cfg"]: - self.merge_debug_global() - if self.cur_source_cfg["need_cfg"]: - self.merge_debug_source() - - else: - if self.cur_global_cfg["need_cfg"]: - self.delete_debug_global() - if self.cur_source_cfg["need_cfg"]: - self.delete_debug_source() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - debug_time_stamp=dict(choices=['date_boot', 'date_second', 'date_tenthsecond', - 'date_millisecond', 'shortdate_second', 'shortdate_tenthsecond', - 'shortdate_millisecond', 'formatdate_second', 'formatdate_tenthsecond', - 'formatdate_millisecond']), - module_name=dict(type='str'), - channel_id=dict(type='str'), - debug_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - debug_level=dict(choices=['emergencies', 'alert', 'critical', 'error', 'warning', 'notification', - 'informational', 'debugging']) - ) - - argument_spec.update(ce_argument_spec) - module = InfoCenterDebug(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_info_center_global.py b/plugins/modules/network/cloudengine/ce_info_center_global.py deleted file mode 100644 index 96355e99ca..0000000000 --- a/plugins/modules/network/cloudengine/ce_info_center_global.py +++ /dev/null @@ -1,1725 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_info_center_global -short_description: Manages outputting logs on HUAWEI CloudEngine switches. -description: - - This module offers the ability to be output to the log buffer, log file, console, terminal, or log host on HUAWEI CloudEngine switches. -author: - - Li Yanfeng (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - info_center_enable: - description: - - Whether the info-center function is enabled. The value is of the Boolean type. - choices: ['true','false'] - packet_priority: - description: - - Set the priority of the syslog packet.The value is an integer ranging from 0 to 7. The default value is 0. - suppress_enable: - description: - - Whether a device is enabled to suppress duplicate statistics. The value is of the Boolean type. - choices: [ 'false', 'true' ] - logfile_max_num: - description: - - Maximum number of log files of the same type. The default value is 200. - - The value range for log files is[3, 500], for security files is [1, 3],and for operation files is [1, 7]. - logfile_max_size: - description: - - Maximum size (in MB) of a log file. The default value is 32. - - The value range for log files is [4, 8, 16, 32], for security files is [1, 4], - - and for operation files is [1, 4]. - default: 32 - choices: ['4', '8', '16', '32'] - channel_id: - description: - - Number for channel. The value is an integer ranging from 0 to 9. The default value is 0. - channel_cfg_name: - description: - - Channel name.The value is a string of 1 to 30 case-sensitive characters. The default value is console. - default: console - channel_out_direct: - description: - - Direction of information output. - choices: ['console','monitor','trapbuffer','logbuffer','snmp','logfile'] - filter_feature_name: - description: - - Feature name of the filtered log. The value is a string of 1 to 31 case-insensitive characters. - filter_log_name: - description: - - Name of the filtered log. The value is a string of 1 to 63 case-sensitive characters. - ip_type: - description: - - Log server address type, IPv4 or IPv6. - choices: ['ipv4','ipv6'] - server_ip: - description: - - Log server address, IPv4 or IPv6 type. The value is a string of 0 to 255 characters. - The value can be an valid IPv4 or IPv6 address. - server_domain: - description: - - Server name. The value is a string of 1 to 255 case-sensitive characters. - is_default_vpn: - description: - - Use the default VPN or not. - type: bool - default: 'no' - vrf_name: - description: - - VPN name on a log server. The value is a string of 1 to 31 case-sensitive characters. - The default value is _public_. - level: - description: - - Level of logs saved on a log server. - choices: ['emergencies','alert','critical','error','warning','notification','informational','debugging'] - server_port: - description: - - Number of a port sending logs.The value is an integer ranging from 1 to 65535. - For UDP, the default value is 514. For TCP, the default value is 601. For TSL, the default value is 6514. - facility: - description: - - Log record tool. - choices: ['local0','local1','local2','local3','local4','local5','local6','local7'] - channel_name: - description: - - Channel name. The value is a string of 1 to 30 case-sensitive characters. - timestamp: - description: - - Log server timestamp. The value is of the enumerated type and case-sensitive. - choices: ['UTC', 'localtime'] - transport_mode: - description: - - Transport mode. The value is of the enumerated type and case-sensitive. - choices: ['tcp','udp'] - ssl_policy_name: - description: - - SSL policy name. The value is a string of 1 to 23 case-sensitive characters. - source_ip: - description: - - Log source ip address, IPv4 or IPv6 type. The value is a string of 0 to 255. - The value can be an valid IPv4 or IPv6 address. - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] -''' -EXAMPLES = ''' -- name: info center global module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Config info-center enable - ce_info_center_global: - info_center_enable: true - state: present - provider: "{{ cli }}" - - - name: Config statistic-suppress enable - ce_info_center_global: - suppress_enable: true - state: present - provider: "{{ cli }}" - - - name: Config info-center syslog packet-priority 1 - ce_info_center_global: - packet_priority: 2 - state: present - provider: "{{ cli }}" - - - name: Config info-center channel 1 name aaa - ce_info_center_global: - channel_id: 1 - channel_cfg_name: aaa - state: present - provider: "{{ cli }}" - - - name: Config info-center logfile size 10 - ce_info_center_global: - logfile_max_num: 10 - state: present - provider: "{{ cli }}" - - - name: Config info-center console channel 1 - ce_info_center_global: - channel_out_direct: console - channel_id: 1 - state: present - provider: "{{ cli }}" - - - name: Config info-center filter-id bymodule-alias snmp snmp_ipunlock - ce_info_center_global: - filter_feature_name: SNMP - filter_log_name: SNMP_IPLOCK - state: present - provider: "{{ cli }}" - - - - name: Config info-center max-logfile-number 16 - ce_info_center_global: - logfile_max_size: 16 - state: present - provider: "{{ cli }}" - - - name: Config syslog loghost domain. - ce_info_center_global: - server_domain: aaa - vrf_name: aaa - channel_id: 1 - transport_mode: tcp - facility: local4 - server_port: 100 - level: alert - timestamp: UTC - state: present - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"channel_id": "1", "facility": "local4", "is_default_vpn": True, "level": "alert", "server_domain": "aaa", - "server_port": "100", "state": "present", "timestamp": "localtime", "transport_mode": "tcp"} -existing: - description: k/v pairs of existing rollback - returned: always - type: dict - sample: - "server_domain_info": [ - { - "chnlId": "1", - "chnlName": "monitor", - "facility": "local4", - "isBriefFmt": "false", - "isDefaultVpn": "false", - "level": "alert", - "serverDomain": "aaa", - "serverPort": "100", - "sourceIP": "0.0.0.0", - "sslPolicyName": "gmc", - "timestamp": "UTC", - "transportMode": "tcp", - "vrfName": "aaa" - } - ] -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: - "server_domain_info": [ - { - "chnlId": "1", - "chnlName": "monitor", - "facility": "local4", - "isBriefFmt": "false", - "isDefaultVpn": "true", - "level": "alert", - "serverDomain": "aaa", - "serverPort": "100", - "sourceIP": "0.0.0.0", - "sslPolicyName": null, - "timestamp": "localtime", - "transportMode": "tcp", - "vrfName": "_public_" - }, - { - "chnlId": "1", - "chnlName": "monitor", - "facility": "local4", - "isBriefFmt": "false", - "isDefaultVpn": "false", - "level": "alert", - "serverDomain": "aaa", - "serverPort": "100", - "sourceIP": "0.0.0.0", - "sslPolicyName": "gmc", - "timestamp": "UTC", - "transportMode": "tcp", - "vrfName": "aaa" - } - ] -updates: - description: command sent to the device - returned: always - type: list - sample: ["info-center loghost domain aaa level alert port 100 facility local4 channel 1 localtime transport tcp"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, get_nc_config, set_nc_config, check_ip_addr - - -CE_NC_GET_CENTER_GLOBAL_INFO_HEADER = """ - - - -""" -CE_NC_GET_CENTER_GLOBAL_INFO_TAIL = """ - - - -""" - -CE_NC_MERGE_CENTER_GLOBAL_INFO_HEADER = """ - - - -""" - -CE_NC_MERGE_CENTER_GLOBAL_INFO_TAIL = """ - - - -""" - -CE_NC_GET_LOG_FILE_INFO_HEADER = """ - - - - -""" -CE_NC_GET_LOG_FILE_INFO_TAIL = """ - - - - -""" - -CE_NC_MERGE_LOG_FILE_INFO_HEADER = """ - - - - -""" - -CE_NC_MERGE_LOG_FILE_INFO_TAIL = """ - - - - -""" - - -CE_NC_GET_CHANNEL_INFO = """ - - - - - %s - - - - - -""" - -CE_NC_MERGE_CHANNEL_INFO_HEADER = """ - - - - -""" -CE_NC_MERGE_CHANNEL_INFO_TAIL = """ - - - - -""" - -CE_NC_GET_CHANNEL_DIRECT_INFO = """ - - - - - %s - - - - - -""" -CE_NC_MERGE_CHANNEL_DIRECT_HEADER = """ - - - - -""" - -CE_NC_MERGE_CHANNEL_DIRECT_TAIL = """ - - - - -""" - -CE_NC_GET_FILTER_INFO = """ - - - - - - - - - - -""" - -CE_NC_CREATE_CHANNEL_FILTER_HEADER = """ - - - - - -""" -CE_NC_CREATE_CHANNEL_FILTER_TAIL = """ - - - - -""" -CE_NC_DELETE_CHANNEL_FILTER_HEADER = """ - - - - - -""" -CE_NC_DELETE_CHANNEL_FILTER_TAIL = """ - - - - -""" - -CE_NC_GET_SERVER_IP_INFO_HEADER = """ - - - - - %s - %s - %s - %s -""" -CE_NC_GET_SERVER_IP_INFO_TAIL = """ - - - - -""" -CE_NC_MERGE_SERVER_IP_INFO_HEADER = """ - - - - - %s - %s - %s - %s -""" -CE_NC_MERGE_SERVER_IP_INFO_TAIL = """ - - - - -""" -CE_NC_DELETE_SERVER_IP_INFO_HEADER = """ - - - - - %s - %s - %s - %s -""" -CE_NC_DELETE_SERVER_IP_INFO_TAIL = """ - - - - -""" -CE_NC_GET_SERVER_DNS_INFO_HEADER = """ - - - - -""" - -CE_NC_GET_SERVER_DNS_INFO_TAIL = """ - - - - -""" - -CE_NC_MERGE_SERVER_DNS_INFO_HEADER = """ - - - - - %s - %s - %s -""" -CE_NC_MERGE_SERVER_DNS_INFO_TAIL = """ - - - - -""" - -CE_NC_DELETE_SERVER_DNS_INFO_HEADER = """ - - - - - %s - %s - %s -""" -CE_NC_DELETE_SERVER_DNS_INFO_TAIL = """ - - - - -""" - - -def get_out_direct_default(out_direct): - """get default out direct""" - - outdict = {"console": "1", "monitor": "2", "trapbuffer": "3", - "logbuffer": "4", "snmp": "5", "logfile": "6"} - channel_id_default = outdict.get(out_direct) - return channel_id_default - - -def get_channel_name_default(channel_id): - """get default out direct""" - - channel_dict = {"0": "console", "1": "monitor", "2": "loghost", "3": "trapbuffer", "4": "logbuffer", - "5": "snmpagent", "6": "channel6", "7": "channel7", "8": "channel8", "9": "channel9"} - channel_name_default = channel_dict.get(channel_id) - return channel_name_default - - -class InfoCenterGlobal(object): - """ - Manages info center global configuration. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.info_center_enable = self.module.params['info_center_enable'] or None - self.packet_priority = self.module.params['packet_priority'] or None - self.suppress_enable = self.module.params['suppress_enable'] or None - self.logfile_max_num = self.module.params['logfile_max_num'] or None - self.logfile_max_size = self.module.params['logfile_max_size'] or None - self.channel_id = self.module.params['channel_id'] or None - self.channel_cfg_name = self.module.params['channel_cfg_name'] or None - self.channel_out_direct = self.module.params['channel_out_direct'] or None - self.filter_feature_name = self.module.params['filter_feature_name'] or None - self.filter_log_name = self.module.params['filter_log_name'] or None - self.ip_type = self.module.params['ip_type'] or None - self.server_ip = self.module.params['server_ip'] or None - self.server_domain = self.module.params['server_domain'] or None - self.is_default_vpn = self.module.params['is_default_vpn'] or None - self.vrf_name = self.module.params['vrf_name'] or None - self.level = self.module.params['level'] or None - self.server_port = self.module.params['server_port'] or None - self.facility = self.module.params['facility'] or None - self.channel_name = self.module.params['channel_name'] or None - self.timestamp = self.module.params['timestamp'] or None - self.transport_mode = self.module.params['transport_mode'] or None - self.ssl_policy_name = self.module.params['ssl_policy_name'] or None - self.source_ip = self.module.params['source_ip'] or None - self.state = self.module.params['state'] or None - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.existing = dict() - self.proposed = dict() - self.end_state = dict() - - # syslog info - self.cur_global_info = None - self.cur_logfile_info = None - self.channel_info = None - self.channel_direct_info = None - self.filter_info = None - self.server_ip_info = None - self.server_domain_info = None - - def init_module(self): - """ init module """ - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, con_obj, xml_name): - """Check if response message is already succeed.""" - - xml_str = con_obj.xml - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_channel_dict(self): - """ get channel attributes dict.""" - - channel_info = dict() - # get channel info - conf_str = CE_NC_GET_CHANNEL_INFO % self.channel_id - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return channel_info - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - channel_info["channelInfos"] = list() - channels = root.findall("syslog/icChannels/icChannel") - if channels: - for channel in channels: - channel_dict = dict() - for ele in channel: - if ele.tag in ["icChnlId", "icChnlCfgName"]: - channel_dict[ele.tag] = ele.text - channel_info["channelInfos"].append(channel_dict) - return channel_info - - def is_exist_channel_id_name(self, channel_id, channel_name): - """if channel id exist""" - - if not self.channel_info: - return False - - for id2name in self.channel_info["channelInfos"]: - if id2name["icChnlId"] == channel_id and id2name["icChnlCfgName"] == channel_name: - return True - return False - - def config_merge_syslog_channel(self, channel_id, channel_name): - """config channel id""" - - if not self.is_exist_channel_id_name(channel_id, channel_name): - conf_str = CE_NC_MERGE_CHANNEL_INFO_HEADER - if channel_id: - conf_str += "%s" % channel_id - if channel_name: - conf_str += "%s" % channel_name - - conf_str += CE_NC_MERGE_CHANNEL_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge syslog channel id failed.') - - self.updates_cmd.append( - "info-center channel %s name %s" % (channel_id, channel_name)) - self.changed = True - - def delete_merge_syslog_channel(self, channel_id, channel_name): - """delete channel id""" - - change_flag = False - - if channel_name: - for id2name in self.channel_info["channelInfos"]: - channel_default_name = get_channel_name_default( - id2name["icChnlId"]) - if id2name["icChnlId"] == channel_id and id2name["icChnlCfgName"] == channel_name: - channel_name = channel_default_name - change_flag = True - - if not channel_name: - for id2name in self.channel_info["channelInfos"]: - channel_default_name = get_channel_name_default( - id2name["icChnlId"]) - if id2name["icChnlId"] == channel_id and id2name["icChnlCfgName"] != channel_default_name: - channel_name = channel_default_name - change_flag = True - if change_flag: - conf_str = CE_NC_MERGE_CHANNEL_INFO_HEADER - if channel_id: - conf_str += "%s" % channel_id - if channel_name: - conf_str += "%s" % channel_name - - conf_str += CE_NC_MERGE_CHANNEL_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge syslog channel id failed.') - - self.updates_cmd.append("undo info-center channel %s" % channel_id) - self.changed = True - - def get_channel_direct_dict(self): - """ get channel direct attributes dict.""" - - channel_direct_info = dict() - # get channel direct info - conf_str = CE_NC_GET_CHANNEL_DIRECT_INFO % self.channel_out_direct - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return channel_direct_info - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - channel_direct_info["channelDirectInfos"] = list() - dir_channels = root.findall("syslog/icDirChannels/icDirChannel") - if dir_channels: - for ic_dir_channel in dir_channels: - channel_direct_dict = dict() - for ele in ic_dir_channel: - if ele.tag in ["icOutDirect", "icCfgChnlId"]: - channel_direct_dict[ele.tag] = ele.text - channel_direct_info["channelDirectInfos"].append( - channel_direct_dict) - return channel_direct_info - - def is_exist_out_direct(self, out_direct, channel_id): - """if channel out direct exist""" - - if not self.channel_direct_info: - return False - - for id2name in self.channel_direct_info["channelDirectInfos"]: - if id2name["icOutDirect"] == out_direct and id2name["icCfgChnlId"] == channel_id: - return True - return False - - def config_merge_out_direct(self, out_direct, channel_id): - """config out direct""" - - if not self.is_exist_out_direct(out_direct, channel_id): - conf_str = CE_NC_MERGE_CHANNEL_DIRECT_HEADER - if out_direct: - conf_str += "%s" % out_direct - if channel_id: - conf_str += "%s" % channel_id - - conf_str += CE_NC_MERGE_CHANNEL_DIRECT_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge syslog channel out direct failed.') - - self.updates_cmd.append( - "info-center %s channel %s" % (out_direct, channel_id)) - self.changed = True - - def delete_merge_out_direct(self, out_direct, channel_id): - """delete out direct""" - - change_flag = False - channel_id_default = get_out_direct_default(out_direct) - if channel_id: - for id2name in self.channel_direct_info["channelDirectInfos"]: - if id2name["icOutDirect"] == out_direct and id2name["icCfgChnlId"] == channel_id: - if channel_id != channel_id_default: - channel_id = channel_id_default - change_flag = True - - if not channel_id: - for id2name in self.channel_direct_info["channelDirectInfos"]: - if id2name["icOutDirect"] == out_direct and id2name["icCfgChnlId"] != channel_id_default: - channel_id = channel_id_default - change_flag = True - - if change_flag: - conf_str = CE_NC_MERGE_CHANNEL_DIRECT_HEADER - if out_direct: - conf_str += "%s" % out_direct - if channel_id: - conf_str += "%s" % channel_id - - conf_str += CE_NC_MERGE_CHANNEL_DIRECT_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge syslog channel out direct failed.') - - self.updates_cmd.append("undo info-center logfile channel") - self.changed = True - - def get_filter_dict(self): - """ get syslog filter attributes dict.""" - - filter_info = dict() - # get filter info - conf_str = CE_NC_GET_FILTER_INFO - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return filter_info - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - filter_info["filterInfos"] = list() - ic_filters = root.findall("syslog/icFilters/icFilter") - if ic_filters: - for ic_filter in ic_filters: - filter_dict = dict() - for ele in ic_filter: - if ele.tag in ["icFeatureName", "icFilterLogName"]: - filter_dict[ele.tag] = ele.text - filter_info["filterInfos"].append(filter_dict) - return filter_info - - def is_exist_filter(self, filter_feature_name, filter_log_name): - """if filter info exist""" - - if not self.filter_info: - return False - for id2name in self.filter_info["filterInfos"]: - if id2name["icFeatureName"] == filter_feature_name and id2name["icFilterLogName"] == filter_log_name: - return True - return False - - def config_merge_filter(self, filter_feature_name, filter_log_name): - """config filter""" - - if not self.is_exist_filter(filter_feature_name, filter_log_name): - conf_str = CE_NC_CREATE_CHANNEL_FILTER_HEADER - conf_str += "true" - if filter_feature_name: - conf_str += "%s" % filter_feature_name - if filter_log_name: - conf_str += "%s" % filter_log_name - - conf_str += CE_NC_CREATE_CHANNEL_FILTER_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge syslog filter failed.') - - self.updates_cmd.append("info-center filter-id bymodule-alias %s %s" - % (filter_feature_name, filter_log_name)) - self.changed = True - - def delete_merge_filter(self, filter_feature_name, filter_log_name): - """delete filter""" - - change_flag = False - if self.is_exist_filter(filter_feature_name, filter_log_name): - for id2name in self.filter_info["filterInfos"]: - if id2name["icFeatureName"] == filter_feature_name and id2name["icFilterLogName"] == filter_log_name: - change_flag = True - if change_flag: - conf_str = CE_NC_DELETE_CHANNEL_FILTER_HEADER - conf_str += "true" - if filter_feature_name: - conf_str += "%s" % filter_feature_name - if filter_log_name: - conf_str += "%s" % filter_log_name - - conf_str += CE_NC_DELETE_CHANNEL_FILTER_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge syslog channel out direct failed.') - self.updates_cmd.append("undo info-center filter-id bymodule-alias %s %s" - % (filter_feature_name, filter_log_name)) - self.changed = True - - def get_server_ip_dict(self): - """ get server ip attributes dict.""" - - server_ip_info = dict() - # get server ip info - is_default_vpn = "false" - if not self.is_default_vpn: - self.is_default_vpn = False - if self.is_default_vpn is True: - is_default_vpn = "true" - if not self.vrf_name: - self.vrf_name = "_public_" - conf_str = CE_NC_GET_SERVER_IP_INFO_HEADER % ( - self.ip_type, self.server_ip, self.vrf_name, is_default_vpn) - conf_str += CE_NC_GET_SERVER_IP_INFO_TAIL - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return server_ip_info - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - server_ip_info["serverIpInfos"] = list() - syslog_servers = root.findall("syslog/syslogServers/syslogServer") - if syslog_servers: - for syslog_server in syslog_servers: - server_dict = dict() - for ele in syslog_server: - if ele.tag in ["ipType", "serverIp", "vrfName", "level", "serverPort", "facility", "chnlId", - "chnlName", "timestamp", "transportMode", "sslPolicyName", "isDefaultVpn", - "sourceIP", "isBriefFmt"]: - server_dict[ele.tag] = ele.text - server_ip_info["serverIpInfos"].append(server_dict) - return server_ip_info - - def config_merge_loghost(self): - """config loghost ip or dns""" - - conf_str = "" - is_default_vpn = "false" - if self.is_default_vpn is True: - is_default_vpn = "true" - if self.ip_type: - conf_str = CE_NC_MERGE_SERVER_IP_INFO_HEADER % (self.ip_type, self.server_ip, self.vrf_name, - is_default_vpn) - elif self.server_domain: - conf_str = CE_NC_MERGE_SERVER_DNS_INFO_HEADER % ( - self.server_domain, self.vrf_name, is_default_vpn) - if self.level: - conf_str += "%s" % self.level - if self.server_port: - conf_str += "%s" % self.server_port - if self.facility: - conf_str += "%s" % self.facility - if self.channel_id: - conf_str += "%s" % self.channel_id - if self.channel_name: - conf_str += "%s" % self.channel_name - if self.timestamp: - conf_str += "%s" % self.timestamp - if self.transport_mode: - conf_str += "%s" % self.transport_mode - if self.ssl_policy_name: - conf_str += "%s" % self.ssl_policy_name - if self.source_ip: - conf_str += "%s" % self.source_ip - if self.ip_type: - conf_str += CE_NC_MERGE_SERVER_IP_INFO_TAIL - elif self.server_domain: - conf_str += CE_NC_MERGE_SERVER_DNS_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge server loghost failed.') - - cmd = "info-center loghost" - if self.ip_type == "ipv4" and self.server_ip: - cmd += " %s" % self.server_ip - if self.ip_type == "ipv6" and self.server_ip: - cmd += " ipv6 %s" % self.server_ip - if self.server_domain: - cmd += " domain %s" % self.server_domain - if self.channel_id: - cmd += " channel %s" % self.channel_id - if self.channel_name: - cmd += " channel %s" % self.channel_name - if self.vrf_name: - if self.vrf_name != "_public_": - cmd += " vpn-instance %s" % self.vrf_name - if self.source_ip: - cmd += " source-ip %s" % self.source_ip - if self.facility: - cmd += " facility %s" % self.facility - if self.server_port: - cmd += " port %s" % self.server_port - if self.level: - cmd += " level %s" % self.level - if self.timestamp: - if self.timestamp == "localtime": - cmd += " local-time" - else: - cmd += " utc" - if self.transport_mode: - cmd += " transport %s" % self.transport_mode - if self.ssl_policy_name: - cmd += " ssl-policy %s" % self.ssl_policy_name - self.updates_cmd.append(cmd) - self.changed = True - - def delete_merge_loghost(self): - """delete loghost ip or dns""" - - conf_str = "" - is_default_vpn = "false" - if self.is_default_vpn is True: - is_default_vpn = "true" - if self.ip_type: - conf_str = CE_NC_DELETE_SERVER_IP_INFO_HEADER % (self.ip_type, self.server_ip, self.vrf_name, - is_default_vpn) - elif self.server_domain: - conf_str = CE_NC_DELETE_SERVER_DNS_INFO_HEADER % ( - self.server_domain, self.vrf_name, is_default_vpn) - if self.level: - conf_str += "%s" % self.level - if self.server_port: - conf_str += "%s" % self.server_port - if self.facility: - conf_str += "%s" % self.facility - if self.channel_id: - conf_str += "%s" % self.channel_id - if self.channel_name: - conf_str += "%s" % self.channel_name - if self.timestamp: - conf_str += "%s" % self.timestamp - if self.transport_mode: - conf_str += "%s" % self.transport_mode - if self.ssl_policy_name: - conf_str += "%s" % self.ssl_policy_name - if self.source_ip: - conf_str += "%s" % self.source_ip - if self.ip_type: - conf_str += CE_NC_DELETE_SERVER_IP_INFO_TAIL - elif self.server_domain: - conf_str += CE_NC_DELETE_SERVER_DNS_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge server loghost failed.') - - cmd = "undo info-center loghost" - if self.ip_type == "ipv4" and self.server_ip: - cmd += " %s" % self.server_ip - if self.ip_type == "ipv6" and self.server_ip: - cmd += " ipv6 %s" % self.server_ip - if self.server_domain: - cmd += " domain %s" % self.server_domain - if self.vrf_name: - if self.vrf_name != "_public_": - cmd += " vpn-instance %s" % self.vrf_name - self.updates_cmd.append(cmd) - self.changed = True - - def get_server_domain_dict(self): - """ get server domain attributes dict""" - - server_domain_info = dict() - # get server domain info - if not self.is_default_vpn: - self.is_default_vpn = False - if not self.vrf_name: - self.vrf_name = "_public_" - conf_str = CE_NC_GET_SERVER_DNS_INFO_HEADER - conf_str += CE_NC_GET_SERVER_DNS_INFO_TAIL - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return server_domain_info - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - server_domain_info["serverAddressInfos"] = list() - syslog_dnss = root.findall("syslog/syslogDNSs/syslogDNS") - if syslog_dnss: - for syslog_dns in syslog_dnss: - dns_dict = dict() - for ele in syslog_dns: - if ele.tag in ["serverDomain", "vrfName", "level", "serverPort", "facility", "chnlId", - "chnlName", "timestamp", "transportMode", "sslPolicyName", "isDefaultVpn", - "sourceIP", "isBriefFmt"]: - dns_dict[ele.tag] = ele.text - server_domain_info["serverAddressInfos"].append(dns_dict) - - return server_domain_info - - def check_need_loghost_cfg(self): - """ check need cfg""" - - need_cfg = False - find_flag = False - if self.ip_type and self.server_ip: - if self.server_ip_info: - for tmp in self.server_ip_info["serverIpInfos"]: - find_flag = True - if self.ip_type and tmp.get("ipType") != self.ip_type: - find_flag = False - if self.server_ip and tmp.get("serverIp") != self.server_ip: - find_flag = False - if self.vrf_name and tmp.get("vrfName") != self.vrf_name: - find_flag = False - if self.level and tmp.get("level") != self.level: - find_flag = False - if self.server_port and tmp.get("serverPort") != self.server_port: - find_flag = False - if self.facility and tmp.get("facility") != self.facility: - find_flag = False - if self.channel_id and tmp.get("chnlId") != self.channel_id: - find_flag = False - if self.channel_name and tmp.get("chnlName") != self.channel_name: - find_flag = False - if self.timestamp and tmp.get("timestamp") != self.timestamp: - find_flag = False - if self.transport_mode and tmp.get("transportMode") != self.transport_mode: - find_flag = False - if self.ssl_policy_name and tmp.get("sslPolicyName") != self.ssl_policy_name: - find_flag = False - if self.source_ip and tmp.get("sourceIP") != self.source_ip: - find_flag = False - if find_flag: - break - elif self.server_domain: - if self.server_domain_info: - for tmp in self.server_domain_info["serverAddressInfos"]: - find_flag = True - if self.server_domain and tmp.get("serverDomain") != self.server_domain: - find_flag = False - if self.vrf_name and tmp.get("vrfName") != self.vrf_name: - find_flag = False - if self.level and tmp.get("level") != self.level: - find_flag = False - if self.server_port and tmp.get("serverPort") != self.server_port: - find_flag = False - if self.facility and tmp.get("facility") != self.facility: - find_flag = False - if self.channel_id and tmp.get("chnlId") != self.channel_id: - find_flag = False - if self.channel_name and tmp.get("chnlName") != self.channel_name: - find_flag = False - if self.timestamp and tmp.get("timestamp") != self.timestamp: - find_flag = False - if self.transport_mode and tmp.get("transportMode") != self.transport_mode: - find_flag = False - if self.ssl_policy_name and tmp.get("sslPolicyName") != self.ssl_policy_name: - find_flag = False - if self.source_ip and tmp.get("sourceIP") != self.source_ip: - find_flag = False - if find_flag: - break - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - elif self.state == "absent": - need_cfg = bool(find_flag) - return need_cfg - - def get_syslog_global(self): - """get syslog global attributes""" - - cur_global_info = dict() - conf_str = CE_NC_GET_CENTER_GLOBAL_INFO_HEADER - if self.info_center_enable: - conf_str += "" - if self.packet_priority: - conf_str += "" - if self.suppress_enable: - conf_str += "" - conf_str += CE_NC_GET_CENTER_GLOBAL_INFO_TAIL - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return cur_global_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - global_info = root.findall( - "syslog/globalParam") - - if global_info: - for tmp in global_info: - for site in tmp: - if site.tag in ["icEnable", "packetPriority", "suppressEnable"]: - cur_global_info[site.tag] = site.text - return cur_global_info - - def merge_syslog_global(self): - """config global""" - - conf_str = CE_NC_MERGE_CENTER_GLOBAL_INFO_HEADER - if self.info_center_enable: - conf_str += "%s" % self.info_center_enable - if self.packet_priority: - if self.state == "present": - packet_priority = self.packet_priority - else: - packet_priority = 0 - conf_str += "%s" % packet_priority - if self.suppress_enable: - conf_str += "%s" % self.suppress_enable - - conf_str += CE_NC_MERGE_CENTER_GLOBAL_INFO_TAIL - - if self.info_center_enable == "true" and self.cur_global_info["icEnable"] != self.info_center_enable: - cmd = "info-center enable" - self.updates_cmd.append(cmd) - self.changed = True - if self.suppress_enable == "true" and self.cur_global_info["suppressEnable"] != self.suppress_enable: - cmd = "info-center statistic-suppress enable" - self.updates_cmd.append(cmd) - self.changed = True - if self.info_center_enable == "false" and self.cur_global_info["icEnable"] != self.info_center_enable: - cmd = "undo info-center enable" - self.updates_cmd.append(cmd) - self.changed = True - if self.suppress_enable == "false" and self.cur_global_info["suppressEnable"] != self.suppress_enable: - cmd = "undo info-center statistic-suppress enable" - self.updates_cmd.append(cmd) - self.changed = True - - if self.state == "present": - if self.packet_priority: - if self.cur_global_info["packetPriority"] != self.packet_priority: - cmd = "info-center syslog packet-priority %s" % self.packet_priority - self.updates_cmd.append(cmd) - self.changed = True - if self.state == "absent": - if self.packet_priority: - if self.cur_global_info["packetPriority"] == self.packet_priority: - cmd = "undo info-center syslog packet-priority %s" % self.packet_priority - self.updates_cmd.append(cmd) - self.changed = True - if self.changed: - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge syslog global failed.') - - def get_syslog_logfile(self): - """get syslog logfile""" - - cur_logfile_info = dict() - conf_str = CE_NC_GET_LOG_FILE_INFO_HEADER - conf_str += "log" - if self.logfile_max_num: - conf_str += "" - if self.logfile_max_size: - conf_str += "" - conf_str += CE_NC_GET_LOG_FILE_INFO_TAIL - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return cur_logfile_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - logfile_info = root.findall( - "syslog/icLogFileInfos/icLogFileInfo") - if logfile_info: - for tmp in logfile_info: - for site in tmp: - if site.tag in ["maxFileNum", "maxFileSize"]: - cur_logfile_info[site.tag] = site.text - return cur_logfile_info - - def merge_syslog_logfile(self): - """config logfile""" - - logfile_max_num = "200" - conf_str = CE_NC_MERGE_LOG_FILE_INFO_HEADER - if self.logfile_max_num: - if self.state == "present": - logfile_max_num = self.logfile_max_num - else: - if self.logfile_max_num != "200" and self.cur_logfile_info["maxFileNum"] == self.logfile_max_num: - logfile_max_num = "200" - conf_str += "%s" % logfile_max_num - - if self.logfile_max_size: - logfile_max_size = "32" - if self.state == "present": - logfile_max_size = self.logfile_max_size - else: - if self.logfile_max_size != "32" and self.cur_logfile_info["maxFileSize"] == self.logfile_max_size: - logfile_max_size = "32" - conf_str += "%s" % logfile_max_size - - conf_str += "log" - conf_str += CE_NC_MERGE_LOG_FILE_INFO_TAIL - - if self.state == "present": - if self.logfile_max_num: - if self.cur_logfile_info["maxFileNum"] != self.logfile_max_num: - cmd = "info-center max-logfile-number %s" % self.logfile_max_num - self.updates_cmd.append(cmd) - self.changed = True - if self.logfile_max_size: - if self.cur_logfile_info["maxFileSize"] != self.logfile_max_size: - cmd = "info-center logfile size %s" % self.logfile_max_size - self.updates_cmd.append(cmd) - self.changed = True - if self.state == "absent": - if self.logfile_max_num and self.logfile_max_num != "200": - if self.cur_logfile_info["maxFileNum"] == self.logfile_max_num: - cmd = "undo info-center max-logfile-number" - self.updates_cmd.append(cmd) - self.changed = True - if self.logfile_max_size and self.logfile_max_size != "32": - if self.cur_logfile_info["maxFileSize"] == self.logfile_max_size: - cmd = "undo info-center logfile size" - self.updates_cmd.append(cmd) - self.changed = True - - if self.changed: - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge syslog logfile failed.') - - def check_params(self): - """Check all input params""" - - # packet_priority check - if self.packet_priority: - if not self.packet_priority.isdigit(): - self.module.fail_json( - msg='Error: The parameter of packet priority is invalid.') - if int(self.packet_priority) > 7 or int(self.packet_priority) < 0: - self.module.fail_json( - msg='Error: The packet priority must be an integer between 0 and 7.') - - # logfile_max_num check - if self.logfile_max_num: - if not self.logfile_max_num.isdigit(): - self.module.fail_json( - msg='Error: The parameter of logfile_max_num is invalid.') - if int(self.logfile_max_num) > 500 or int(self.logfile_max_num) < 3: - self.module.fail_json( - msg='Error: The logfile_max_num must be an integer between 3 and 500.') - - # channel_id check - if self.channel_id: - if not self.channel_id.isdigit(): - self.module.fail_json( - msg='Error: The parameter of channel_id is invalid.') - if int(self.channel_id) > 9 or int(self.channel_id) < 0: - self.module.fail_json( - msg='Error: The channel_id must be an integer between 0 and 9.') - - # channel_cfg_name check - if self.channel_cfg_name: - if len(self.channel_cfg_name) > 30 \ - or len(self.channel_cfg_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: channel_cfg_name is not in the range from 1 to 30.') - - # filter_feature_name check - if self.filter_feature_name: - if len(self.filter_feature_name) > 31 \ - or len(self.filter_feature_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: filter_feature_name is not in the range from 1 to 31.') - - # filter_log_name check - if self.filter_log_name: - if len(self.filter_log_name) > 63 \ - or len(self.filter_log_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: filter_log_name is not in the range from 1 to 63.') - - # server_ip check - if self.server_ip: - if not check_ip_addr(self.server_ip): - self.module.fail_json( - msg='Error: The %s is not a valid ip address' % self.server_ip) - # source_ip check - if self.source_ip: - if not check_ip_addr(self.source_ip): - self.module.fail_json( - msg='Error: The %s is not a valid ip address' % self.source_ip) - - # server_domain check - if self.server_domain: - if len(self.server_domain) > 255 \ - or len(self.server_domain.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: server_domain is not in the range from 1 to 255.') - - # vrf_name check - if self.vrf_name: - if len(self.vrf_name) > 31 \ - or len(self.vrf_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: vrf_name is not in the range from 1 to 31.') - - # server_port check - if self.server_port: - if not self.server_port.isdigit(): - self.module.fail_json( - msg='Error: The parameter of server_port is invalid.') - if int(self.server_port) > 65535 or int(self.server_port) < 1: - self.module.fail_json( - msg='Error: The server_port must be an integer between 1 and 65535.') - - # channel_name check - if self.channel_name: - if len(self.channel_name) > 31 \ - or len(self.channel_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: channel_name is not in the range from 1 to 30.') - - # ssl_policy_name check - if self.ssl_policy_name: - if len(self.ssl_policy_name) > 23 \ - or len(self.ssl_policy_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: ssl_policy_name is not in the range from 1 to 23.') - - def get_proposed(self): - """get proposed info""" - - if self.info_center_enable: - self.proposed["info_center_enable"] = self.info_center_enable - if self.packet_priority: - self.proposed["packet_priority"] = self.packet_priority - if self.suppress_enable: - self.proposed["suppress_enable"] = self.suppress_enable - if self.logfile_max_num: - self.proposed["logfile_max_num"] = self.logfile_max_num - if self.logfile_max_size: - self.proposed["logfile_max_size"] = self.logfile_max_size - if self.channel_id: - self.proposed["channel_id"] = self.channel_id - if self.channel_cfg_name: - self.proposed["channel_cfg_name"] = self.channel_cfg_name - if self.channel_out_direct: - self.proposed["channel_out_direct"] = self.channel_out_direct - if self.filter_feature_name: - self.proposed["filter_feature_name"] = self.filter_feature_name - if self.filter_log_name: - self.proposed["filter_log_name"] = self.filter_log_name - if self.ip_type: - self.proposed["ip_type"] = self.ip_type - if self.server_ip: - self.proposed["server_ip"] = self.server_ip - if self.server_domain: - self.proposed["server_domain"] = self.server_domain - if self.vrf_name: - self.proposed["vrf_name"] = self.vrf_name - if self.level: - self.proposed["level"] = self.level - if self.server_port: - self.proposed["server_port"] = self.server_port - if self.facility: - self.proposed["facility"] = self.facility - if self.channel_name: - self.proposed["channel_name"] = self.channel_name - if self.timestamp: - self.proposed["timestamp"] = self.timestamp - if self.ssl_policy_name: - self.proposed["ssl_policy_name"] = self.ssl_policy_name - if self.transport_mode: - self.proposed["transport_mode"] = self.transport_mode - if self.is_default_vpn: - self.proposed["is_default_vpn"] = self.is_default_vpn - if self.source_ip: - self.proposed["source_ip"] = self.source_ip - if self.state: - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if self.info_center_enable: - self.existing["info_center_enable"] = self.cur_global_info[ - "icEnable"] - if self.packet_priority: - self.existing["packet_priority"] = self.cur_global_info[ - "packetPriority"] - if self.suppress_enable: - self.existing["suppress_enable"] = self.cur_global_info[ - "suppressEnable"] - if self.logfile_max_num: - self.existing["logfile_max_num"] = self.cur_logfile_info[ - "maxFileNum"] - if self.logfile_max_size: - self.existing["logfile_max_size"] = self.cur_logfile_info[ - "maxFileSize"] - - if self.channel_id and self.channel_cfg_name: - if self.channel_info: - self.existing["channel_id_info"] = self.channel_info[ - "channelInfos"] - if self.channel_out_direct and self.channel_id: - if self.channel_direct_info: - self.existing["channel_out_direct_info"] = self.channel_direct_info[ - "channelDirectInfos"] - if self.filter_feature_name and self.filter_log_name: - if self.filter_info: - self.existing["filter_id_info"] = self.filter_info[ - "filterInfos"] - if self.ip_type: - if self.server_ip_info: - self.existing["server_ip_info"] = self.server_ip_info[ - "serverIpInfos"] - - if self.server_domain: - if self.server_domain_info: - self.existing["server_domain_info"] = self.server_domain_info[ - "serverAddressInfos"] - - def get_end_state(self): - """get end state info""" - - if self.info_center_enable or self.packet_priority or self.suppress_enable: - self.cur_global_info = self.get_syslog_global() - if self.logfile_max_num or self.logfile_max_size: - self.cur_logfile_info = self.get_syslog_logfile() - if self.channel_id and self.channel_cfg_name: - self.channel_info = self.get_channel_dict() - if self.channel_out_direct and self.channel_id: - self.channel_direct_info = self.get_channel_direct_dict() - if self.filter_feature_name and self.filter_log_name: - self.filter_info = self.get_filter_dict() - if self.ip_type: - self.server_ip_info = self.get_server_ip_dict() - if self.server_domain: - self.server_domain_info = self.get_server_domain_dict() - - if self.info_center_enable: - self.end_state[ - "info_center_enable"] = self.cur_global_info["icEnable"] - if self.packet_priority: - self.end_state["packet_priority"] = self.cur_global_info[ - "packetPriority"] - if self.suppress_enable: - self.end_state["suppress_enable"] = self.cur_global_info[ - "suppressEnable"] - if self.logfile_max_num: - self.end_state["logfile_max_num"] = self.cur_logfile_info[ - "maxFileNum"] - if self.logfile_max_size: - self.end_state["logfile_max_size"] = self.cur_logfile_info[ - "maxFileSize"] - - if self.channel_id and self.channel_cfg_name: - if self.channel_info: - self.end_state["channel_id_info"] = self.channel_info[ - "channelInfos"] - - if self.channel_out_direct and self.channel_id: - if self.channel_direct_info: - self.end_state["channel_out_direct_info"] = self.channel_direct_info[ - "channelDirectInfos"] - - if self.filter_feature_name and self.filter_log_name: - if self.filter_info: - self.end_state["filter_id_info"] = self.filter_info[ - "filterInfos"] - - if self.ip_type: - if self.server_ip_info: - self.end_state["server_ip_info"] = self.server_ip_info[ - "serverIpInfos"] - - if self.server_domain: - if self.server_domain_info: - self.end_state["server_domain_info"] = self.server_domain_info[ - "serverAddressInfos"] - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - if self.info_center_enable or self.packet_priority or self.suppress_enable: - self.cur_global_info = self.get_syslog_global() - if self.logfile_max_num or self.logfile_max_size: - self.cur_logfile_info = self.get_syslog_logfile() - if self.channel_id: - self.channel_info = self.get_channel_dict() - if self.channel_out_direct: - self.channel_direct_info = self.get_channel_direct_dict() - if self.filter_feature_name and self.filter_log_name: - self.filter_info = self.get_filter_dict() - if self.ip_type: - self.server_ip_info = self.get_server_ip_dict() - if self.server_domain: - self.server_domain_info = self.get_server_domain_dict() - self.get_existing() - self.get_proposed() - if self.info_center_enable or self.packet_priority or self.suppress_enable: - self.merge_syslog_global() - - if self.logfile_max_num or self.logfile_max_size: - self.merge_syslog_logfile() - - if self.server_ip: - if not self.ip_type: - self.module.fail_json( - msg='Error: ip_type and server_ip must be exist at the same time.') - if self.ip_type: - if not self.server_ip: - self.module.fail_json( - msg='Error: ip_type and server_ip must be exist at the same time.') - - if self.ip_type or self.server_domain or self.channel_id or self.filter_feature_name: - if self.ip_type and self.server_domain: - self.module.fail_json( - msg='Error: ip_type and server_domain can not be exist at the same time.') - if self.channel_id and self.channel_name: - self.module.fail_json( - msg='Error: channel_id and channel_name can not be exist at the same time.') - if self.ssl_policy_name: - if self.transport_mode == "udp": - self.module.fail_json( - msg='Error: transport_mode: udp does not support ssl_policy.') - if not self.transport_mode: - self.module.fail_json( - msg='Error: transport_mode, ssl_policy_name must be exist at the same time.') - if self.ip_type == "ipv6": - if self.vrf_name and self.vrf_name != "_public_": - self.module.fail_json( - msg='Error: ipType:ipv6 only support default vpn:_public_.') - if self.is_default_vpn is True: - if self.vrf_name: - if self.vrf_name != "_public_": - self.module.fail_json( - msg='Error: vrf_name should be _public_ when is_default_vpn is True.') - else: - self.vrf_name = "_public_" - else: - if self.vrf_name == "_public_": - self.module.fail_json( - msg='Error: The default vpn value is _public_, but is_default_vpn is False.') - if self.state == "present": - # info-center channel channel-number name channel-name - if self.channel_id and self.channel_cfg_name: - self.config_merge_syslog_channel( - self.channel_id, self.channel_cfg_name) - # info-center { console | logfile | monitor | snmp | logbuffer - # | trapbuffer } channel channel-number - if self.channel_out_direct and self.channel_id: - self.config_merge_out_direct( - self.channel_out_direct, self.channel_id) - # info-center filter-id bymodule-alias modname alias - if self.filter_feature_name and self.filter_log_name: - self.config_merge_filter( - self.filter_feature_name, self.filter_log_name) - if self.ip_type and self.server_ip: - if not self.vrf_name: - self.vrf_name = "_public_" - if self.check_need_loghost_cfg(): - self.config_merge_loghost() - if self.server_domain: - if not self.vrf_name: - self.vrf_name = "_public_" - if self.check_need_loghost_cfg(): - self.config_merge_loghost() - - elif self.state == "absent": - if self.channel_id: - self.delete_merge_syslog_channel( - self.channel_id, self.channel_cfg_name) - if self.channel_out_direct: - self.delete_merge_out_direct( - self.channel_out_direct, self.channel_id) - if self.filter_feature_name and self.filter_log_name: - self.delete_merge_filter( - self.filter_feature_name, self.filter_log_name) - if self.ip_type and self.server_ip: - if not self.vrf_name: - self.vrf_name = "_public_" - if self.check_need_loghost_cfg(): - self.delete_merge_loghost() - if self.server_domain: - if not self.vrf_name: - self.vrf_name = "_public_" - if self.check_need_loghost_cfg(): - self.delete_merge_loghost() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - info_center_enable=dict(choices=['true', 'false']), - packet_priority=dict(type='str'), - suppress_enable=dict(choices=['true', 'false']), - logfile_max_num=dict(type='str'), - logfile_max_size=dict(choices=['4', '8', '16', '32']), - channel_id=dict(type='str'), - channel_cfg_name=dict(type='str'), - channel_out_direct=dict(choices=['console', 'monitor', - 'trapbuffer', 'logbuffer', 'snmp', 'logfile']), - filter_feature_name=dict(type='str'), - filter_log_name=dict(type='str'), - ip_type=dict(choices=['ipv4', 'ipv6']), - server_ip=dict(type='str'), - server_domain=dict(type='str'), - is_default_vpn=dict(default=False, type='bool'), - vrf_name=dict(type='str'), - level=dict(choices=['emergencies', 'alert', 'critical', 'error', 'warning', 'notification', - 'informational', 'debugging']), - server_port=dict(type='str'), - facility=dict(choices=['local0', 'local1', 'local2', - 'local3', 'local4', 'local5', 'local6', 'local7']), - channel_name=dict(type='str'), - timestamp=dict(choices=['UTC', 'localtime']), - transport_mode=dict(choices=['tcp', 'udp']), - ssl_policy_name=dict(type='str'), - source_ip=dict(type='str'), - state=dict(choices=['present', 'absent'], default='present') - - ) - argument_spec.update(ce_argument_spec) - module = InfoCenterGlobal(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_info_center_log.py b/plugins/modules/network/cloudengine/ce_info_center_log.py deleted file mode 100644 index fc04cffdfb..0000000000 --- a/plugins/modules/network/cloudengine/ce_info_center_log.py +++ /dev/null @@ -1,548 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_info_center_log -short_description: Manages information center log configuration on HUAWEI CloudEngine switches. -description: - - Setting the Timestamp Format of Logs. - Configuring the Device to Output Logs to the Log Buffer. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - log_time_stamp: - description: - - Sets the timestamp format of logs. - choices: ['date_boot', 'date_second', 'date_tenthsecond', 'date_millisecond', - 'shortdate_second', 'shortdate_tenthsecond', 'shortdate_millisecond', - 'formatdate_second', 'formatdate_tenthsecond', 'formatdate_millisecond'] - log_buff_enable: - description: - - Enables the Switch to send logs to the log buffer. - default: no_use - choices: ['no_use','true', 'false'] - log_buff_size: - description: - - Specifies the maximum number of logs in the log buffer. - The value is an integer that ranges from 0 to 10240. If logbuffer-size is 0, logs are not displayed. - module_name: - description: - - Specifies the name of a module. - The value is a module name in registration logs. - channel_id: - description: - - Specifies a channel ID. - The value is an integer ranging from 0 to 9. - log_enable: - description: - - Indicates whether log filtering is enabled. - default: no_use - choices: ['no_use','true', 'false'] - log_level: - description: - - Specifies a log severity. - choices: ['emergencies', 'alert', 'critical', 'error', - 'warning', 'notification', 'informational', 'debugging'] - state: - description: - - Determines whether the config should be present or not - on the device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' - -- name: CloudEngine info center log test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Setting the timestamp format of logs" - ce_info_center_log: - log_time_stamp: date_tenthsecond - provider: "{{ cli }}" - - - name: "Enabled to output information to the log buffer" - ce_info_center_log: - log_buff_enable: true - provider: "{{ cli }}" - - - name: "Set the maximum number of logs in the log buffer" - ce_info_center_log: - log_buff_size: 100 - provider: "{{ cli }}" - - - name: "Set a rule for outputting logs to a channel" - ce_info_center_log: - module_name: aaa - channel_id: 1 - log_enable: true - log_level: critical - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"log_time_stamp": "date_tenthsecond", "state": "present"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"log_time_stamp": "date_second"} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"log_time_stamp": "date_tenthsecond"} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["info-center timestamp log date precision-time tenth-second"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_LOG = """ - - - - - - - - - - %s - %s - - - - - - - -""" - -CE_NC_GET_LOG_GLOBAL = """ - - - - - - - - - -""" - -TIME_STAMP_DICT = {"date_boot": "boot", - "date_second": "date precision-time second", - "date_tenthsecond": "date precision-time tenth-second", - "date_millisecond": "date precision-time millisecond", - "shortdate_second": "short-date precision-time second", - "shortdate_tenthsecond": "short-date precision-time tenth-second", - "shortdate_millisecond": "short-date precision-time millisecond", - "formatdate_second": "format-date precision-time second", - "formatdate_tenthsecond": "format-date precision-time tenth-second", - "formatdate_millisecond": "format-date precision-time millisecond"} - -CHANNEL_DEFAULT_LOG_STATE = {"0": "true", - "1": "true", - "2": "true", - "3": "false", - "4": "true", - "5": "false", - "6": "true", - "7": "true", - "8": "true", - "9": "true"} - -CHANNEL_DEFAULT_LOG_LEVEL = {"0": "warning", - "1": "warning", - "2": "informational", - "3": "informational", - "4": "warning", - "5": "debugging", - "6": "debugging", - "7": "warning", - "8": "debugging", - "9": "debugging"} - - -class InfoCenterLog(object): - """ - Manages information center log configuration - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.log_time_stamp = self.module.params['log_time_stamp'] - self.log_buff_enable = self.module.params['log_buff_enable'] - self.log_buff_size = self.module.params['log_buff_size'] - self.module_name = self.module.params['module_name'] - self.channel_id = self.module.params['channel_id'] - self.log_enable = self.module.params['log_enable'] - self.log_level = self.module.params['log_level'] - self.state = self.module.params['state'] - - # state - self.log_dict = dict() - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """init module""" - - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_log_dict(self): - """ log config dict""" - - log_dict = dict() - if self.module_name: - if self.module_name.lower() == "default": - conf_str = CE_NC_GET_LOG % (self.module_name.lower(), self.channel_id) - else: - conf_str = CE_NC_GET_LOG % (self.module_name.upper(), self.channel_id) - else: - conf_str = CE_NC_GET_LOG_GLOBAL - - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return log_dict - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # get global param info - glb = root.find("syslog/globalParam") - if glb: - for attr in glb: - if attr.tag in ["bufferSize", "logTimeStamp", "icLogBuffEn"]: - log_dict[attr.tag] = attr.text - - # get info-center source info - log_dict["source"] = dict() - src = root.find("syslog/icSources/icSource") - if src: - for attr in src: - if attr.tag in ["moduleName", "icChannelId", "icChannelName", "logEnFlg", "logEnLevel"]: - log_dict["source"][attr.tag] = attr.text - - return log_dict - - def config_log_global(self): - """config log global param""" - - xml_str = '' - if self.log_time_stamp: - if self.state == "present" and self.log_time_stamp.upper() != self.log_dict.get("logTimeStamp"): - xml_str += '%s' % self.log_time_stamp.upper() - self.updates_cmd.append( - "info-center timestamp log %s" % TIME_STAMP_DICT.get(self.log_time_stamp)) - elif self.state == "absent" and self.log_time_stamp.upper() == self.log_dict.get("logTimeStamp"): - xml_str += 'DATE_SECOND' # set default - self.updates_cmd.append("undo info-center timestamp log") - else: - pass - - if self.log_buff_enable != 'no_use': - if self.log_dict.get("icLogBuffEn") != self.log_buff_enable: - xml_str += '%s' % self.log_buff_enable - if self.log_buff_enable == "true": - self.updates_cmd.append("info-center logbuffer") - else: - self.updates_cmd.append("undo info-center logbuffer") - - if self.log_buff_size: - if self.state == "present" and self.log_dict.get("bufferSize") != self.log_buff_size: - xml_str += '%s' % self.log_buff_size - self.updates_cmd.append( - "info-center logbuffer size %s" % self.log_buff_size) - elif self.state == "absent" and self.log_dict.get("bufferSize") == self.log_buff_size: - xml_str += '512' - self.updates_cmd.append("undo info-center logbuffer size") - - if xml_str == '': - return "" - else: - xml_str += '' - return xml_str - - def config_log_soruce(self): - """config info-center sources""" - - xml_str = '' - if not self.module_name or not self.channel_id: - return xml_str - - source = self.log_dict["source"] - if self.state == "present": - xml_str = '' - cmd = 'info-center source %s channel %s log' % ( - self.module_name, self.channel_id) - else: - if not source or self.module_name != source.get("moduleName").lower() or \ - self.channel_id != source.get("icChannelId"): - return '' - - if self.log_enable == 'no_use' and not self.log_level: - xml_str = '' - else: - xml_str = '' - cmd = 'undo info-center source %s channel %s log' % ( - self.module_name, self.channel_id) - - xml_str += '%s%s' % ( - self.module_name, self.channel_id) - - # log_enable - if self.log_enable != 'no_use': - if self.state == "present" and (not source or self.log_enable != source.get("logEnFlg")): - xml_str += '%s' % self.log_enable - if self.log_enable == "true": - cmd += ' state on' - else: - cmd += ' state off' - elif self.state == "absent" and source and self.log_level == source.get("logEnLevel"): - xml_str += '%s' % CHANNEL_DEFAULT_LOG_STATE.get(self.channel_id) - cmd += ' state' - - # log_level - if self.log_level: - if self.state == "present" and (not source or self.log_level != source.get("logEnLevel")): - xml_str += '%s' % self.log_level - cmd += ' level %s' % self.log_level - elif self.state == "absent" and source and self.log_level == source.get("logEnLevel"): - xml_str += '%s' % CHANNEL_DEFAULT_LOG_LEVEL.get(self.channel_id) - cmd += ' level' - - if xml_str.endswith(""): - if self.log_enable == 'no_use' and not self.log_level and self.state == "absent": - xml_str += '' - self.updates_cmd.append(cmd) - return xml_str - else: - return '' - else: - xml_str += '' - self.updates_cmd.append(cmd) - return xml_str - - def netconf_load_config(self, xml_str): - """load log config by netconf""" - - if not xml_str: - return - - xml_cfg = """ - - - %s - - """ % xml_str - - recv_xml = set_nc_config(self.module, xml_cfg) - self.check_response(recv_xml, "SET_LOG") - self.changed = True - - def check_params(self): - """Check all input params""" - - # check log_buff_size ranges from 0 to 10240 - if self.log_buff_size: - if not self.log_buff_size.isdigit(): - self.module.fail_json( - msg="Error: log_buff_size is not digit.") - if int(self.log_buff_size) < 0 or int(self.log_buff_size) > 10240: - self.module.fail_json( - msg="Error: log_buff_size is not ranges from 0 to 10240.") - - # check channel_id ranging from 0 to 9 - if self.channel_id: - if not self.channel_id.isdigit(): - self.module.fail_json(msg="Error: channel_id is not digit.") - if int(self.channel_id) < 0 or int(self.channel_id) > 9: - self.module.fail_json( - msg="Error: channel_id is not ranges from 0 to 9.") - - # module_name and channel_id must be set at the same time - if bool(self.module_name) != bool(self.channel_id): - self.module.fail_json( - msg="Error: module_name and channel_id must be set at the same time.") - - def get_proposed(self): - """get proposed info""" - - if self.log_time_stamp: - self.proposed["log_time_stamp"] = self.log_time_stamp - if self.log_buff_enable != 'no_use': - self.proposed["log_buff_enable"] = self.log_buff_enable - if self.log_buff_size: - self.proposed["log_buff_size"] = self.log_buff_size - if self.module_name: - self.proposed["module_name"] = self.module_name - if self.channel_id: - self.proposed["channel_id"] = self.channel_id - if self.log_enable != 'no_use': - self.proposed["log_enable"] = self.log_enable - if self.log_level: - self.proposed["log_level"] = self.log_level - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.log_dict: - return - - if self.log_time_stamp: - self.existing["log_time_stamp"] = self.log_dict.get("logTimeStamp").lower() - if self.log_buff_enable != 'no_use': - self.existing["log_buff_enable"] = self.log_dict.get("icLogBuffEn") - if self.log_buff_size: - self.existing["log_buff_size"] = self.log_dict.get("bufferSize") - if self.module_name: - self.existing["source"] = self.log_dict.get("source") - - def get_end_state(self): - """get end state info""" - - log_dict = self.get_log_dict() - if not log_dict: - return - - if self.log_time_stamp: - self.end_state["log_time_stamp"] = log_dict.get("logTimeStamp").lower() - if self.log_buff_enable != 'no_use': - self.end_state["log_buff_enable"] = log_dict.get("icLogBuffEn") - if self.log_buff_size: - self.end_state["log_buff_size"] = log_dict.get("bufferSize") - if self.module_name: - self.end_state["source"] = log_dict.get("source") - - def work(self): - """worker""" - - self.check_params() - self.log_dict = self.get_log_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - xml_str = '' - if self.log_time_stamp or self.log_buff_enable != 'no_use' or self.log_buff_size: - xml_str += self.config_log_global() - - if self.module_name: - xml_str += self.config_log_soruce() - - if xml_str: - self.netconf_load_config(xml_str) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - log_time_stamp=dict(required=False, type='str', - choices=['date_boot', 'date_second', 'date_tenthsecond', 'date_millisecond', - 'shortdate_second', 'shortdate_tenthsecond', 'shortdate_millisecond', - 'formatdate_second', 'formatdate_tenthsecond', 'formatdate_millisecond']), - log_buff_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - log_buff_size=dict(required=False, type='str'), - module_name=dict(required=False, type='str'), - channel_id=dict(required=False, type='str'), - log_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - log_level=dict(required=False, type='str', - choices=['emergencies', 'alert', 'critical', 'error', - 'warning', 'notification', 'informational', 'debugging']), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - - argument_spec.update(ce_argument_spec) - module = InfoCenterLog(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_info_center_trap.py b/plugins/modules/network/cloudengine/ce_info_center_trap.py deleted file mode 100644 index 31d5276e69..0000000000 --- a/plugins/modules/network/cloudengine/ce_info_center_trap.py +++ /dev/null @@ -1,697 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_info_center_trap -short_description: Manages information center trap configuration on HUAWEI CloudEngine switches. -description: - - Manages information center trap configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] - trap_time_stamp: - description: - - Timestamp format of alarm information. - choices: ['date_boot', 'date_second', 'date_tenthsecond', 'date_millisecond', 'shortdate_second', - 'shortdate_tenthsecond', 'shortdate_millisecond', 'formatdate_second', 'formatdate_tenthsecond', - 'formatdate_millisecond'] - trap_buff_enable: - description: - - Whether a trap buffer is enabled to output information. - default: no_use - choices: ['no_use','true','false'] - trap_buff_size: - description: - - Size of a trap buffer. - The value is an integer ranging from 0 to 1024. The default value is 256. - module_name: - description: - - Module name of the rule. - The value is a string of 1 to 31 case-insensitive characters. The default value is default. - Please use lower-case letter, such as [aaa, acl, arp, bfd]. - channel_id: - description: - - Number of a channel. - The value is an integer ranging from 0 to 9. The default value is 0. - trap_enable: - description: - - Whether a device is enabled to output alarms. - default: no_use - choices: ['no_use','true','false'] - trap_level: - description: - - Trap level permitted to output. - choices: ['emergencies', 'alert', 'critical', 'error', 'warning', 'notification', - 'informational', 'debugging'] -''' - -EXAMPLES = ''' - -- name: CloudEngine info center trap test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config trap buffer" - ce_info_center_trap: - state: present - trap_buff_enable: true - trap_buff_size: 768 - provider: "{{ cli }}" - - - name: "Undo trap buffer" - ce_info_center_trap: - state: absent - trap_buff_enable: true - trap_buff_size: 768 - provider: "{{ cli }}" - - - name: "Config trap module log level" - ce_info_center_trap: - state: present - module_name: aaa - channel_id: 1 - trap_enable: true - trap_level: error - provider: "{{ cli }}" - - - name: "Undo trap module log level" - ce_info_center_trap: - state: absent - module_name: aaa - channel_id: 1 - trap_enable: true - trap_level: error - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"state": "present", "trap_buff_enable": "true", "trap_buff_size": "768"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"icTrapBuffEn": "false", "trapBuffSize": "256"} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"icTrapBuffEn": "true", "trapBuffSize": "768"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["info-center trapbuffer", "info-center trapbuffer size 768"] -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -# get info center trap global -CE_GET_TRAP_GLOBAL_HEADER = """ - - - -""" -CE_GET_TRAP_GLOBAL_TAIL = """ - - - -""" -# merge info center trap global -CE_MERGE_TRAP_GLOBAL_HEADER = """ - - - -""" -CE_MERGE_TRAP_GLOBAL_TAIL = """ - - - -""" - -# get info center trap source -CE_GET_TRAP_SOURCE_HEADER = """ - - - - -""" -CE_GET_TRAP_SOURCE_TAIL = """ - - - - -""" -# merge info center trap source -CE_MERGE_TRAP_SOURCE_HEADER = """ - - - - -""" -CE_MERGE_TRAP_SOURCE_TAIL = """ - - - - -""" -# delete info center trap source -CE_DELETE_TRAP_SOURCE_HEADER = """ - - - - -""" -CE_DELETE_TRAP_SOURCE_TAIL = """ - - - - -""" - -TIME_STAMP_DICT = {"date_boot": "boot", - "date_second": "date precision-time second", - "date_tenthsecond": "date precision-time tenth-second", - "date_millisecond": "date precision-time millisecond", - "shortdate_second": "short-date precision-time second", - "shortdate_tenthsecond": "short-date precision-time tenth-second", - "shortdate_millisecond": "short-date precision-time millisecond", - "formatdate_second": "format-date precision-time second", - "formatdate_tenthsecond": "format-date precision-time tenth-second", - "formatdate_millisecond": "format-date precision-time millisecond"} - -CHANNEL_DEFAULT_TRAP_STATE = {"0": "true", - "1": "true", - "2": "true", - "3": "true", - "4": "false", - "5": "true", - "6": "true", - "7": "true", - "8": "true", - "9": "true"} - -CHANNEL_DEFAULT_TRAP_LEVEL = {"0": "debugging", - "1": "debugging", - "2": "debugging", - "3": "debugging", - "4": "debugging", - "5": "debugging", - "6": "debugging", - "7": "debugging", - "8": "debugging", - "9": "debugging"} - - -class InfoCenterTrap(object): - """ Manages info center trap configuration """ - - def __init__(self, **kwargs): - """ Init function """ - - # argument spec - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # module args - self.state = self.module.params['state'] - self.trap_time_stamp = self.module.params['trap_time_stamp'] or None - self.trap_buff_enable = self.module.params['trap_buff_enable'] - self.trap_buff_size = self.module.params['trap_buff_size'] or None - self.module_name = self.module.params['module_name'] or None - self.channel_id = self.module.params['channel_id'] or None - self.trap_enable = self.module.params['trap_enable'] - self.trap_level = self.module.params['trap_level'] or None - - # cur config - self.cur_global_cfg = dict() - self.cur_source_cfg = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def netconf_get_config(self, conf_str): - """ Netconf get config """ - - xml_str = get_nc_config(self.module, conf_str) - - return xml_str - - def netconf_set_config(self, conf_str): - """ Netconf set config """ - - xml_str = set_nc_config(self.module, conf_str) - - return xml_str - - def check_global_args(self): - """ Check global args """ - - need_cfg = False - find_flag = False - self.cur_global_cfg["global_cfg"] = [] - - if self.trap_time_stamp or self.trap_buff_enable != 'no_use' or self.trap_buff_size: - if self.trap_buff_size: - if self.trap_buff_size.isdigit(): - if int(self.trap_buff_size) < 0 or int(self.trap_buff_size) > 1024: - self.module.fail_json( - msg='Error: The value of trap_buff_size is out of [0 - 1024].') - else: - self.module.fail_json( - msg='Error: The trap_buff_size is not digit.') - - conf_str = CE_GET_TRAP_GLOBAL_HEADER - - if self.trap_time_stamp: - conf_str += "" - if self.trap_buff_enable != 'no_use': - conf_str += "" - if self.trap_buff_size: - conf_str += "" - - conf_str += CE_GET_TRAP_GLOBAL_TAIL - recv_xml = self.netconf_get_config(conf_str=conf_str) - - if "" in recv_xml: - find_flag = False - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - global_cfg = root.findall("syslog/globalParam") - if global_cfg: - for tmp in global_cfg: - tmp_dict = dict() - for site in tmp: - if site.tag in ["trapTimeStamp", "icTrapBuffEn", "trapBuffSize"]: - tmp_dict[site.tag] = site.text - - self.cur_global_cfg["global_cfg"].append(tmp_dict) - - if self.cur_global_cfg["global_cfg"]: - for tmp in self.cur_global_cfg["global_cfg"]: - find_flag = True - - if self.trap_time_stamp and tmp.get("trapTimeStamp").lower() != self.trap_time_stamp: - find_flag = False - if self.trap_buff_enable != 'no_use' and tmp.get("icTrapBuffEn") != self.trap_buff_enable: - find_flag = False - if self.trap_buff_size and tmp.get("trapBuffSize") != self.trap_buff_size: - find_flag = False - - if find_flag: - break - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - else: - need_cfg = bool(find_flag) - - self.cur_global_cfg["need_cfg"] = need_cfg - - def check_source_args(self): - """ Check source args """ - - need_cfg = False - find_flag = False - self.cur_source_cfg["source_cfg"] = list() - - if self.module_name: - if len(self.module_name) < 1 or len(self.module_name) > 31: - self.module.fail_json( - msg='Error: The module_name is out of [1 - 31].') - - if not self.channel_id: - self.module.fail_json( - msg='Error: Please input channel_id at the same time.') - - if self.channel_id: - if self.channel_id.isdigit(): - if int(self.channel_id) < 0 or int(self.channel_id) > 9: - self.module.fail_json( - msg='Error: The value of channel_id is out of [0 - 9].') - else: - self.module.fail_json( - msg='Error: The channel_id is not digit.') - - conf_str = CE_GET_TRAP_SOURCE_HEADER - - if self.module_name != "default": - conf_str += "%s" % self.module_name.upper() - else: - conf_str += "default" - - if self.channel_id: - conf_str += "" - if self.trap_enable != 'no_use': - conf_str += "" - if self.trap_level: - conf_str += "" - - conf_str += CE_GET_TRAP_SOURCE_TAIL - recv_xml = self.netconf_get_config(conf_str=conf_str) - - if "" in recv_xml: - find_flag = False - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - source_cfg = root.findall("syslog/icSources/icSource") - if source_cfg: - for tmp in source_cfg: - tmp_dict = dict() - for site in tmp: - if site.tag in ["moduleName", "icChannelId", "trapEnFlg", "trapEnLevel"]: - tmp_dict[site.tag] = site.text - - self.cur_source_cfg["source_cfg"].append(tmp_dict) - - if self.cur_source_cfg["source_cfg"]: - for tmp in self.cur_source_cfg["source_cfg"]: - find_flag = True - - if self.module_name and tmp.get("moduleName").lower() != self.module_name.lower(): - find_flag = False - if self.channel_id and tmp.get("icChannelId") != self.channel_id: - find_flag = False - if self.trap_enable != 'no_use' and tmp.get("trapEnFlg") != self.trap_enable: - find_flag = False - if self.trap_level and tmp.get("trapEnLevel") != self.trap_level: - find_flag = False - - if find_flag: - break - else: - find_flag = False - - if self.state == "present": - need_cfg = bool(not find_flag) - else: - need_cfg = bool(find_flag) - - self.cur_source_cfg["need_cfg"] = need_cfg - - def get_proposed(self): - """ Get proposed """ - - self.proposed["state"] = self.state - - if self.trap_time_stamp: - self.proposed["trap_time_stamp"] = self.trap_time_stamp - if self.trap_buff_enable != 'no_use': - self.proposed["trap_buff_enable"] = self.trap_buff_enable - if self.trap_buff_size: - self.proposed["trap_buff_size"] = self.trap_buff_size - if self.module_name: - self.proposed["module_name"] = self.module_name - if self.channel_id: - self.proposed["channel_id"] = self.channel_id - if self.trap_enable != 'no_use': - self.proposed["trap_enable"] = self.trap_enable - if self.trap_level: - self.proposed["trap_level"] = self.trap_level - - def get_existing(self): - """ Get existing """ - - if self.cur_global_cfg["global_cfg"]: - self.existing["global_cfg"] = self.cur_global_cfg["global_cfg"] - if self.cur_source_cfg["source_cfg"]: - self.existing["source_cfg"] = self.cur_source_cfg["source_cfg"] - - def get_end_state(self): - """ Get end state """ - - self.check_global_args() - if self.cur_global_cfg["global_cfg"]: - self.end_state["global_cfg"] = self.cur_global_cfg["global_cfg"] - - self.check_source_args() - if self.cur_source_cfg["source_cfg"]: - self.end_state["source_cfg"] = self.cur_source_cfg["source_cfg"] - - def merge_trap_global(self): - """ Merge trap global """ - - conf_str = CE_MERGE_TRAP_GLOBAL_HEADER - - if self.trap_time_stamp: - conf_str += "%s" % self.trap_time_stamp.upper() - if self.trap_buff_enable != 'no_use': - conf_str += "%s" % self.trap_buff_enable - if self.trap_buff_size: - conf_str += "%s" % self.trap_buff_size - - conf_str += CE_MERGE_TRAP_GLOBAL_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge trap global failed.') - - if self.trap_time_stamp: - cmd = "info-center timestamp trap " + TIME_STAMP_DICT.get(self.trap_time_stamp) - self.updates_cmd.append(cmd) - if self.trap_buff_enable != 'no_use': - if self.trap_buff_enable == "true": - cmd = "info-center trapbuffer" - else: - cmd = "undo info-center trapbuffer" - self.updates_cmd.append(cmd) - if self.trap_buff_size: - cmd = "info-center trapbuffer size %s" % self.trap_buff_size - self.updates_cmd.append(cmd) - - self.changed = True - - def delete_trap_global(self): - """ Delete trap global """ - - conf_str = CE_MERGE_TRAP_GLOBAL_HEADER - - if self.trap_time_stamp: - conf_str += "DATE_SECOND" - if self.trap_buff_enable != 'no_use': - conf_str += "false" - if self.trap_buff_size: - conf_str += "256" - - conf_str += CE_MERGE_TRAP_GLOBAL_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: delete trap global failed.') - - if self.trap_time_stamp: - cmd = "undo info-center timestamp trap" - self.updates_cmd.append(cmd) - if self.trap_buff_enable != 'no_use': - cmd = "undo info-center trapbuffer" - self.updates_cmd.append(cmd) - if self.trap_buff_size: - cmd = "undo info-center trapbuffer size" - self.updates_cmd.append(cmd) - - self.changed = True - - def merge_trap_source(self): - """ Merge trap source """ - - conf_str = CE_MERGE_TRAP_SOURCE_HEADER - - if self.module_name: - conf_str += "%s" % self.module_name - if self.channel_id: - conf_str += "%s" % self.channel_id - if self.trap_enable != 'no_use': - conf_str += "%s" % self.trap_enable - if self.trap_level: - conf_str += "%s" % self.trap_level - - conf_str += CE_MERGE_TRAP_SOURCE_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge trap source failed.') - - cmd = "info-center source" - if self.module_name: - cmd += " %s" % self.module_name - if self.channel_id: - cmd += " channel %s" % self.channel_id - if self.trap_enable != 'no_use': - if self.trap_enable == "true": - cmd += " trap state on" - else: - cmd += " trap state off" - if self.trap_level: - cmd += " level %s" % self.trap_level - - self.updates_cmd.append(cmd) - self.changed = True - - def delete_trap_source(self): - """ Delete trap source """ - - if self.trap_enable == 'no_use' and not self.trap_level: - conf_str = CE_DELETE_TRAP_SOURCE_HEADER - if self.module_name: - conf_str += "%s" % self.module_name - if self.channel_id: - conf_str += "%s" % self.channel_id - conf_str += CE_DELETE_TRAP_SOURCE_TAIL - else: - conf_str = CE_MERGE_TRAP_SOURCE_HEADER - if self.module_name: - conf_str += "%s" % self.module_name - if self.channel_id: - conf_str += "%s" % self.channel_id - if self.trap_enable != 'no_use': - conf_str += "%s" % CHANNEL_DEFAULT_TRAP_STATE.get(self.channel_id) - if self.trap_level: - conf_str += "%s" % CHANNEL_DEFAULT_TRAP_LEVEL.get(self.channel_id) - conf_str += CE_MERGE_TRAP_SOURCE_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Delete trap source failed.') - - cmd = "undo info-center source" - if self.module_name: - cmd += " %s" % self.module_name - if self.channel_id: - cmd += " channel %s" % self.channel_id - if self.trap_enable != 'no_use': - cmd += " trap state" - if self.trap_level: - cmd += " level" - - self.updates_cmd.append(cmd) - self.changed = True - - def work(self): - """ work function """ - - self.check_global_args() - self.check_source_args() - self.get_proposed() - self.get_existing() - - if self.state == "present": - if self.cur_global_cfg["need_cfg"]: - self.merge_trap_global() - if self.cur_source_cfg["need_cfg"]: - self.merge_trap_source() - - else: - if self.cur_global_cfg["need_cfg"]: - self.delete_trap_global() - if self.cur_source_cfg["need_cfg"]: - self.delete_trap_source() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - trap_time_stamp=dict(choices=['date_boot', 'date_second', 'date_tenthsecond', - 'date_millisecond', 'shortdate_second', 'shortdate_tenthsecond', - 'shortdate_millisecond', 'formatdate_second', 'formatdate_tenthsecond', - 'formatdate_millisecond']), - trap_buff_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - trap_buff_size=dict(type='str'), - module_name=dict(type='str'), - channel_id=dict(type='str'), - trap_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - trap_level=dict(choices=['emergencies', 'alert', 'critical', 'error', 'warning', 'notification', - 'informational', 'debugging']) - ) - - argument_spec.update(ce_argument_spec) - module = InfoCenterTrap(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_interface.py b/plugins/modules/network/cloudengine/ce_interface.py deleted file mode 100644 index 930a27fd0b..0000000000 --- a/plugins/modules/network/cloudengine/ce_interface.py +++ /dev/null @@ -1,895 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_interface -short_description: Manages physical attributes of interfaces on HUAWEI CloudEngine switches. -description: - - Manages physical attributes of interfaces on HUAWEI CloudEngine switches. -author: QijunPan (@QijunPan) -notes: - - This module is also used to create logical interfaces such as - vlanif and loopbacks. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - interface: - description: - - Full name of interface, i.e. 40GE1/0/10, Tunnel1. - interface_type: - description: - - Interface type to be configured from the device. - choices: ['ge', '10ge', '25ge', '4x10ge', '40ge', '100ge', 'vlanif', 'loopback', 'meth', - 'eth-trunk', 'nve', 'tunnel', 'ethernet', 'fcoe-port', 'fabric-port', 'stack-port', 'null'] - admin_state: - description: - - Specifies the interface management status. - The value is an enumerated type. - up, An interface is in the administrative Up state. - down, An interface is in the administrative Down state. - choices: ['up', 'down'] - description: - description: - - Specifies an interface description. - The value is a string of 1 to 242 case-sensitive characters, - spaces supported but question marks (?) not supported. - mode: - description: - - Manage Layer 2 or Layer 3 state of the interface. - choices: ['layer2', 'layer3'] - l2sub: - description: - - Specifies whether the interface is a Layer 2 sub-interface. - type: bool - default: 'no' - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present', 'absent', 'default'] -''' - -EXAMPLES = ''' -- name: interface module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Ensure an interface is a Layer 3 port and that it has the proper description - ce_interface: - interface: 10GE1/0/22 - description: 'Configured by Ansible' - mode: layer3 - provider: '{{ cli }}' - - - name: Admin down an interface - ce_interface: - interface: 10GE1/0/22 - admin_state: down - provider: '{{ cli }}' - - - name: Remove all tunnel interfaces - ce_interface: - interface_type: tunnel - state: absent - provider: '{{ cli }}' - - - name: Remove all logical interfaces - ce_interface: - interface_type: '{{ item }}' - state: absent - provider: '{{ cli }}' - with_items: - - loopback - - eth-trunk - - nve - - - name: Admin up all 10GE interfaces - ce_interface: - interface_type: 10GE - admin_state: up - provider: '{{ cli }}' -''' -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"interface": "10GE1/0/10", "admin_state": "down"} -existing: - description: k/v pairs of existing switchport - returned: always - type: dict - sample: {"admin_state": "up", "description": "None", - "interface": "10GE1/0/10", "mode": "layer2"} -end_state: - description: k/v pairs of switchport after module execution - returned: always - type: dict - sample: {"admin_state": "down", "description": "None", - "interface": "10GE1/0/10", "mode": "layer2"} -updates: - description: command list sent to the device - returned: always - type: list - sample: ["interface 10GE1/0/10", "shutdown"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - - -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_INTFS = """ - - - - - - %s - - - - - - - - - -""" - - -CE_NC_GET_INTF = """ - - - - - %s - - - - - - - - - - -""" - -CE_NC_XML_CREATE_INTF = """ - - - - %s - %s - - - -""" - -CE_NC_XML_CREATE_INTF_L2SUB = """ - - - - %s - %s - true - - - -""" - -CE_NC_XML_DELETE_INTF = """ - - - - %s - - - -""" - - -CE_NC_XML_MERGE_INTF_DES = """ - - - - %s - %s - - - -""" -CE_NC_XML_MERGE_INTF_STATUS = """ - - - - %s - %s - - - -""" - -CE_NC_XML_MERGE_INTF_L2ENABLE = """ - - - - %s - %s - - - -""" - -ADMIN_STATE_TYPE = ('ge', '10ge', '25ge', '4x10ge', '40ge', '100ge', - 'vlanif', 'meth', 'eth-trunk', 'vbdif', 'tunnel', - 'ethernet', 'stack-port') - -SWITCH_PORT_TYPE = ('ge', '10ge', '25ge', - '4x10ge', '40ge', '100ge', 'eth-trunk') - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - if interface.upper().startswith('GE'): - return 'ge' - elif interface.upper().startswith('10GE'): - return '10ge' - elif interface.upper().startswith('25GE'): - return '25ge' - elif interface.upper().startswith('4X10GE'): - return '4x10ge' - elif interface.upper().startswith('40GE'): - return '40ge' - elif interface.upper().startswith('100GE'): - return '100ge' - elif interface.upper().startswith('VLANIF'): - return 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - return 'loopback' - elif interface.upper().startswith('METH'): - return 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - return 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - return 'vbdif' - elif interface.upper().startswith('NVE'): - return 'nve' - elif interface.upper().startswith('TUNNEL'): - return 'tunnel' - elif interface.upper().startswith('ETHERNET'): - return 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - return 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - return 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - return 'stack-port' - elif interface.upper().startswith('NULL'): - return 'null' - else: - return None - - -def is_admin_state_enable(iftype): - """admin state disable: loopback nve""" - - return bool(iftype in ADMIN_STATE_TYPE) - - -def is_portswitch_enalbe(iftype): - """"is portswitch? """ - - return bool(iftype in SWITCH_PORT_TYPE) - - -class Interface(object): - """Manages physical attributes of interfaces.""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # interface info - self.interface = self.module.params['interface'] - self.interface_type = self.module.params['interface_type'] - self.admin_state = self.module.params['admin_state'] - self.description = self.module.params['description'] - self.mode = self.module.params['mode'] - self.l2sub = self.module.params['l2sub'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - self.intfs_info = dict() # all type interface info - self.intf_info = dict() # one interface info - self.intf_type = None # loopback tunnel ... - - def init_module(self): - """init_module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_interfaces_dict(self): - """ get interfaces attributes dict.""" - - intfs_info = dict() - conf_str = CE_NC_GET_INTFS % self.interface_type - recv_xml = get_nc_config(self.module, conf_str) - - if "" in recv_xml: - return intfs_info - - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - intfs = root.findall("ifm/interfaces/") - if intfs: - for intf in intfs: - intf_type = intf.find("ifPhyType").text.lower() - if intf_type: - if not intfs_info.get(intf_type): - intfs_info[intf_type] = list() - intf_info = dict() - for tmp in intf: - intf_info[tmp.tag] = tmp.text - intfs_info[intf_type].append(intf_info) - return intfs_info - - def get_interface_dict(self, ifname): - """ get one interface attributes dict.""" - - intf_info = dict() - conf_str = CE_NC_GET_INTF % ifname - recv_xml = get_nc_config(self.module, conf_str) - - if "" in recv_xml: - return intf_info - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - intfs = root.findall("ifm/interfaces/interface/") - if intfs: - for intf in intfs: - intf_info[intf.tag] = intf.text - return intf_info - - def create_interface(self, ifname, description, admin_state, mode, l2sub): - """Create interface.""" - - if l2sub: - self.updates_cmd.append("interface %s mode l2" % ifname) - else: - self.updates_cmd.append("interface %s" % ifname) - - if not description: - description = '' - else: - self.updates_cmd.append("description %s" % description) - - if l2sub: - xmlstr = CE_NC_XML_CREATE_INTF_L2SUB % (ifname, description) - else: - xmlstr = CE_NC_XML_CREATE_INTF % (ifname, description) - if admin_state and is_admin_state_enable(self.intf_type): - xmlstr += CE_NC_XML_MERGE_INTF_STATUS % (ifname, admin_state) - if admin_state == 'up': - self.updates_cmd.append("undo shutdown") - else: - self.updates_cmd.append("shutdown") - if mode and is_portswitch_enalbe(self.intf_type): - if mode == "layer2": - xmlstr += CE_NC_XML_MERGE_INTF_L2ENABLE % (ifname, 'enable') - self.updates_cmd.append('portswitch') - elif mode == "layer3": - xmlstr += CE_NC_XML_MERGE_INTF_L2ENABLE % (ifname, 'disable') - self.updates_cmd.append('undo portswitch') - - conf_str = ' ' + xmlstr + ' ' - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "CREATE_INTF") - self.changed = True - - def delete_interface(self, ifname): - """ Delete interface.""" - - xmlstr = CE_NC_XML_DELETE_INTF % ifname - conf_str = ' ' + xmlstr + ' ' - self.updates_cmd.append('undo interface %s' % ifname) - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "DELETE_INTF") - self.changed = True - - def delete_interfaces(self, iftype): - """ Delete interfaces with type.""" - - xmlstr = '' - intfs_list = self.intfs_info.get(iftype.lower()) - if not intfs_list: - return - - for intf in intfs_list: - xmlstr += CE_NC_XML_DELETE_INTF % intf['ifName'] - self.updates_cmd.append('undo interface %s' % intf['ifName']) - - conf_str = ' ' + xmlstr + ' ' - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "DELETE_INTFS") - self.changed = True - - def merge_interface(self, ifname, description, admin_state, mode): - """ Merge interface attributes.""" - - xmlstr = '' - change = False - self.updates_cmd.append("interface %s" % ifname) - if description and self.intf_info["ifDescr"] != description: - xmlstr += CE_NC_XML_MERGE_INTF_DES % (ifname, description) - self.updates_cmd.append("description %s" % description) - change = True - - if admin_state and is_admin_state_enable(self.intf_type) \ - and self.intf_info["ifAdminStatus"] != admin_state: - xmlstr += CE_NC_XML_MERGE_INTF_STATUS % (ifname, admin_state) - change = True - if admin_state == "up": - self.updates_cmd.append("undo shutdown") - else: - self.updates_cmd.append("shutdown") - - if is_portswitch_enalbe(self.intf_type): - if mode == "layer2" and self.intf_info["isL2SwitchPort"] != "true": - xmlstr += CE_NC_XML_MERGE_INTF_L2ENABLE % (ifname, 'enable') - self.updates_cmd.append("portswitch") - change = True - elif mode == "layer3" \ - and self.intf_info["isL2SwitchPort"] != "false": - xmlstr += CE_NC_XML_MERGE_INTF_L2ENABLE % (ifname, 'disable') - self.updates_cmd.append("undo portswitch") - change = True - - if not change: - return - - conf_str = ' ' + xmlstr + ' ' - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "MERGE_INTF_ATTR") - self.changed = True - - def merge_interfaces(self, iftype, description, admin_state, mode): - """ Merge interface attributes by type.""" - - xmlstr = '' - change = False - intfs_list = self.intfs_info.get(iftype.lower()) - if not intfs_list: - return - - for intf in intfs_list: - if_change = False - self.updates_cmd.append("interface %s" % intf['ifName']) - if description and intf["ifDescr"] != description: - xmlstr += CE_NC_XML_MERGE_INTF_DES % ( - intf['ifName'], description) - self.updates_cmd.append("description %s" % description) - if_change = True - if admin_state and is_admin_state_enable(self.intf_type)\ - and intf["ifAdminStatus"] != admin_state: - xmlstr += CE_NC_XML_MERGE_INTF_STATUS % ( - intf['ifName'], admin_state) - if_change = True - if admin_state == "up": - self.updates_cmd.append("undo shutdown") - else: - self.updates_cmd.append("shutdown") - - if is_portswitch_enalbe(self.intf_type): - if mode == "layer2" \ - and intf["isL2SwitchPort"] != "true": - xmlstr += CE_NC_XML_MERGE_INTF_L2ENABLE % ( - intf['ifName'], 'enable') - self.updates_cmd.append("portswitch") - if_change = True - elif mode == "layer3" \ - and intf["isL2SwitchPort"] != "false": - xmlstr += CE_NC_XML_MERGE_INTF_L2ENABLE % ( - intf['ifName'], 'disable') - self.updates_cmd.append("undo portswitch") - if_change = True - - if if_change: - change = True - else: - self.updates_cmd.pop() - - if not change: - return - - conf_str = ' ' + xmlstr + ' ' - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "MERGE_INTFS_ATTR") - self.changed = True - - def default_interface(self, ifname): - """default_interface""" - - change = False - xmlstr = "" - self.updates_cmd.append("interface %s" % ifname) - # set description default - if self.intf_info["ifDescr"]: - xmlstr += CE_NC_XML_MERGE_INTF_DES % (ifname, '') - self.updates_cmd.append("undo description") - change = True - - # set admin_status default - if is_admin_state_enable(self.intf_type) \ - and self.intf_info["ifAdminStatus"] != 'up': - xmlstr += CE_NC_XML_MERGE_INTF_STATUS % (ifname, 'up') - self.updates_cmd.append("undo shutdown") - change = True - - # set portswitch default - if is_portswitch_enalbe(self.intf_type) \ - and self.intf_info["isL2SwitchPort"] != "true": - xmlstr += CE_NC_XML_MERGE_INTF_L2ENABLE % (ifname, 'enable') - self.updates_cmd.append("portswitch") - change = True - - if not change: - return - - conf_str = ' ' + xmlstr + ' ' - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "SET_INTF_DEFAULT") - self.changed = True - - def default_interfaces(self, iftype): - """ Set interface config to default by type.""" - - change = False - xmlstr = '' - intfs_list = self.intfs_info.get(iftype.lower()) - if not intfs_list: - return - - for intf in intfs_list: - if_change = False - self.updates_cmd.append("interface %s" % intf['ifName']) - - # set description default - if intf['ifDescr']: - xmlstr += CE_NC_XML_MERGE_INTF_DES % (intf['ifName'], '') - self.updates_cmd.append("undo description") - if_change = True - - # set admin_status default - if is_admin_state_enable(self.intf_type) and intf["ifAdminStatus"] != 'up': - xmlstr += CE_NC_XML_MERGE_INTF_STATUS % (intf['ifName'], 'up') - self.updates_cmd.append("undo shutdown") - if_change = True - - # set portswitch default - if is_portswitch_enalbe(self.intf_type) and intf["isL2SwitchPort"] != "true": - xmlstr += CE_NC_XML_MERGE_INTF_L2ENABLE % (intf['ifName'], 'enable') - self.updates_cmd.append("portswitch") - if_change = True - - if if_change: - change = True - else: - self.updates_cmd.pop() - - if not change: - return - - conf_str = ' ' + xmlstr + ' ' - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "SET_INTFS_DEFAULT") - self.changed = True - - def check_params(self): - """Check all input params""" - - if not self.interface and not self.interface_type: - self.module.fail_json( - msg='Error: Interface or interface_type must be set.') - if self.interface and self.interface_type: - self.module.fail_json( - msg='Error: Interface or interface_type' - ' can not be set at the same time.') - - # interface type check - if self.interface: - self.intf_type = get_interface_type(self.interface) - if not self.intf_type: - self.module.fail_json( - msg='Error: interface name of %s' - ' is error.' % self.interface) - - elif self.interface_type: - self.intf_type = get_interface_type(self.interface_type) - if not self.intf_type or self.intf_type != self.interface_type.replace(" ", "").lower(): - self.module.fail_json( - msg='Error: interface type of %s' - ' is error.' % self.interface_type) - - if not self.intf_type: - self.module.fail_json( - msg='Error: interface or interface type %s is error.') - - # shutdown check - if not is_admin_state_enable(self.intf_type) \ - and self.state == "present" and self.admin_state == "down": - self.module.fail_json( - msg='Error: The %s interface can not' - ' be shutdown.' % self.intf_type) - - # port switch mode check - if not is_portswitch_enalbe(self.intf_type)\ - and self.mode and self.state == "present": - self.module.fail_json( - msg='Error: The %s interface can not manage' - ' Layer 2 or Layer 3 state.' % self.intf_type) - - # check description len - if self.description: - if len(self.description) > 242 \ - or len(self.description.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: interface description ' - 'is not in the range from 1 to 242.') - # check l2sub flag - if self.l2sub: - if not self.interface: - self.module.fail_json(msg='Error: L2sub flag can not be set when there no interface set with.') - if self.interface.count(".") != 1: - self.module.fail_json(msg='Error: Interface name is invalid, it is not sub-interface.') - - def get_proposed(self): - """get_proposed""" - - self.proposed['state'] = self.state - if self.interface: - self.proposed["interface"] = self.interface - if self.interface_type: - self.proposed["interface_type"] = self.interface_type - - if self.state == 'present': - if self.description: - self.proposed["description"] = self.description - if self.mode: - self.proposed["mode"] = self.mode - if self.admin_state: - self.proposed["admin_state"] = self.admin_state - self.proposed["l2sub"] = self.l2sub - - elif self.state == 'default': - if self.description: - self.proposed["description"] = "" - if is_admin_state_enable(self.intf_type) and self.admin_state: - self.proposed["admin_state"] = self.admin_state - if is_portswitch_enalbe(self.intf_type) and self.mode: - self.proposed["mode"] = self.mode - - def get_existing(self): - """get_existing""" - - if self.intf_info: - self.existing["interface"] = self.intf_info["ifName"] - if is_admin_state_enable(self.intf_type): - self.existing["admin_state"] = self.intf_info["ifAdminStatus"] - self.existing["description"] = self.intf_info["ifDescr"] - if is_portswitch_enalbe(self.intf_type): - if self.intf_info["isL2SwitchPort"] == "true": - self.existing["mode"] = "layer2" - else: - self.existing["mode"] = "layer3" - - if self.intfs_info: - intfs = self.intfs_info.get(self.intf_type.lower()) - for intf in intfs: - intf_para = dict() - if intf["ifAdminStatus"]: - intf_para["admin_state"] = intf["ifAdminStatus"] - intf_para["description"] = intf["ifDescr"] - - if intf["isL2SwitchPort"] == "true": - intf_para["mode"] = "layer2" - else: - intf_para["mode"] = "layer3" - self.existing[intf["ifName"]] = intf_para - - def get_end_state(self): - """get_end_state""" - if self.interface: - end_info = self.get_interface_dict(self.interface) - if end_info: - self.end_state["interface"] = end_info["ifName"] - if is_admin_state_enable(self.intf_type): - self.end_state["admin_state"] = end_info["ifAdminStatus"] - self.end_state["description"] = end_info["ifDescr"] - if is_portswitch_enalbe(self.intf_type): - if end_info["isL2SwitchPort"] == "true": - self.end_state["mode"] = "layer2" - else: - self.end_state["mode"] = "layer3" - - if self.interface_type: - end_info = self.get_interfaces_dict() - intfs = end_info.get(self.intf_type.lower()) - for intf in intfs: - intf_para = dict() - if intf["ifAdminStatus"]: - intf_para["admin_state"] = intf["ifAdminStatus"] - intf_para["description"] = intf["ifDescr"] - - if intf["isL2SwitchPort"] == "true": - intf_para["mode"] = "layer2" - else: - intf_para["mode"] = "layer3" - self.end_state[intf["ifName"]] = intf_para - - def work(self): - """worker""" - - self.check_params() - - # single interface config - if self.interface: - self.intf_info = self.get_interface_dict(self.interface) - self.get_existing() - if self.state == 'present': - if not self.intf_info: - # create interface - self.create_interface(self.interface, - self.description, - self.admin_state, - self.mode, - self.l2sub) - else: - # merge interface - if self.description or self.admin_state or self.mode: - self.merge_interface(self.interface, - self.description, - self.admin_state, - self.mode) - - elif self.state == 'absent': - if self.intf_info: - # delete interface - self.delete_interface(self.interface) - else: - # interface does not exist - self.module.fail_json( - msg='Error: interface does not exist.') - - else: # default - if not self.intf_info: - # error, interface does not exist - self.module.fail_json( - msg='Error: interface does not exist.') - else: - self.default_interface(self.interface) - - # interface type config - else: - self.intfs_info = self.get_interfaces_dict() - self.get_existing() - if self.state == 'present': - if self.intfs_info.get(self.intf_type.lower()): - if self.description or self.admin_state or self.mode: - self.merge_interfaces(self.intf_type, - self.description, - self.admin_state, - self.mode) - elif self.state == 'absent': - # delete all interface of this type - if self.intfs_info.get(self.intf_type.lower()): - self.delete_interfaces(self.intf_type) - - else: - # set interfaces config to default - if self.intfs_info.get(self.intf_type.lower()): - self.default_interfaces(self.intf_type) - else: - self.module.fail_json( - msg='Error: no interface in this type.') - - self.get_proposed() - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """main""" - - argument_spec = dict( - interface=dict(required=False, type='str'), - admin_state=dict(choices=['up', 'down'], required=False), - description=dict(required=False, default=None), - mode=dict(choices=['layer2', 'layer3'], required=False), - interface_type=dict(required=False), - l2sub=dict(required=False, default=False, type='bool'), - state=dict(choices=['absent', 'present', 'default'], - default='present', required=False), - ) - - argument_spec.update(ce_argument_spec) - interface = Interface(argument_spec) - interface.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_interface_ospf.py b/plugins/modules/network/cloudengine/ce_interface_ospf.py deleted file mode 100644 index 7e7e4f92a5..0000000000 --- a/plugins/modules/network/cloudengine/ce_interface_ospf.py +++ /dev/null @@ -1,797 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_interface_ospf -short_description: Manages configuration of an OSPF interface instanceon HUAWEI CloudEngine switches. -description: - - Manages configuration of an OSPF interface instanceon HUAWEI CloudEngine switches. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - interface: - description: - - Full name of interface, i.e. 40GE1/0/10. - required: true - process_id: - description: - - Specifies a process ID. - The value is an integer ranging from 1 to 4294967295. - required: true - area: - description: - - Ospf area associated with this ospf process. - Valid values are a string, formatted as an IP address - (i.e. "0.0.0.0") or as an integer between 1 and 4294967295. - required: true - cost: - description: - - The cost associated with this interface. - Valid values are an integer in the range from 1 to 65535. - hello_interval: - description: - - Time between sending successive hello packets. - Valid values are an integer in the range from 1 to 65535. - dead_interval: - description: - - Time interval an ospf neighbor waits for a hello - packet before tearing down adjacencies. Valid values are an - integer in the range from 1 to 235926000. - silent_interface: - description: - - Setting to true will prevent this interface from receiving - HELLO packets. Valid values are 'true' and 'false'. - type: bool - default: 'no' - auth_mode: - description: - - Specifies the authentication type. - choices: ['none', 'null', 'hmac-sha256', 'md5', 'hmac-md5', 'simple'] - auth_text_simple: - description: - - Specifies a password for simple authentication. - The value is a string of 1 to 8 characters. - auth_key_id: - description: - - Authentication key id when C(auth_mode) is 'hmac-sha256', 'md5' or 'hmac-md5. - Valid value is an integer is in the range from 1 to 255. - auth_text_md5: - description: - - Specifies a password for MD5, HMAC-MD5, or HMAC-SHA256 authentication. - The value is a string of 1 to 255 case-sensitive characters, spaces not supported. - state: - description: - - Determines whether the config should be present or not - on the device. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: eth_trunk module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Enables OSPF and sets the cost on an interface - ce_interface_ospf: - interface: 10GE1/0/30 - process_id: 1 - area: 100 - cost: 100 - provider: '{{ cli }}' - - - name: Sets the dead interval of the OSPF neighbor - ce_interface_ospf: - interface: 10GE1/0/30 - process_id: 1 - area: 100 - dead_interval: 100 - provider: '{{ cli }}' - - - name: Sets the interval for sending Hello packets on an interface - ce_interface_ospf: - interface: 10GE1/0/30 - process_id: 1 - area: 100 - hello_interval: 2 - provider: '{{ cli }}' - - - name: Disables an interface from receiving and sending OSPF packets - ce_interface_ospf: - interface: 10GE1/0/30 - process_id: 1 - area: 100 - silent_interface: true - provider: '{{ cli }}' -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"process_id": "1", "area": "0.0.0.100", "interface": "10GE1/0/30", "cost": "100"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"process_id": "1", "area": "0.0.0.100"} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"process_id": "1", "area": "0.0.0.100", "interface": "10GE1/0/30", - "cost": "100", "dead_interval": "40", "hello_interval": "10", - "silent_interface": "false", "auth_mode": "none"} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["interface 10GE1/0/30", - "ospf enable 1 area 0.0.0.100", - "ospf cost 100"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_OSPF = """ - - - - - - %s - - - - - %s - - - %s - - - - - - - - - - - - - - - - - - -""" - -CE_NC_XML_BUILD_PROCESS = """ - - - - - - %s - - - %s - %s - - - - - - - -""" - -CE_NC_XML_BUILD_MERGE_INTF = """ - - - %s - - -""" - -CE_NC_XML_BUILD_DELETE_INTF = """ - - - %s - - -""" -CE_NC_XML_SET_IF_NAME = """ - %s -""" - -CE_NC_XML_SET_HELLO = """ - %s -""" - -CE_NC_XML_SET_DEAD = """ - %s -""" - -CE_NC_XML_SET_SILENT = """ - %s -""" - -CE_NC_XML_SET_COST = """ - %s -""" - -CE_NC_XML_SET_AUTH_MODE = """ - %s -""" - - -CE_NC_XML_SET_AUTH_TEXT_SIMPLE = """ - %s -""" - -CE_NC_XML_SET_AUTH_MD5 = """ - %s - %s -""" - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - if interface.upper().startswith('GE'): - return 'ge' - elif interface.upper().startswith('10GE'): - return '10ge' - elif interface.upper().startswith('25GE'): - return '25ge' - elif interface.upper().startswith('4X10GE'): - return '4x10ge' - elif interface.upper().startswith('40GE'): - return '40ge' - elif interface.upper().startswith('100GE'): - return '100ge' - elif interface.upper().startswith('VLANIF'): - return 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - return 'loopback' - elif interface.upper().startswith('METH'): - return 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - return 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - return 'vbdif' - elif interface.upper().startswith('NVE'): - return 'nve' - elif interface.upper().startswith('TUNNEL'): - return 'tunnel' - elif interface.upper().startswith('ETHERNET'): - return 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - return 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - return 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - return 'stack-port' - elif interface.upper().startswith('NULL'): - return 'null' - else: - return None - - -def is_valid_v4addr(addr): - """check is ipv4 addr is valid""" - - if not addr: - return False - - if addr.find('.') != -1: - addr_list = addr.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - -class InterfaceOSPF(object): - """ - Manages configuration of an OSPF interface instance. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.interface = self.module.params['interface'] - self.process_id = self.module.params['process_id'] - self.area = self.module.params['area'] - self.cost = self.module.params['cost'] - self.hello_interval = self.module.params['hello_interval'] - self.dead_interval = self.module.params['dead_interval'] - self.silent_interface = self.module.params['silent_interface'] - self.auth_mode = self.module.params['auth_mode'] - self.auth_text_simple = self.module.params['auth_text_simple'] - self.auth_key_id = self.module.params['auth_key_id'] - self.auth_text_md5 = self.module.params['auth_text_md5'] - self.state = self.module.params['state'] - - # ospf info - self.ospf_info = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def netconf_set_config(self, xml_str, xml_name): - """netconf set config""" - - rcv_xml = set_nc_config(self.module, xml_str) - if "" not in rcv_xml: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_area_ip(self): - """convert integer to ip address""" - - if not self.area.isdigit(): - return self.area - - addr_int = ['0'] * 4 - addr_int[0] = str(((int(self.area) & 0xFF000000) >> 24) & 0xFF) - addr_int[1] = str(((int(self.area) & 0x00FF0000) >> 16) & 0xFF) - addr_int[2] = str(((int(self.area) & 0x0000FF00) >> 8) & 0XFF) - addr_int[3] = str(int(self.area) & 0xFF) - - return '.'.join(addr_int) - - def get_ospf_dict(self): - """ get one ospf attributes dict.""" - - ospf_info = dict() - conf_str = CE_NC_GET_OSPF % ( - self.process_id, self.get_area_ip(), self.interface) - rcv_xml = get_nc_config(self.module, conf_str) - - if "" in rcv_xml: - return ospf_info - - xml_str = rcv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get process base info - root = ElementTree.fromstring(xml_str) - ospfsite = root.find("ospfv2/ospfv2comm/ospfSites/ospfSite") - if not ospfsite: - self.module.fail_json(msg="Error: ospf process does not exist.") - - for site in ospfsite: - if site.tag in ["processId", "routerId", "vrfName"]: - ospf_info[site.tag] = site.text - - # get areas info - ospf_info["areaId"] = "" - areas = root.find( - "ospfv2/ospfv2comm/ospfSites/ospfSite/areas/area") - if areas: - for area in areas: - if area.tag == "areaId": - ospf_info["areaId"] = area.text - break - - # get interface info - ospf_info["interface"] = dict() - intf = root.find( - "ospfv2/ospfv2comm/ospfSites/ospfSite/areas/area/interfaces/interface") - if intf: - for attr in intf: - if attr.tag in ["ifName", "networkType", - "helloInterval", "deadInterval", - "silentEnable", "configCost", - "authenticationMode", "authTextSimple", - "keyId", "authTextMd5"]: - ospf_info["interface"][attr.tag] = attr.text - - return ospf_info - - def set_ospf_interface(self): - """set interface ospf enable, and set its ospf attributes""" - - xml_intf = CE_NC_XML_SET_IF_NAME % self.interface - - # ospf view - self.updates_cmd.append("ospf %s" % self.process_id) - self.updates_cmd.append("area %s" % self.get_area_ip()) - if self.silent_interface: - xml_intf += CE_NC_XML_SET_SILENT % str(self.silent_interface).lower() - if self.silent_interface: - self.updates_cmd.append("silent-interface %s" % self.interface) - else: - self.updates_cmd.append("undo silent-interface %s" % self.interface) - - # interface view - self.updates_cmd.append("interface %s" % self.interface) - self.updates_cmd.append("ospf enable %s area %s" % ( - self.process_id, self.get_area_ip())) - if self.cost: - xml_intf += CE_NC_XML_SET_COST % self.cost - self.updates_cmd.append("ospf cost %s" % self.cost) - if self.hello_interval: - xml_intf += CE_NC_XML_SET_HELLO % self.hello_interval - self.updates_cmd.append("ospf timer hello %s" % - self.hello_interval) - if self.dead_interval: - xml_intf += CE_NC_XML_SET_DEAD % self.dead_interval - self.updates_cmd.append("ospf timer dead %s" % self.dead_interval) - if self.auth_mode: - xml_intf += CE_NC_XML_SET_AUTH_MODE % self.auth_mode - if self.auth_mode == "none": - self.updates_cmd.append("undo ospf authentication-mode") - else: - self.updates_cmd.append("ospf authentication-mode %s" % self.auth_mode) - if self.auth_mode == "simple" and self.auth_text_simple: - xml_intf += CE_NC_XML_SET_AUTH_TEXT_SIMPLE % self.auth_text_simple - self.updates_cmd.pop() - self.updates_cmd.append("ospf authentication-mode %s %s" - % (self.auth_mode, self.auth_text_simple)) - elif self.auth_mode in ["hmac-sha256", "md5", "hmac-md5"] and self.auth_key_id: - xml_intf += CE_NC_XML_SET_AUTH_MD5 % ( - self.auth_key_id, self.auth_text_md5) - self.updates_cmd.pop() - self.updates_cmd.append("ospf authentication-mode %s %s %s" - % (self.auth_mode, self.auth_key_id, self.auth_text_md5)) - else: - pass - - xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id, - self.get_area_ip(), - (CE_NC_XML_BUILD_MERGE_INTF % xml_intf)) - self.netconf_set_config(xml_str, "SET_INTERFACE_OSPF") - self.changed = True - - def merge_ospf_interface(self): - """merge interface ospf attributes""" - - intf_dict = self.ospf_info["interface"] - - # ospf view - xml_ospf = "" - if intf_dict.get("silentEnable") != str(self.silent_interface).lower(): - xml_ospf += CE_NC_XML_SET_SILENT % str(self.silent_interface).lower() - self.updates_cmd.append("ospf %s" % self.process_id) - self.updates_cmd.append("area %s" % self.get_area_ip()) - if self.silent_interface: - self.updates_cmd.append("silent-interface %s" % self.interface) - else: - self.updates_cmd.append("undo silent-interface %s" % self.interface) - - # interface view - xml_intf = "" - self.updates_cmd.append("interface %s" % self.interface) - if self.cost and intf_dict.get("configCost") != self.cost: - xml_intf += CE_NC_XML_SET_COST % self.cost - self.updates_cmd.append("ospf cost %s" % self.cost) - if self.hello_interval and intf_dict.get("helloInterval") != self.hello_interval: - xml_intf += CE_NC_XML_SET_HELLO % self.hello_interval - self.updates_cmd.append("ospf timer hello %s" % - self.hello_interval) - if self.dead_interval and intf_dict.get("deadInterval") != self.dead_interval: - xml_intf += CE_NC_XML_SET_DEAD % self.dead_interval - self.updates_cmd.append("ospf timer dead %s" % self.dead_interval) - if self.auth_mode: - # NOTE: for security, authentication config will always be update - xml_intf += CE_NC_XML_SET_AUTH_MODE % self.auth_mode - if self.auth_mode == "none": - self.updates_cmd.append("undo ospf authentication-mode") - else: - self.updates_cmd.append("ospf authentication-mode %s" % self.auth_mode) - if self.auth_mode == "simple" and self.auth_text_simple: - xml_intf += CE_NC_XML_SET_AUTH_TEXT_SIMPLE % self.auth_text_simple - self.updates_cmd.pop() - self.updates_cmd.append("ospf authentication-mode %s %s" - % (self.auth_mode, self.auth_text_simple)) - elif self.auth_mode in ["hmac-sha256", "md5", "hmac-md5"] and self.auth_key_id: - xml_intf += CE_NC_XML_SET_AUTH_MD5 % ( - self.auth_key_id, self.auth_text_md5) - self.updates_cmd.pop() - self.updates_cmd.append("ospf authentication-mode %s %s %s" - % (self.auth_mode, self.auth_key_id, self.auth_text_md5)) - else: - pass - if not xml_intf: - self.updates_cmd.pop() # remove command: interface - - if not xml_ospf and not xml_intf: - return - - xml_sum = CE_NC_XML_SET_IF_NAME % self.interface - xml_sum += xml_ospf + xml_intf - xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id, - self.get_area_ip(), - (CE_NC_XML_BUILD_MERGE_INTF % xml_sum)) - self.netconf_set_config(xml_str, "MERGE_INTERFACE_OSPF") - self.changed = True - - def unset_ospf_interface(self): - """set interface ospf disable, and all its ospf attributes will be removed""" - - intf_dict = self.ospf_info["interface"] - xml_sum = "" - xml_intf = CE_NC_XML_SET_IF_NAME % self.interface - if intf_dict.get("silentEnable") == "true": - xml_sum += CE_NC_XML_BUILD_MERGE_INTF % ( - xml_intf + (CE_NC_XML_SET_SILENT % "false")) - self.updates_cmd.append("ospf %s" % self.process_id) - self.updates_cmd.append("area %s" % self.get_area_ip()) - self.updates_cmd.append( - "undo silent-interface %s" % self.interface) - - xml_sum += CE_NC_XML_BUILD_DELETE_INTF % xml_intf - xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id, - self.get_area_ip(), - xml_sum) - self.netconf_set_config(xml_str, "DELETE_INTERFACE_OSPF") - self.updates_cmd.append("undo ospf cost") - self.updates_cmd.append("undo ospf timer hello") - self.updates_cmd.append("undo ospf timer dead") - self.updates_cmd.append("undo ospf authentication-mode") - self.updates_cmd.append("undo ospf enable %s area %s" % ( - self.process_id, self.get_area_ip())) - self.changed = True - - def check_params(self): - """Check all input params""" - - self.interface = self.interface.replace(" ", "").upper() - - # interface check - if not get_interface_type(self.interface): - self.module.fail_json(msg="Error: interface is invalid.") - - # process_id check - if not self.process_id.isdigit(): - self.module.fail_json(msg="Error: process_id is not digit.") - if int(self.process_id) < 1 or int(self.process_id) > 4294967295: - self.module.fail_json(msg="Error: process_id must be an integer between 1 and 4294967295.") - - # area check - if self.area.isdigit(): - if int(self.area) < 0 or int(self.area) > 4294967295: - self.module.fail_json(msg="Error: area id (Integer) must be between 0 and 4294967295.") - else: - if not is_valid_v4addr(self.area): - self.module.fail_json(msg="Error: area id is invalid.") - - # area authentication check - if self.state == "present": - if self.auth_mode: - if self.auth_mode == "simple": - if self.auth_text_simple and len(self.auth_text_simple) > 8: - self.module.fail_json( - msg="Error: auth_text_simple is not in the range from 1 to 8.") - if self.auth_mode in ["hmac-sha256", "hmac-sha256", "md5"]: - if self.auth_key_id and not self.auth_text_md5: - self.module.fail_json( - msg='Error: auth_key_id and auth_text_md5 should be set at the same time.') - if not self.auth_key_id and self.auth_text_md5: - self.module.fail_json( - msg='Error: auth_key_id and auth_text_md5 should be set at the same time.') - if self.auth_key_id: - if not self.auth_key_id.isdigit(): - self.module.fail_json( - msg="Error: auth_key_id is not digit.") - if int(self.auth_key_id) < 1 or int(self.auth_key_id) > 255: - self.module.fail_json( - msg="Error: auth_key_id is not in the range from 1 to 255.") - if self.auth_text_md5 and len(self.auth_text_md5) > 255: - self.module.fail_json( - msg="Error: auth_text_md5 is not in the range from 1 to 255.") - # cost check - if self.cost: - if not self.cost.isdigit(): - self.module.fail_json(msg="Error: cost is not digit.") - if int(self.cost) < 1 or int(self.cost) > 65535: - self.module.fail_json( - msg="Error: cost is not in the range from 1 to 65535") - - # hello_interval check - if self.hello_interval: - if not self.hello_interval.isdigit(): - self.module.fail_json( - msg="Error: hello_interval is not digit.") - if int(self.hello_interval) < 1 or int(self.hello_interval) > 65535: - self.module.fail_json( - msg="Error: hello_interval is not in the range from 1 to 65535") - - # dead_interval check - if self.dead_interval: - if not self.dead_interval.isdigit(): - self.module.fail_json(msg="Error: dead_interval is not digit.") - if int(self.dead_interval) < 1 or int(self.dead_interval) > 235926000: - self.module.fail_json( - msg="Error: dead_interval is not in the range from 1 to 235926000") - - def get_proposed(self): - """get proposed info""" - - self.proposed["interface"] = self.interface - self.proposed["process_id"] = self.process_id - self.proposed["area"] = self.get_area_ip() - self.proposed["cost"] = self.cost - self.proposed["hello_interval"] = self.hello_interval - self.proposed["dead_interval"] = self.dead_interval - self.proposed["silent_interface"] = self.silent_interface - if self.auth_mode: - self.proposed["auth_mode"] = self.auth_mode - if self.auth_mode == "simple": - self.proposed["auth_text_simple"] = self.auth_text_simple - if self.auth_mode in ["hmac-sha256", "hmac-sha256", "md5"]: - self.proposed["auth_key_id"] = self.auth_key_id - self.proposed["auth_text_md5"] = self.auth_text_md5 - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.ospf_info: - return - - if self.ospf_info["interface"]: - self.existing["interface"] = self.interface - self.existing["cost"] = self.ospf_info["interface"].get("configCost") - self.existing["hello_interval"] = self.ospf_info["interface"].get("helloInterval") - self.existing["dead_interval"] = self.ospf_info["interface"].get("deadInterval") - self.existing["silent_interface"] = self.ospf_info["interface"].get("silentEnable") - self.existing["auth_mode"] = self.ospf_info["interface"].get("authenticationMode") - self.existing["auth_text_simple"] = self.ospf_info["interface"].get("authTextSimple") - self.existing["auth_key_id"] = self.ospf_info["interface"].get("keyId") - self.existing["auth_text_md5"] = self.ospf_info["interface"].get("authTextMd5") - self.existing["process_id"] = self.ospf_info["processId"] - self.existing["area"] = self.ospf_info["areaId"] - - def get_end_state(self): - """get end state info""" - - ospf_info = self.get_ospf_dict() - if not ospf_info: - return - - if ospf_info["interface"]: - self.end_state["interface"] = self.interface - self.end_state["cost"] = ospf_info["interface"].get("configCost") - self.end_state["hello_interval"] = ospf_info["interface"].get("helloInterval") - self.end_state["dead_interval"] = ospf_info["interface"].get("deadInterval") - self.end_state["silent_interface"] = ospf_info["interface"].get("silentEnable") - self.end_state["auth_mode"] = ospf_info["interface"].get("authenticationMode") - self.end_state["auth_text_simple"] = ospf_info["interface"].get("authTextSimple") - self.end_state["auth_key_id"] = ospf_info["interface"].get("keyId") - self.end_state["auth_text_md5"] = ospf_info["interface"].get("authTextMd5") - self.end_state["process_id"] = ospf_info["processId"] - self.end_state["area"] = ospf_info["areaId"] - - def work(self): - """worker""" - - self.check_params() - self.ospf_info = self.get_ospf_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - if self.state == "present": - if not self.ospf_info or not self.ospf_info["interface"]: - # create ospf area and set interface config - self.set_ospf_interface() - else: - # merge interface ospf area config - self.merge_ospf_interface() - else: - if self.ospf_info and self.ospf_info["interface"]: - # delete interface ospf area config - self.unset_ospf_interface() - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - interface=dict(required=True, type='str'), - process_id=dict(required=True, type='str'), - area=dict(required=True, type='str'), - cost=dict(required=False, type='str'), - hello_interval=dict(required=False, type='str'), - dead_interval=dict(required=False, type='str'), - silent_interface=dict(required=False, default=False, type='bool'), - auth_mode=dict(required=False, - choices=['none', 'null', 'hmac-sha256', 'md5', 'hmac-md5', 'simple'], type='str'), - auth_text_simple=dict(required=False, type='str', no_log=True), - auth_key_id=dict(required=False, type='str'), - auth_text_md5=dict(required=False, type='str', no_log=True), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - - argument_spec.update(ce_argument_spec) - module = InterfaceOSPF(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_ip_interface.py b/plugins/modules/network/cloudengine/ce_ip_interface.py deleted file mode 100644 index b1a9539c8f..0000000000 --- a/plugins/modules/network/cloudengine/ce_ip_interface.py +++ /dev/null @@ -1,739 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_ip_interface -short_description: Manages L3 attributes for IPv4 and IPv6 interfaces on HUAWEI CloudEngine switches. -description: - - Manages Layer 3 attributes for IPv4 and IPv6 interfaces on HUAWEI CloudEngine switches. -author: QijunPan (@QijunPan) -notes: - - Interface must already be a L3 port when using this module. - - Logical interfaces (loopback, vlanif) must be created first. - - C(mask) must be inserted in decimal format (i.e. 24) for - both IPv6 and IPv4. - - A single interface can have multiple IPv6 configured. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - interface: - description: - - Full name of interface, i.e. 40GE1/0/22, vlanif10. - required: true - addr: - description: - - IPv4 or IPv6 Address. - mask: - description: - - Subnet mask for IPv4 or IPv6 Address in decimal format. - version: - description: - - IP address version. - default: v4 - choices: ['v4','v6'] - ipv4_type: - description: - - Specifies an address type. - The value is an enumerated type. - main, primary IP address. - sub, secondary IP address. - default: main - choices: ['main','sub'] - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: ip_interface module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Ensure ipv4 address is configured on 10GE1/0/22 - ce_ip_interface: - interface: 10GE1/0/22 - version: v4 - state: present - addr: 20.20.20.20 - mask: 24 - provider: '{{ cli }}' - - - name: Ensure ipv4 secondary address is configured on 10GE1/0/22 - ce_ip_interface: - interface: 10GE1/0/22 - version: v4 - state: present - addr: 30.30.30.30 - mask: 24 - ipv4_type: sub - provider: '{{ cli }}' - - - name: Ensure ipv6 is enabled on 10GE1/0/22 - ce_ip_interface: - interface: 10GE1/0/22 - version: v6 - state: present - provider: '{{ cli }}' - - - name: Ensure ipv6 address is configured on 10GE1/0/22 - ce_ip_interface: - interface: 10GE1/0/22 - version: v6 - state: present - addr: 2001::db8:800:200c:cccb - mask: 64 - provider: '{{ cli }}' -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"addr": "20.20.20.20", "interface": "10GE1/0/22", "mask": "24"} -existing: - description: k/v pairs of existing IP attributes on the interface - returned: always - type: dict - sample: {"ipv4": [{"ifIpAddr": "11.11.11.11", "subnetMask": "255.255.0.0", "addrType": "main"}], - "interface": "10GE1/0/22"} -end_state: - description: k/v pairs of IP attributes after module execution - returned: always - type: dict - sample: {"ipv4": [{"ifIpAddr": "20.20.20.20", "subnetMask": "255.255.255.0", "addrType": "main"}], - "interface": "10GE1/0/22"} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["interface 10GE1/0/22", "ip address 20.20.20.20 24"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_INTF = """ - - - - - %s - - - - - - - - - -""" - -CE_NC_ADD_IPV4 = """ - - - - - %s - - - - %s - %s - %s - - - - - - - -""" - -CE_NC_MERGE_IPV4 = """ - - - - - %s - - - - %s - %s - main - - - %s - %s - main - - - - - - - -""" - - -CE_NC_DEL_IPV4 = """ - - - - - %s - - - - %s - %s - %s - - - - - - - -""" - -CE_NC_ADD_IPV6 = """ - - - - - %s - - - - %s - %s - global - - - - - - - -""" - -CE_NC_DEL_IPV6 = """ - - - - - %s - - - - %s - %s - global - - - - - - - -""" - -CE_NC_MERGE_IPV6_ENABLE = """ - - - - - %s - - %s - - - - - -""" - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - if interface.upper().startswith('GE'): - return 'ge' - elif interface.upper().startswith('10GE'): - return '10ge' - elif interface.upper().startswith('25GE'): - return '25ge' - elif interface.upper().startswith('4X10GE'): - return '4x10ge' - elif interface.upper().startswith('40GE'): - return '40ge' - elif interface.upper().startswith('100GE'): - return '100ge' - elif interface.upper().startswith('VLANIF'): - return 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - return 'loopback' - elif interface.upper().startswith('METH'): - return 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - return 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - return 'vbdif' - elif interface.upper().startswith('NVE'): - return 'nve' - elif interface.upper().startswith('TUNNEL'): - return 'tunnel' - elif interface.upper().startswith('ETHERNET'): - return 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - return 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - return 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - return 'stack-port' - elif interface.upper().startswith('NULL'): - return 'null' - else: - return None - - -def is_valid_v4addr(addr): - """check is ipv4 addr is valid""" - - if not addr: - return False - - if addr.find('.') != -1: - addr_list = addr.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - -class IpInterface(object): - """ - Manages L3 attributes for IPv4 and IPv6 interfaces. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info] - self.interface = self.module.params['interface'] - self.addr = self.module.params['addr'] - self.mask = self.module.params['mask'] - self.version = self.module.params['version'] - self.ipv4_type = self.module.params['ipv4_type'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - # interface info - self.intf_info = dict() - self.intf_type = None - - def __init_module__(self): - """ init module """ - - required_if = [("version", "v4", ("addr", "mask"))] - required_together = [("addr", "mask")] - self.module = AnsibleModule( - argument_spec=self.spec, - required_if=required_if, - required_together=required_together, - supports_check_mode=True - ) - - def netconf_set_config(self, xml_str, xml_name): - """ netconf set config """ - - rcv_xml = set_nc_config(self.module, xml_str) - if "" not in rcv_xml: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_interface_dict(self, ifname): - """ get one interface attributes dict.""" - - intf_info = dict() - conf_str = CE_NC_GET_INTF % ifname - rcv_xml = get_nc_config(self.module, conf_str) - - if "" in rcv_xml: - return intf_info - - # get interface base info - intf = re.findall( - r'.*(.*).*\s*' - r'(.*).*', rcv_xml) - - if intf: - intf_info = dict(ifName=intf[0][0], - isL2SwitchPort=intf[0][1]) - - # get interface ipv4 address info - ipv4_info = re.findall( - r'.*(.*).*\s*(.*)' - r'.*\s*(.*).*', rcv_xml) - intf_info["am4CfgAddr"] = list() - for info in ipv4_info: - intf_info["am4CfgAddr"].append( - dict(ifIpAddr=info[0], subnetMask=info[1], addrType=info[2])) - - # get interface ipv6 address info - ipv6_info = re.findall( - r'.*.*\s*(.*).*', rcv_xml) - if not ipv6_info: - self.module.fail_json(msg='Error: Fail to get interface %s IPv6 state.' % self.interface) - else: - intf_info["enableFlag"] = ipv6_info[0] - - # get interface ipv6 enable info - ipv6_info = re.findall( - r'.*(.*).*\s*(.*)' - r'.*\s*(.*).*', rcv_xml) - - intf_info["am6CfgAddr"] = list() - for info in ipv6_info: - intf_info["am6CfgAddr"].append( - dict(ifIp6Addr=info[0], addrPrefixLen=info[1], addrType6=info[2])) - - return intf_info - - def convert_len_to_mask(self, masklen): - """convert mask length to ip address mask, i.e. 24 to 255.255.255.0""" - - mask_int = ["0"] * 4 - length = int(masklen) - - if length > 32: - self.module.fail_json(msg='Error: IPv4 ipaddress mask length is invalid.') - if length < 8: - mask_int[0] = str(int((0xFF << (8 - length % 8)) & 0xFF)) - if length >= 8: - mask_int[0] = '255' - mask_int[1] = str(int((0xFF << (16 - (length % 16))) & 0xFF)) - if length >= 16: - mask_int[1] = '255' - mask_int[2] = str(int((0xFF << (24 - (length % 24))) & 0xFF)) - if length >= 24: - mask_int[2] = '255' - mask_int[3] = str(int((0xFF << (32 - (length % 32))) & 0xFF)) - if length == 32: - mask_int[3] = '255' - - return '.'.join(mask_int) - - def is_ipv4_exist(self, addr, maskstr, ipv4_type): - """"Check IPv4 address exist""" - - addrs = self.intf_info["am4CfgAddr"] - if not addrs: - return False - - for address in addrs: - if address["ifIpAddr"] == addr: - return address["subnetMask"] == maskstr and address["addrType"] == ipv4_type - return False - - def get_ipv4_main_addr(self): - """get IPv4 main address""" - - addrs = self.intf_info["am4CfgAddr"] - if not addrs: - return None - - for address in addrs: - if address["addrType"] == "main": - return address - - return None - - def is_ipv6_exist(self, addr, masklen): - """Check IPv6 address exist""" - - addrs = self.intf_info["am6CfgAddr"] - if not addrs: - return False - - for address in addrs: - if address["ifIp6Addr"] == addr.upper(): - if address["addrPrefixLen"] == masklen and address["addrType6"] == "global": - return True - else: - self.module.fail_json( - msg="Error: Input IPv6 address or mask is invalid.") - - return False - - def set_ipv4_addr(self, ifname, addr, mask, ipv4_type): - """Set interface IPv4 address""" - - if not addr or not mask or not type: - return - - maskstr = self.convert_len_to_mask(mask) - if self.state == "present": - if not self.is_ipv4_exist(addr, maskstr, ipv4_type): - # primary IP address - if ipv4_type == "main": - main_addr = self.get_ipv4_main_addr() - if not main_addr: - # no ipv4 main address in this interface - xml_str = CE_NC_ADD_IPV4 % (ifname, addr, maskstr, ipv4_type) - self.netconf_set_config(xml_str, "ADD_IPV4_ADDR") - else: - # remove old address and set new - xml_str = CE_NC_MERGE_IPV4 % (ifname, main_addr["ifIpAddr"], - main_addr["subnetMask"], - addr, maskstr) - self.netconf_set_config(xml_str, "MERGE_IPV4_ADDR") - # secondary IP address - else: - xml_str = CE_NC_ADD_IPV4 % (ifname, addr, maskstr, ipv4_type) - self.netconf_set_config(xml_str, "ADD_IPV4_ADDR") - - self.updates_cmd.append("interface %s" % ifname) - if ipv4_type == "main": - self.updates_cmd.append("ip address %s %s" % (addr, maskstr)) - else: - self.updates_cmd.append("ip address %s %s sub" % (addr, maskstr)) - self.changed = True - else: - if self.is_ipv4_exist(addr, maskstr, ipv4_type): - xml_str = CE_NC_DEL_IPV4 % (ifname, addr, maskstr, ipv4_type) - self.netconf_set_config(xml_str, "DEL_IPV4_ADDR") - self.updates_cmd.append("interface %s" % ifname) - if ipv4_type == "main": - self.updates_cmd.append("undo ip address %s %s" % (addr, maskstr)) - else: - self.updates_cmd.append("undo ip address %s %s sub" % (addr, maskstr)) - self.changed = True - - def set_ipv6_addr(self, ifname, addr, mask): - """Set interface IPv6 address""" - - if not addr or not mask: - return - - if self.state == "present": - self.updates_cmd.append("interface %s" % ifname) - if self.intf_info["enableFlag"] == "false": - xml_str = CE_NC_MERGE_IPV6_ENABLE % (ifname, "true") - self.netconf_set_config(xml_str, "SET_IPV6_ENABLE") - self.updates_cmd.append("ipv6 enable") - self.changed = True - - if not self.is_ipv6_exist(addr, mask): - xml_str = CE_NC_ADD_IPV6 % (ifname, addr, mask) - self.netconf_set_config(xml_str, "ADD_IPV6_ADDR") - - self.updates_cmd.append("ipv6 address %s %s" % (addr, mask)) - self.changed = True - - if not self.changed: - self.updates_cmd.pop() - else: - if self.is_ipv6_exist(addr, mask): - xml_str = CE_NC_DEL_IPV6 % (ifname, addr, mask) - self.netconf_set_config(xml_str, "DEL_IPV6_ADDR") - self.updates_cmd.append("interface %s" % ifname) - self.updates_cmd.append( - "undo ipv6 address %s %s" % (addr, mask)) - self.changed = True - - def set_ipv6_enable(self, ifname): - """Set interface IPv6 enable""" - - if self.state == "present": - if self.intf_info["enableFlag"] == "false": - xml_str = CE_NC_MERGE_IPV6_ENABLE % (ifname, "true") - self.netconf_set_config(xml_str, "SET_IPV6_ENABLE") - self.updates_cmd.append("interface %s" % ifname) - self.updates_cmd.append("ipv6 enable") - self.changed = True - else: - if self.intf_info["enableFlag"] == "true": - xml_str = CE_NC_MERGE_IPV6_ENABLE % (ifname, "false") - self.netconf_set_config(xml_str, "SET_IPV6_DISABLE") - self.updates_cmd.append("interface %s" % ifname) - self.updates_cmd.append("undo ipv6 enable") - self.changed = True - - def check_params(self): - """Check all input params""" - - # check interface type - if self.interface: - self.intf_type = get_interface_type(self.interface) - if not self.intf_type: - self.module.fail_json( - msg='Error: Interface name of %s ' - 'is error.' % self.interface) - - # ipv4 addr and mask check - if self.version == "v4": - if not is_valid_v4addr(self.addr): - self.module.fail_json( - msg='Error: The %s is not a valid address.' % self.addr) - if not self.mask.isdigit(): - self.module.fail_json(msg='Error: mask is invalid.') - if int(self.mask) > 32 or int(self.mask) < 1: - self.module.fail_json( - msg='Error: mask must be an integer between 1 and 32.') - - # ipv6 mask check - if self.version == "v6": - if self.addr: - if not self.mask.isdigit(): - self.module.fail_json(msg='Error: mask is invalid.') - if int(self.mask) > 128 or int(self.mask) < 1: - self.module.fail_json( - msg='Error: mask must be an integer between 1 and 128.') - - # interface and layer3 check - self.intf_info = self.get_interface_dict(self.interface) - if not self.intf_info: - self.module.fail_json(msg='Error: interface %s does not exist.' % self.interface) - - if self.intf_info["isL2SwitchPort"] == "true": - self.module.fail_json(msg='Error: interface %s is layer2.' % self.interface) - - def get_proposed(self): - """get proposed info""" - - self.proposed["state"] = self.state - self.proposed["addr"] = self.addr - self.proposed["mask"] = self.mask - self.proposed["ipv4_type"] = self.ipv4_type - self.proposed["version"] = self.version - self.proposed["interface"] = self.interface - - def get_existing(self): - """get existing info""" - - self.existing["interface"] = self.interface - self.existing["ipv4addr"] = self.intf_info["am4CfgAddr"] - self.existing["ipv6addr"] = self.intf_info["am6CfgAddr"] - self.existing["ipv6enalbe"] = self.intf_info["enableFlag"] - - def get_end_state(self): - """get end state info""" - - intf_info = self.get_interface_dict(self.interface) - self.end_state["interface"] = self.interface - self.end_state["ipv4addr"] = intf_info["am4CfgAddr"] - self.end_state["ipv6addr"] = intf_info["am6CfgAddr"] - self.end_state["ipv6enalbe"] = intf_info["enableFlag"] - - def work(self): - """worker""" - - self.check_params() - self.get_existing() - self.get_proposed() - - # deal present or absent - if self.version == "v4": - self.set_ipv4_addr(self.interface, self.addr, self.mask, self.ipv4_type) - else: - if not self.addr and not self.mask: - self.set_ipv6_enable(self.interface) - else: - self.set_ipv6_addr(self.interface, self.addr, self.mask) - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - interface=dict(required=True), - addr=dict(required=False), - version=dict(required=False, choices=['v4', 'v6'], - default='v4'), - mask=dict(type='str', required=False), - ipv4_type=dict(required=False, choices=['main', 'sub'], default='main'), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - - argument_spec.update(ce_argument_spec) - module = IpInterface(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_is_is_instance.py b/plugins/modules/network/cloudengine/ce_is_is_instance.py deleted file mode 100644 index 555c5ba778..0000000000 --- a/plugins/modules/network/cloudengine/ce_is_is_instance.py +++ /dev/null @@ -1,330 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# 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 = r''' ---- -module: ce_is_is_instance -author: xuxiaowei0512 (@CloudEngine-Ansible) -short_description: Manages isis process id configuration on HUAWEI CloudEngine devices. -description: - - Manages isis process id, creates a isis instance id or deletes a process id on HUAWEI CloudEngine devices. -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - This module works with connection C(netconf). -options: - instance_id: - description: - - Specifies the id of a isis process.The value is a number of 1 to 4294967295. - required: true - type: int - vpn_name: - description: - - VPN Instance, associate the VPN instance with the corresponding IS-IS process. - type: str - state: - description: - - Determines whether the config should be present or not on the device. - default: present - type: str - choices: ['present', 'absent'] -''' - -EXAMPLES = r''' - - name: Set isis process - ce_is_is_instance: - instance_id: 3 - state: present - - - name: Unset isis process - ce_is_is_instance: - instance_id: 3 - state: absent - - - name: check isis process - ce_is_is_instance: - instance_id: 4294967296 - state: present - - - name: Set vpn name - ce_is_is_instance: - instance_id: 22 - vpn_name: vpn1 - state: present - - - name: check vpn name - ce_is_is_instance: - instance_id: 22 - vpn_name: vpn1234567896321452212221556asdasdasdasdsadvdv - state: present -''' - -RETURN = r''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "instance_id": 1, - "vpn_name": null - } -existing: - description: k/v pairs of existing configuration - returned: always - type: dict - sample: { - "session": {} - } -end_state: - description: k/v pairs of configuration after module execution - returned: always - type: dict - sample: { - "session": { - "instance_id": 1, - "vpn_name": null - } - } -updates: - description: commands sent to the device - returned: always - type: list - sample: [ - "isis 1" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config - -CE_NC_GET_ISIS = """ - - - %s - - -""" - -CE_NC_GET_ISIS_INSTANCE = """ - - - %s - - - -""" - - -def is_valid_ip_vpn(vpname): - """check ip vpn""" - - if not vpname: - return False - - if vpname == "_public_": - return False - - if len(vpname) < 1 or len(vpname) > 31: - return False - - return True - - -class ISIS_Instance(object): - """Manages ISIS Instance""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.instance_id = self.module.params['instance_id'] - self.vpn_name = self.module.params['vpn_name'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.isis_dict = dict() - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def get_isis_dict(self): - """isis config dict""" - isis_dict = dict() - isis_dict["instance"] = dict() - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_INSTANCE % self.instance_id)) - - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return isis_dict - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # get isis info - glb = root.find("isiscomm/isSites/isSite") - if glb: - for attr in glb: - isis_dict["instance"][attr.tag] = attr.text - - return isis_dict - - def config_session(self): - """configures isis""" - xml_str = "" - instance = self.isis_dict["instance"] - if not self.instance_id: - return xml_str - - if self.state == "present": - xml_str = "%s" % self.instance_id - self.updates_cmd.append("isis %s" % self.instance_id) - - if self.vpn_name: - xml_str += "%s" % self.vpn_name - self.updates_cmd.append("vpn-instance %s" % self.vpn_name) - else: - # absent - if self.instance_id and str(self.instance_id) == instance.get("instanceId"): - xml_str = "%s" % self.instance_id - self.updates_cmd.append("undo isis %s" % self.instance_id) - - if self.state == "present": - return '' + xml_str + '' - else: - if xml_str: - return '' + xml_str + '' - - def netconf_load_config(self, xml_str): - """load isis config by netconf""" - - if not xml_str: - return - - xml_cfg = """ - - - %s - - """ % xml_str - set_nc_config(self.module, xml_cfg) - self.changed = True - - def check_params(self): - """Check all input params""" - - # check instance id - if not self.instance_id: - self.module.fail_json(msg="Error: Missing required arguments: instance_id.") - - if self.instance_id: - if self.instance_id < 1 or self.instance_id > 4294967295: - self.module.fail_json(msg="Error: Instance id is not ranges from 1 to 4294967295.") - - # check vpn_name - if self.vpn_name: - if not is_valid_ip_vpn(self.vpn_name): - self.module.fail_json(msg="Error: Session vpn_name is invalid.") - - def get_proposed(self): - """get proposed info""" - # base config - self.proposed["instance_id"] = self.instance_id - self.proposed["vpn_name"] = self.vpn_name - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.isis_dict: - self.existing["instance"] = None - - self.existing["instance"] = self.isis_dict.get("instance") - - def get_end_state(self): - """get end state info""" - - isis_dict = self.get_isis_dict() - if not isis_dict: - self.end_state["instance"] = None - - self.end_state["instance"] = isis_dict.get("instance") - - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - self.check_params() - self.isis_dict = self.get_isis_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - xml_str = '' - if self.instance_id: - cfg_str = self.config_session() - if cfg_str: - xml_str += cfg_str - - # update to device - if xml_str: - self.netconf_load_config(xml_str) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - argument_spec = dict( - instance_id=dict(required=True, type='int'), - vpn_name=dict(required=False, type='str'), - state=dict(required=False, default='present', choices=['present', 'absent']) - ) - - module = ISIS_Instance(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_is_is_interface.py b/plugins/modules/network/cloudengine/ce_is_is_interface.py deleted file mode 100644 index 41e47d0243..0000000000 --- a/plugins/modules/network/cloudengine/ce_is_is_interface.py +++ /dev/null @@ -1,788 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# 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: ce_is_is_interface -author: xuxiaowei0512 (@CloudEngine-Ansible) -short_description: Manages isis interface configuration on HUAWEI CloudEngine devices. -description: - - Manages isis process id, creates a isis instance id or deletes a process id on HUAWEI CloudEngine devices. -notes: - - Interface must already be a L3 port when using this module. - - This module requires the netconf system service be enabled on the remote device being managed. - - This module works with connection C(netconf). -options: - instance_id: - description: - - Specifies the id of a isis process. - The value is a number of 1 to 4294967295. - required: true - type: int - ifname: - description: - - A L3 interface. - required: true - type: str - leveltype: - description: - - level type for three types. - type: str - choices: ['level_1', 'level_2', 'level_1_2'] - level1dispriority: - description: - - the dispriority of the level1. - The value is a number of 1 to 127. - type: int - level2dispriority: - description: - - the dispriority of the level1. - The value is a number of 1 to 127. - type: int - silentenable: - description: - - enable the interface can send isis message. - The value is a bool type. - type: bool - silentcost: - description: - - Specifies whether the routing cost of the silent interface is 0. - The value is a bool type. - type: bool - typep2penable: - description: - - Simulate the network type of the interface as P2P. - The value is a bool type. - type: bool - snpacheck: - description: - - Enable SNPA check for LSPs and SNPs. - The value is a bool type. - type: bool - p2pnegotiationmode: - description: - - Set the P2P neighbor negotiation type. - type: str - choices: ['2_way', '3_way', '3_wayonly'] - p2ppeeripignore: - description: - - When the P2P hello packet is received, no IP address check is performed. - The value is a bool type. - type: bool - ppposicpcheckenable: - description: - - Interface for setting PPP link protocol to check OSICP negotiation status. - The value is a bool type. - type: bool - level1cost: - description: - - Specifies the link cost of the interface when performing Level-1 SPF calculation. - The value is a number of 0 to 16777215. - type: int - level2cost: - description: - - Specifies the link cost of the interface when performing Level-2 SPF calculation. - The value is a number of 0 to 16777215. - type: int - bfdstaticen: - description: - - Configure static BFD on a specific interface enabled with ISIS. - The value is a bool type. - type: bool - bfdblocken: - description: - - Blocking interfaces to dynamically create BFD features. - The value is a bool type. - type: bool - state: - description: - - Determines whether the config should be present or not on the device. - type: str - default: 'present' - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' - - name: "create vlan and config vlanif" - ce_config: - lines: 'vlan {{ test_vlan_id }},quit,interface {{test_intf_vlanif}},ip address {{test_vlanif_ip}} 24' - match: none - - - name: "create eth-trunk and config eth-trunk" - ce_config: - lines: 'interface {{test_intf_trunk}},undo portswitch,ip address {{test_trunk_ip}} 24' - match: none - - - name: "create vpn instance" - ce_config: - lines: 'ip vpn-instance {{test_vpn}},ipv4-family' - match: none - - - name: Set isis circuit-level - ce_is_is_interface: - instance_id: 3 - ifname: Eth-Trunk10 - leveltype: level_1_2 - state: present - - - name: Set isis level1dispriority - ce_is_is_interface: - instance_id: 3 - ifname: Eth-Trunk10 - level1dispriority: 0 - state: present - - - name: Set isis level2dispriority - ce_is_is_interface: - instance_id: 3 - ifname: Eth-Trunk10 - level2dispriority: 0 - state: present - - - name: Set isis silentenable - ce_is_is_interface: - instance_id: 3 - ifname: Eth-Trunk10 - silentenable: true - state: present - - - name: Set vpn name - ce_is_is_instance: - instance_id: 22 - vpn_name: vpn1 - state: present -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "addr_type": null, - "create_type": null, - "dest_addr": null, - "out_if_name": "10GE1/0/1", - "session_name": "bfd_l2link", - "src_addr": null, - "state": "present", - "use_default_ip": true, - "vrf_name": null - } -existing: - description: k/v pairs of existing configuration - returned: always - type: dict - sample: { - "session": {} - } -end_state: - description: k/v pairs of configuration after module execution - returned: always - type: dict - sample: { - "session": { - "addrType": "IPV4", - "createType": "SESS_STATIC", - "destAddr": null, - "outIfName": "10GE1/0/1", - "sessName": "bfd_l2link", - "srcAddr": null, - "useDefaultIp": "true", - "vrfName": null - } - } -updates: - description: commands sent to the device - returned: always - type: list - sample: [ - "bfd bfd_l2link bind peer-ip default-ip interface 10ge1/0/1" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import sys -import socket -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config - -CE_NC_GET_ISIS = """ - - - %s - - -""" - -CE_NC_GET_ISIS_INTERFACE = """ - - - %s - - - - - - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_INTERFACE = """ - - - %s - - - %s - - - - -""" - -CE_NC_DELETE_ISIS_INTERFACE = """ - - - %s - - - %s - - - - -""" - -CE_NC_GET_ISIS_BFDINTERFACE = """ - - - %s - - - afIpv4 - 0 - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_BFDINTERFACE = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_DELETE_ISIS_BFDINTERFACE = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - - -def is_valid_ip_vpn(vpname): - """check ip vpn""" - - if not vpname: - return False - - if vpname == "_public_": - return False - - if len(vpname) < 1 or len(vpname) > 31: - return False - - return True - - -def check_ip_addr(ipaddr): - """check ip address, Supports IPv4 and IPv6""" - - if not ipaddr or '\x00' in ipaddr: - return False - - try: - res = socket.getaddrinfo(ipaddr, 0, socket.AF_UNSPEC, - socket.SOCK_STREAM, - 0, socket.AI_NUMERICHOST) - return bool(res) - except socket.gaierror: - err = sys.exc_info()[1] - if err.args[0] == socket.EAI_NONAME: - return False - raise - - return True - - -def check_default_ip(ipaddr): - """check the default multicast IP address""" - - # The value ranges from 224.0.0.107 to 224.0.0.250 - if not check_ip_addr(ipaddr): - return False - - if ipaddr.count(".") != 3: - return False - - ips = ipaddr.split(".") - if ips[0] != "224" or ips[1] != "0" or ips[2] != "0": - return False - - if not ips[3].isdigit() or int(ips[3]) < 107 or int(ips[3]) > 250: - return False - - return True - - -def get_interface_type(interface): - """get the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface.upper().startswith('GE'): - return 'ge' - elif interface.upper().startswith('10GE'): - return '10ge' - elif interface.upper().startswith('25GE'): - return '25ge' - elif interface.upper().startswith('4X10GE'): - return '4x10ge' - elif interface.upper().startswith('40GE'): - return '40ge' - elif interface.upper().startswith('100GE'): - return '100ge' - elif interface.upper().startswith('VLANIF'): - return 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - return 'loopback' - elif interface.upper().startswith('METH'): - return 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - return 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - return 'vbdif' - elif interface.upper().startswith('NVE'): - return 'nve' - elif interface.upper().startswith('TUNNEL'): - return 'tunnel' - elif interface.upper().startswith('ETHERNET'): - return 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - return 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - return 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - return 'stack-port' - elif interface.upper().startswith('NULL'): - return'null' - else: - return None - - -class ISIS_Instance(object): - """Manages ISIS Instance""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.instance_id = self.module.params['instance_id'] - self.ifname = self.module.params['ifname'] - self.leveltype = self.module.params['leveltype'] - self.level1dispriority = self.module.params['level1dispriority'] - self.level2dispriority = self.module.params['level2dispriority'] - self.silentenable = self.module.params['silentenable'] - self.silentcost = self.module.params['silentcost'] - self.typep2penable = self.module.params['typep2penable'] - self.snpacheck = self.module.params['snpacheck'] - self.p2pnegotiationmode = self.module.params['p2pnegotiationmode'] - self.p2ppeeripignore = self.module.params['p2ppeeripignore'] - self.ppposicpcheckenable = self.module.params['ppposicpcheckenable'] - self.level1cost = self.module.params['level1cost'] - self.level2cost = self.module.params['level2cost'] - self.bfdstaticen = self.module.params['bfdstaticen'] - self.bfdblocken = self.module.params['bfdblocken'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.isis_dict = dict() - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - mutually_exclusive = [["level1dispriority", "level2dispriority"], - ["level1cost", "level2cost"]] - self.module = AnsibleModule( - argument_spec=self.spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def get_isis_dict(self): - """bfd config dict""" - - isis_dict = dict() - isis_dict["instance"] = dict() - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_INTERFACE % self.instance_id)) - if self.bfdstaticen or self.bfdblocken: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_BFDINTERFACE % self.instance_id)) - - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return isis_dict - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # - glb = root.find("isiscomm/isSites/isSite/isCircuits/isCircuit") - if self.bfdstaticen or self.bfdblocken: - glb = root.find("isiscomm/isSites/isSite/isSiteMTs/isSiteMT/isCircMts/isCircMt") - if glb: - for attr in glb: - isis_dict["instance"][attr.tag] = attr.text - - return isis_dict - - def config_session(self): - """configures bfd session""" - - xml_str = "" - instance = self.isis_dict["instance"] - if not self.instance_id: - return xml_str - if self.ifname: - xml_str = "%s" % self.ifname - self.updates_cmd.append("interface %s" % self.ifname) - if self.state == "present": - self.updates_cmd.append("isis enable %s" % self.instance_id) - - if self.leveltype: - if self.leveltype == "level_1": - xml_str += "level_1" - self.updates_cmd.append("isis circuit-level level-1") - elif self.leveltype == "level_2": - xml_str += "level_2" - self.updates_cmd.append("isis circuit-level level-2") - elif self.leveltype == "level_1_2": - xml_str += "level_1_2" - self.updates_cmd.append("isis circuit-level level-1-2") - if self.level1dispriority is not None: - xml_str += "%s" % self.level1dispriority - self.updates_cmd.append("isis dis-priority %s level-1" % self.level1dispriority) - if self.level2dispriority is not None: - xml_str += "%s" % self.level2dispriority - self.updates_cmd.append("isis dis-priority %s level-2" % self.level2dispriority) - if self.p2pnegotiationmode: - if self.p2pnegotiationmode == "2_way": - xml_str += "2_way" - self.updates_cmd.append("isis ppp-negotiation 2-way") - elif self.p2pnegotiationmode == "3_way": - xml_str += "3_way" - self.updates_cmd.append("isis ppp-negotiation 3-way") - elif self.p2pnegotiationmode == "3_wayonly": - xml_str += "3_wayonly" - self.updates_cmd.append("isis ppp-negotiation only") - if self.level1cost is not None: - xml_str += "%s" % self.level1cost - self.updates_cmd.append("isis cost %s level-1" % self.level1cost) - if self.level2cost is not None: - xml_str += "%s" % self.level2cost - self.updates_cmd.append("isis cost %s level-2" % self.level2cost) - - else: - # absent - self.updates_cmd.append("undo isis enable") - if self.leveltype and self.leveltype == instance.get("circuitLevelType"): - xml_str += "level_1_2" - self.updates_cmd.append("undo isis circuit-level") - if self.level1dispriority is not None and self.level1dispriority == instance.get("level1DisPriority"): - xml_str += "64" - self.updates_cmd.append("undo isis dis-priority %s level-1" % self.level1dispriority) - if self.level2dispriority is not None and self.level2dispriority == instance.get("level2dispriority"): - xml_str += "64" - self.updates_cmd.append("undo isis dis-priority %s level-2" % self.level2dispriority) - if self.p2pnegotiationmode and self.p2pnegotiationmode == instance.get("p2pNegotiationMode"): - xml_str += "" - self.updates_cmd.append("undo isis ppp-negotiation") - if self.level1cost is not None and self.level1cost == instance.get("level1Cost"): - xml_str += "" - self.updates_cmd.append("undo isis cost %s level-1" % self.level1cost) - if self.level2cost is not None and self.level2cost == instance.get("level2Cost"): - xml_str += "" - self.updates_cmd.append("undo isis cost %s level-2" % self.level2cost) - - if self.silentenable and instance.get("silentEnable", "false") == "false": - xml_str += "true" - self.updates_cmd.append("isis silent") - elif not self.silentenable and instance.get("silentEnable", "false") == "true": - xml_str += "false" - self.updates_cmd.append("undo isis silent") - - if self.silentcost and instance.get("silentCost", "false") == "false": - xml_str += "true" - self.updates_cmd.append("isis silent advertise-zero-cost") - elif not self.silentcost and instance.get("silentCost", "false") == "true": - xml_str += "false" - - if self.typep2penable and instance.get("typeP2pEnable", "false") == "false": - xml_str += "true" - self.updates_cmd.append("isis circuit-type p2p") - elif not self.typep2penable and instance.get("typeP2pEnable", "false") == "true": - xml_str += "false" - self.updates_cmd.append("undo isis circuit-type") - - if self.snpacheck and instance.get("snpaCheck", "false") == "false": - xml_str += "true" - self.updates_cmd.append("isis circuit-type p2p strict-snpa-check") - elif not self.snpacheck and instance.get("snpaCheck", "false") == "true": - xml_str += "false" - - if self.p2ppeeripignore and instance.get("p2pPeerIPIgnore", "false") == "false": - xml_str += "true" - self.updates_cmd.append("isis peer-ip-ignore") - elif not self.p2ppeeripignore and instance.get("p2pPeerIPIgnore", "false") == "true": - xml_str += "false" - self.updates_cmd.append("undo isis peer-ip-ignore") - - if self.ppposicpcheckenable and instance.get("pPPOsicpCheckEnable", "false") == "false": - xml_str += "true" - self.updates_cmd.append("isis ppp-osicp-check") - elif not self.ppposicpcheckenable and instance.get("pPPOsicpCheckEnable", "false") == "true": - xml_str += "false" - self.updates_cmd.append("undo isis ppp-osicp-check") - if self.bfdstaticen and instance.get("bfdStaticEn", "false") == "false": - xml_str += "true" - self.updates_cmd.append("isis bfd static") - elif not self.bfdstaticen and instance.get("bfdStaticEn", "false") == "true": - xml_str += "false" - self.updates_cmd.append("undo isis bfd static") - if self.bfdblocken and instance.get("bfdBlockEn", "false") == "false": - xml_str += "true" - self.updates_cmd.append("isis bfd block") - elif not self.bfdblocken and instance.get("bfdBlockEn", "false") == "true": - xml_str += "false" - self.updates_cmd.append("undo isis bfd block") - - if self.state == "present": - if self.bfdstaticen is not None or self.bfdblocken is not None: - return CE_NC_MERGE_ISIS_BFDINTERFACE % (self.instance_id, xml_str) - return CE_NC_MERGE_ISIS_INTERFACE % (self.instance_id, xml_str) - else: - if self.bfdstaticen is not None or self.bfdblocken is not None: - return CE_NC_DELETE_ISIS_BFDINTERFACE % (self.instance_id, xml_str) - return CE_NC_DELETE_ISIS_INTERFACE % (self.instance_id, xml_str) - - def netconf_load_config(self, xml_str): - """load bfd config by netconf""" - - if not xml_str: - return - - xml_cfg = """ - - - %s - - """ % xml_str - set_nc_config(self.module, xml_cfg) - self.changed = True - - def check_params(self): - """Check all input params""" - - # check instance id - if not self.instance_id: - self.module.fail_json(msg="Error: Missing required arguments: instance_id.") - - if self.instance_id: - if self.instance_id < 1 or self.instance_id > 4294967295: - self.module.fail_json(msg="Error: Instance id is not ranges from 1 to 4294967295.") - - # check level1dispriority - if self.level1dispriority is not None: - if self.level1dispriority < 0 or self.level1dispriority > 127: - self.module.fail_json(msg="Error: level1dispriority is not ranges from 0 to 127.") - - if self.level2dispriority is not None: - if self.level2dispriority < 0 or self.level2dispriority > 127: - self.module.fail_json(msg="Error: level2dispriority is not ranges from 0 to 127.") - - if self.level1cost is not None: - if self.level1cost < 0 or self.level1cost > 16777215: - self.module.fail_json(msg="Error: level1cost is not ranges from 0 to 16777215.") - - if self.level2cost is not None: - if self.level2cost < 0 or self.level2cost > 16777215: - self.module.fail_json(msg="Error: level2cost is not ranges from 0 to 16777215.") - - def get_proposed(self): - """get proposed info""" - self.proposed["instance_id"] = self.instance_id - self.proposed["ifname"] = self.ifname - self.proposed["leveltype"] = self.leveltype - self.proposed["level1dispriority"] = self.level1dispriority - self.proposed["level2dispriority"] = self.level2dispriority - self.proposed["silentenable"] = self.silentenable - self.proposed["silentcost"] = self.silentcost - self.proposed["typep2penable"] = self.typep2penable - self.proposed["snpacheck"] = self.snpacheck - self.proposed["p2pnegotiationmode"] = self.p2pnegotiationmode - self.proposed["p2ppeeripignore"] = self.p2ppeeripignore - self.proposed["ppposicpcheckenable"] = self.ppposicpcheckenable - self.proposed["level1cost"] = self.level1cost - self.proposed["level2cost"] = self.level2cost - self.proposed["bfdstaticen"] = self.bfdstaticen - self.proposed["bfdblocken"] = self.bfdblocken - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.isis_dict: - self.existing["instance"] = None - else: - self.existing["instance"] = self.isis_dict.get("instance") - - def get_end_state(self): - """get end state info""" - - isis_dict = self.get_isis_dict() - if not isis_dict: - self.end_state["instance"] = None - else: - self.end_state["instance"] = isis_dict.get("instance") - if self.existing == self.end_state: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.isis_dict = self.get_isis_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - xml_str = '' - if self.instance_id: - xml_str += self.config_session() - - # update to device - if xml_str: - self.netconf_load_config(xml_str) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - instance_id=dict(required=True, type='int'), - ifname=dict(required=True, type='str'), - leveltype=dict(required=False, type='str', choices=['level_1', 'level_2', 'level_1_2']), - level1dispriority=dict(required=False, type='int'), - level2dispriority=dict(required=False, type='int'), - silentenable=dict(required=False, type='bool'), - silentcost=dict(required=False, type='bool'), - typep2penable=dict(required=False, type='bool'), - snpacheck=dict(required=False, type='bool'), - p2pnegotiationmode=dict(required=False, type='str', choices=['2_way', '3_way', '3_wayonly']), - p2ppeeripignore=dict(required=False, type='bool'), - ppposicpcheckenable=dict(required=False, type='bool'), - level1cost=dict(required=False, type='int'), - level2cost=dict(required=False, type='int'), - bfdstaticen=dict(required=False, type='bool'), - bfdblocken=dict(required=False, type='bool'), - state=dict(required=False, default='present', choices=['present', 'absent']) - ) - - module = ISIS_Instance(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_is_is_view.py b/plugins/modules/network/cloudengine/ce_is_is_view.py deleted file mode 100644 index 7a8b279ae9..0000000000 --- a/plugins/modules/network/cloudengine/ce_is_is_view.py +++ /dev/null @@ -1,1955 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# 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: ce_is_is_view -author: xuxiaowei0512 (@CloudEngine-Ansible) -short_description: Manages isis view configuration on HUAWEI CloudEngine devices. -description: - - Manages isis process id, creates a isis instance id or deletes a process id - on HUAWEI CloudEngine devices. -options: - coststyle: - description: - - Specifies the cost style. - type: str - choices: ['narrow', 'wide', 'transition', 'ntransition', 'wtransition'] - cost_type: - description: - - Specifies the cost type. - type: str - choices: ['external', 'internal'] - defaultmode: - description: - - Specifies the default mode. - type: str - choices: ['always', 'matchDefault', 'matchAny'] - export_policytype: - description: - - Specifies the default mode. - type: str - choices: ['aclNumOrName', 'ipPrefix', 'routePolicy'] - export_protocol: - description: - - Specifies the export router protocol. - type: str - choices: ['direct', 'ospf', 'isis', 'static', 'rip', 'bgp', 'ospfv3', 'all'] - impotr_leveltype: - description: - - Specifies the export router protocol. - type: str - choices: ['level_1', 'level_2', 'level_1_2'] - islevel: - description: - - Specifies the isis level. - type: str - choices: ['level_1', 'level_2', 'level_1_2'] - level_type: - description: - - Specifies the isis level type. - type: str - choices: ['level_1', 'level_2', 'level_1_2'] - penetration_direct: - description: - - Specifies the penetration direct. - type: str - choices: ['level2-level1', 'level1-level2'] - protocol: - description: - - Specifies the protocol. - type: str - choices: ['direct', 'ospf', 'isis', 'static', 'rip', 'bgp', 'ospfv3', 'all'] - aclnum_or_name: - description: - - Specifies the acl number or name for isis. - type: str - allow_filter: - description: - - Specifies the alow filter or not. - type: bool - allow_up_down: - description: - - Specifies the alow up or down. - type: bool - autocostenable: - description: - - Specifies the alow auto cost enable. - type: bool - autocostenablecompatible: - description: - - Specifies the alow auto cost enable compatible. - type: bool - avoid_learning: - description: - - Specifies the alow avoid learning. - type: bool - bfd_min_tx: - description: - - Specifies the bfd min sent package. - type: int - bfd_min_rx: - description: - - Specifies the bfd min received package. - type: int - bfd_multiplier_num: - description: - - Specifies the bfd multiplier number. - type: int - cost: - description: - - Specifies the bfd cost. - type: int - description: - description: - - Specifies description of isis. - type: str - enablelevel1tolevel2: - description: - - Enable level1 to level2. - type: bool - export_aclnumorname: - description: - - Specifies export acl number or name. - type: str - export_ipprefix: - description: - - Specifies export ip prefix. - type: str - export_processid: - description: - - Specifies export process id. - type: int - export_routepolicyname: - description: - - Specifies export route policy name. - type: str - import_aclnumorname: - description: - - Specifies import acl number or name. - type: str - import_cost: - description: - - Specifies import cost. - type: int - import_ipprefix: - description: - - Specifies import ip prefix. - type: str - import_route_policy: - description: - - Specifies import route policy. - type: str - import_routepolicy_name: - description: - - Specifies import route policy name. - type: str - import_routepolicyname: - description: - - Specifies import route policy name. - type: str - import_tag: - description: - - Specifies import tag. - type: int - inheritcost: - description: - - Enable inherit cost. - type: bool - instance_id: - description: - - Specifies instance id. - type: int - ip_address: - description: - - Specifies ip address. - type: str - ip_prefix_name: - description: - - Specifies ip prefix name. - type: str - max_load: - description: - - Specifies route max load. - type: int - mode_routepolicyname: - description: - - Specifies the mode of route polic yname. - type: str - mode_tag: - description: - - Specifies the tag of mode. - type: int - netentity: - description: - - Specifies the netentity. - type: str - permitibgp: - description: - - Specifies the permitibgp. - type: bool - processid: - description: - - Specifies the process id. - type: int - relaxspfLimit: - description: - - Specifies enable the relax spf limit. - type: bool - route_policy_name: - description: - - Specifies the route policy name. - type: str - stdbandwidth: - description: - - Specifies the std band width. - type: int - stdlevel1cost: - description: - - Specifies the std level1 cost. - type: int - stdlevel2cost: - description: - - Specifies the std level2 cost. - type: int - tag: - description: - - Specifies the isis tag. - type: int - weight: - description: - - Specifies the isis weight. - type: int - preference_value: - description: - - Specifies the preference value. - type: int - state: - description: - - Determines whether the config should be present or not on the device. - default: present - type: str - choices: ['present', 'absent'] -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - This module works with connection C(netconf). -''' - -EXAMPLES = ''' - - name: Set isis description - ce_is_is_view: - instance_id: 3 - description: abcdeggfs - state: present - - - name: Set isis islevel - ce_is_is_view: - instance_id: 3 - islevel: level_1 - state: present - - name: Set isis coststyle - ce_is_is_view: - instance_id: 3 - coststyle: narrow - state: present - - - name: Set isis stdlevel1cost - ce_is_is_view: - instance_id: 3 - stdlevel1cost: 63 - state: present - - - name: set isis stdlevel2cost - ce_is_is_view: - instance_id: 3 - stdlevel2cost: 63 - state: present - - - name: set isis stdbandwidth - ce_is_is_view: - instance_id: 3 - stdbandwidth: 1 - state: present -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "state": "present" - } -existing: - description: k/v pairs of existing configuration - returned: always - type: dict - sample: { - "session": {} - } -end_state: - description: k/v pairs of configuration after module execution - returned: always - type: dict - sample: { - "session": { - "addrType": "IPV4", - "createType": "SESS_STATIC", - "destAddr": null, - "outIfName": "10GE1/0/1", - "sessName": "bfd_l2link", - "srcAddr": null, - "useDefaultIp": "true", - "vrfName": null - } - } -updates: - description: commands sent to the device - returned: always - type: list - sample: [ - "bfd bfd_l2link bind peer-ip default-ip interface 10ge1/0/1" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import sys -import socket -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config - -CE_NC_GET_ISIS = """ - - - %s - - -""" - -CE_NC_GET_ISIS_INSTANCE = """ - - - %s - - - - - - - - - - - -""" - -CE_NC_GET_ISIS_ENTITY = """ - - - %s - - - - - - - -""" - -CE_NC_CREAT_ISIS_ENTITY = """ - - - %s - - - %s - - - - -""" - -CE_NC_DELATE_ISIS_ENTITY = """ - - - %s - - - %s - - - - -""" - -CE_NC_GET_ISIS_PREFERENCE = """ - - - %s - - - - - - - - - - - - -""" - -CE_NC_MREGE_ISIS_PREFERENCE = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_DELETE_ISIS_PREFERENCE = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_GET_ISIS_MAXLOAD = """ - - - %s - - - afIpv4 - 0 - - - - - -""" - -CE_NC_MERGE_ISIS_MAXLOAD = """ - - - %s - - - afIpv4 - 0 - %s - - - - -""" - -CE_NC_DELETE_ISIS_MAXLOAD = """ - - - %s - - - afIpv4 - 0 - 32 - - - - -""" - -CE_NC_GET_ISIS_NEXTHOP = """ - - - %s - - - afIpv4 - 0 - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_NEXTHOP = """ - - - %s - - - afIpv4 - 0 - - - %s - %s - - - - - - -""" - -CE_NC_DELETE_ISIS_NEXTHOP = """ - - - %s - - - afIpv4 - 0 - - - %s - 1 - - - - - - -""" - -CE_NC_GET_ISIS_LEAKROUTELEVEL2 = """ - - - %s - - - afIpv4 - 0 - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_LEAKROUTELEVEL2 = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_DELETE_ISIS_LEAKROUTELEVEL2 = """ - - - %s - - - afIpv4 - 0 - - - 0 - - - - false - - - - - - -""" - -CE_NC_GET_ISIS_LEAKROUTELEVEL1 = """ - - - %s - - - afIpv4 - 0 - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_LEAKROUTELEVEL1 = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_DELETE_ISIS_LEAKROUTELEVEL1 = """ - - - %s - - - afIpv4 - 0 - - - 0 - - - - false - false - - - - - - -""" - -CE_NC_GET_ISIS_DEFAULTROUTE = """ - - - %s - - - afIpv4 - 0 - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_DEFAULTROUTE = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_DELETE_ISIS_DEFAULTROUTE = """ - - - %s - - - afIpv4 - 0 - - - always - 0 - 0 - level_2 - false - - - - - - -""" - -CE_NC_GET_ISIS_IMPORTROUTE = """ - - - %s - - - afIpv4 - 0 - - - - - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_IMPORTROUTE = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_GET_ISIS_EXPORTROUTE = """ - - - %s - - - afIpv4 - 0 - - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_EXPORTROUTE = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_GET_ISIS_IMPORTIPROUTE = """ - - - %s - - - afIpv4 - 0 - - - - - - - - - - - - -""" - -CE_NC_MERGE_ISIS_IMPORTIPROUTE = """ - - - %s - - - afIpv4 - 0 - - - %s - - - - - - -""" - -CE_NC_GET_ISIS_BFDLINK = """ - - - %s - - - afIpv4 - 0 - - - - - - - -""" - -CE_NC_MERGE_ISIS_BFDLINK = """ - - - %s - - - afIpv4 - 0 - %s - - - - -""" - -CE_NC_DELETE_ISIS_BFDLINK = """ - - - %s - - - afIpv4 - 0 - 3 - 3 - 3 - - - - -""" - - -def is_valid_ip_vpn(vpname): - """check ip vpn""" - - if not vpname: - return False - - if vpname == "_public_": - return False - - if len(vpname) < 1 or len(vpname) > 31: - return False - - return True - - -def check_ip_addr(ipaddr): - """check ip address, Supports IPv4 and IPv6""" - - if not ipaddr or '\x00' in ipaddr: - return False - - try: - res = socket.getaddrinfo(ipaddr, 0, socket.AF_UNSPEC, - socket.SOCK_STREAM, - 0, socket.AI_NUMERICHOST) - return bool(res) - except socket.gaierror: - err = sys.exc_info()[1] - if err.args[0] == socket.EAI_NONAME: - return False - raise - - return True - - -def check_default_ip(ipaddr): - """check the default multicast IP address""" - - # The value ranges from 224.0.0.107 to 224.0.0.250 - if not check_ip_addr(ipaddr): - return False - - if ipaddr.count(".") != 3: - return False - - ips = ipaddr.split(".") - if ips[0] != "224" or ips[1] != "0" or ips[2] != "0": - return False - - if not ips[3].isdigit() or int(ips[3]) < 107 or int(ips[3]) > 250: - return False - - return True - - -def get_interface_type(interface): - """get the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class ISIS_View(object): - """Manages ISIS Instance""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.instance_id = self.module.params['instance_id'] - self.description = self.module.params['description'] - self.islevel = self.module.params['islevel'] - self.coststyle = self.module.params['coststyle'] - self.relaxspfLimit = self.module.params['relaxspfLimit'] - self.stdlevel1cost = self.module.params['stdlevel1cost'] - self.stdlevel2cost = self.module.params['stdlevel2cost'] - self.stdbandwidth = self.module.params['stdbandwidth'] - self.autocostenable = self.module.params['autocostenable'] - self.autocostenablecompatible = self.module.params['autocostenablecompatible'] - self.netentity = self.module.params['netentity'] - self.preference_value = self.module.params['preference_value'] - self.route_policy_name = self.module.params['route_policy_name'] - self.max_load = self.module.params['max_load'] - self.ip_address = self.module.params['ip_address'] - self.weight = self.module.params['weight'] - self.aclnum_or_name = self.module.params['aclnum_or_name'] - self.ip_prefix_name = self.module.params['ip_prefix_name'] - self.import_routepolicy_name = self.module.params['import_routepolicy_name'] - self.tag = self.module.params['tag'] - self.allow_filter = self.module.params['allow_filter'] - self.allow_up_down = self.module.params['allow_up_down'] - self.penetration_direct = self.module.params['penetration_direct'] - self.enablelevel1tolevel2 = self.module.params['enablelevel1tolevel2'] - self.defaultmode = self.module.params['defaultmode'] - self.mode_routepolicyname = self.module.params['mode_routepolicyname'] - self.cost = self.module.params['cost'] - self.mode_tag = self.module.params['mode_tag'] - self.level_type = self.module.params['level_type'] - self.avoid_learning = self.module.params['avoid_learning'] - self.protocol = self.module.params['protocol'] - self.processid = self.module.params['processid'] - self.cost_type = self.module.params['cost_type'] - self.import_cost = self.module.params['import_cost'] - self.import_tag = self.module.params['import_tag'] - self.impotr_leveltype = self.module.params['impotr_leveltype'] - self.import_route_policy = self.module.params['import_route_policy'] - self.inheritcost = self.module.params['inheritcost'] - self.permitibgp = self.module.params['permitibgp'] - self.avoid_learning = self.module.params['avoid_learning'] - self.export_protocol = self.module.params['export_protocol'] - self.export_policytype = self.module.params['export_policytype'] - self.export_processid = self.module.params['export_processid'] - self.export_aclnumorname = self.module.params['export_aclnumorname'] - self.export_ipprefix = self.module.params['export_ipprefix'] - self.export_routepolicyname = self.module.params['export_routepolicyname'] - self.import_aclnumorname = self.module.params['import_aclnumorname'] - self.import_ipprefix = self.module.params['import_ipprefix'] - self.import_routepolicyname = self.module.params['import_routepolicyname'] - self.bfd_min_rx = self.module.params['bfd_min_rx'] - self.bfd_min_tx = self.module.params['bfd_min_tx'] - self.bfd_multiplier_num = self.module.params['bfd_multiplier_num'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.isis_dict = dict() - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - - mutually_exclusive = [["stdlevel1cost", "stdlevel2cost"], - ["aclnum_or_name", "ip_prefix_name", "import_routepolicy_name"], - ["export_aclnumorname", "import_ipprefix", "import_routepolicyname"]] - required_together = [('ip_address', 'weight')] - self.module = AnsibleModule( - argument_spec=self.spec, - mutually_exclusive=mutually_exclusive, - required_together=required_together, - supports_check_mode=True) - - def get_isis_dict(self): - """bfd config dict""" - - isis_dict = dict() - isis_dict["instance"] = dict() - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_INSTANCE % self.instance_id)) - - if self.netentity: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_ENTITY % self.instance_id)) - - if self.route_policy_name or self.preference_value: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_PREFERENCE % self.instance_id)) - if self.max_load: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_MAXLOAD % self.instance_id)) - if self.ip_address: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_NEXTHOP % self.instance_id)) - if self.penetration_direct and self.penetration_direct == "level2-level1": - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_LEAKROUTELEVEL2 % self.instance_id)) - elif self.penetration_direct and self.penetration_direct == "level1-level2": - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_LEAKROUTELEVEL1 % self.instance_id)) - elif self.defaultmode: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_DEFAULTROUTE % self.instance_id)) - elif self.protocol: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_IMPORTROUTE % self.instance_id)) - elif self.export_protocol: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_EXPORTROUTE % self.instance_id)) - elif self.bfd_min_rx or self.bfd_min_tx or self.bfd_multiplier_num: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_BFDLINK % self.instance_id)) - elif self.import_aclnumorname or self.import_ipprefix or self.import_ipprefix: - conf_str = CE_NC_GET_ISIS % ( - (CE_NC_GET_ISIS_IMPORTIPROUTE % self.instance_id)) - xml_str = get_nc_config(self.module, conf_str) - - if "" in xml_str: - return isis_dict - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # get bfd global info - if self.netentity: - glb = root.find("isiscomm/isSites/isSite/isNetEntitys/isNetEntity") - elif self.route_policy_name or self.preference_value: - glb = root.find("isiscomm/isSites/isSite//isSiteMTs/isSiteMT/isPreferences/isPreference") - elif self.max_load: - glb = root.find("isiscomm/isSites/isSite/isSiteMTs/isSiteMT") - elif self.ip_address: - glb = root.find("isiscomm/isSites/isSite/isSiteMTs/isSiteMT/isNextHopWeights/isNextHopWeight") - elif self.penetration_direct and self.penetration_direct == "level2-level1": - glb = root.find("isiscomm/isSites/isSite/isSiteMTs/isSiteMT/isLeakRouteLevel2ToLevel1s/isLeakRouteLevel2ToLevel1") - elif self.penetration_direct and self.penetration_direct == "level1-level2": - glb = root.find( - "isiscomm/isSites/isSite/isSiteMTs/isSiteMT/isLeakRouteLevel1ToLevel2s/isLeakRouteLevel1ToLevel2") - elif self.defaultmode: - glb = root.find( - "isiscomm/isSites/isSite/isSiteMTs/isSiteMT/isDefaultRoutes/isDefaultRoute") - elif self.protocol: - glb = root.find( - "isiscomm/isSites/isSite/isSiteMTs/isSiteMT/isImportRoutes/isImportRoute") - elif self.export_protocol: - glb = root.find( - "isiscomm/isSites/isSite/isSiteMTs/isSiteMT/isFilterExports/isFilterExport") - elif self.bfd_min_rx or self.bfd_min_tx or self.bfd_multiplier_num: - glb = root.find( - "isiscomm/isSites/isSite/isSiteMTs/isSiteMT") - elif self.import_aclnumorname or self.import_ipprefix or self.import_ipprefix: - glb = root.find( - "isiscomm/isSites/isSite/isSiteMTs/isSiteMT/isFilterImports/isFilterImport") - else: - glb = root.find("isiscomm/isSites/isSite") - - if glb is not None: - for attr in glb: - isis_dict["instance"][attr.tag] = attr.text - - return isis_dict - - def config_session(self): - """configures bfd session""" - - xml_str = "" - instance = self.isis_dict["instance"] - if not self.instance_id: - return xml_str - xml_str = "%s" % self.instance_id - self.updates_cmd.append("isis %s" % self.instance_id) - cmd_list = list() - - if self.state == "present": - if self.description and self.description != instance.get("description"): - xml_str += "%s" % self.description - self.updates_cmd.append("description %s" % self.description) - - if self.islevel and self.islevel != instance.get("isLevel"): - xml_str += "%s" % self.islevel - self.updates_cmd.append("is-level %s" % self.islevel) - - if self.coststyle: - if self.coststyle != instance.get("costStyle"): - xml_str += "%s" % self.coststyle - self.updates_cmd.append("cost-style %s" % self.coststyle) - if self.relaxspfLimit and instance.get("relaxSpfLimit", "false") == "false": - xml_str += "true" - self.updates_cmd.append("cost-style %s relax-spf-limit" % self.coststyle) - elif not self.relaxspfLimit and instance.get("relaxSpfLimit", "false") == "true": - xml_str += "false" - self.updates_cmd.append("cost-style %s" % self.coststyle) - - if self.stdlevel1cost and str(self.stdlevel1cost) != instance.get("stdLevel1Cost"): - xml_str += "%s" % self.stdlevel1cost - self.updates_cmd.append("circuit-cost %s level-1" % self.stdlevel1cost) - - if self.stdlevel2cost and str(self.stdlevel2cost) != instance.get("stdLevel2Cost"): - xml_str += "%s" % self.stdlevel2cost - self.updates_cmd.append("circuit-cost %s level-2" % self.stdlevel2cost) - - if self.stdbandwidth and str(self.stdbandwidth) != instance.get("stdbandwidth"): - xml_str += "%s" % self.stdbandwidth - self.updates_cmd.append("bandwidth-reference %s" % self.stdbandwidth) - - if self.netentity and self.netentity != instance.get("netEntity"): - xml_str = CE_NC_CREAT_ISIS_ENTITY % (self.instance_id, self.netentity) - self.updates_cmd.append("network-entity %s" % self.netentity) - - if self.preference_value or self.route_policy_name: - xml_str = "" - cmd_session = "preference" - if self.preference_value and str(self.preference_value) != instance.get("preferenceValue"): - xml_str = "%s" % self.preference_value - cmd_session += " %s" % self.preference_value - if self.route_policy_name and self.route_policy_name != instance.get("routePolicyName"): - xml_str += "%s" % self.route_policy_name - cmd_session += " route-policy %s" % self.route_policy_name - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - xml_str = CE_NC_MREGE_ISIS_PREFERENCE % (self.instance_id, xml_str) - - if self.max_load and str(self.max_load) != instance.get("maxLoadBalancing"): - xml_str = CE_NC_MERGE_ISIS_MAXLOAD % (self.instance_id, self.max_load) - self.updates_cmd.append("maximum load-balancing %s" % self.max_load) - - if self.ip_address: - xml_str = CE_NC_MERGE_ISIS_NEXTHOP % (self.instance_id, self.ip_address, self.weight) - self.updates_cmd.append("nexthop %s weight %s" % (self.ip_address, self.weight)) - - if self.penetration_direct: - xml_str = "" - if self.penetration_direct == "level2-level1": - cmd_session = "import-route isis level-2 into level-1" - elif self.penetration_direct == "level1-level2": - cmd_session = "import-route isis level-1 into level-2" - if self.aclnum_or_name: - xml_str = "%s" % self.aclnum_or_name - xml_str += "aclNumOrName" - if isinstance(self.aclnum_or_name, int): - cmd_session += " filter-policy %s" % self.aclnum_or_name - elif isinstance(self.aclnum_or_name, str): - cmd_session += " filter-policy acl-name %s" % self.aclnum_or_name - if self.ip_prefix_name: - xml_str = "%s" % self.ip_prefix_name - xml_str += "ipPrefix" - cmd_session += " filter-policy ip-prefix %s" % self.ip_prefix_name - if self.import_routepolicy_name: - xml_str = "%s" % self.import_routepolicy_name - xml_str += "routePolicy" - cmd_session += " filter-policy route-policy %s" % self.import_routepolicy_name - if self.tag: - xml_str += "%s" % self.tag - cmd_session += " tag %s" % self.tag - if self.allow_filter or self.allow_up_down: - cmd_session += " direct" - if self.allow_filter: - xml_str += "true" - cmd_session += " allow-filter-policy" - if self.allow_up_down: - xml_str += "true" - cmd_session += " allow-up-down-bit" - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - if self.enablelevel1tolevel2: - xml_str += "true" - self.updates_cmd.append("undo import-route isis level-1 into level-2 disable") - - if self.defaultmode: - cmd_session = "default-route-advertise" - if self.defaultmode == "always": - xml_str = "always" - cmd_session += " always" - elif self.defaultmode == "matchDefault": - xml_str = "matchDefault" - cmd_session += " match default" - elif self.defaultmode == "matchAny": - xml_str = "matchAny" - xml_str += "routePolicy" - xml_str += "%s" % self.mode_routepolicyname - cmd_session += " route-policy %s" % self.mode_routepolicyname - if self.cost is not None: - xml_str += "%s" % self.cost - cmd_session += " cost %s" % self.cost - if self.mode_tag: - xml_str += "%s" % self.mode_tag - cmd_session += " tag %s" % self.mode_tag - if self.level_type: - if self.level_type == "level_1": - xml_str += "level_1" - cmd_session += " level-1" - elif self.level_type == "level_2": - xml_str += "level_2" - cmd_session += " level-2" - elif self.level_type == "level_1_2": - xml_str += "level_1_2" - cmd_session += " level-1-2" - if self.avoid_learning: - xml_str += "true" - cmd_session += " avoid-learning" - elif not self.avoid_learning: - xml_str += "false" - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - - if self.protocol: - cmd_session = "import-route" - if self.protocol == "rip": - xml_str = "rip" - cmd_session += " rip" - elif self.protocol == "isis": - xml_str = "isis" - cmd_session += " isis" - elif self.protocol == "ospf": - xml_str = "ospf" - cmd_session += " ospf" - elif self.protocol == "static": - xml_str = "static" - cmd_session += " static" - elif self.protocol == "direct": - xml_str = "direct" - cmd_session += " direct" - elif self.protocol == "bgp": - xml_str = "bgp" - cmd_session += " bgp" - if self.permitibgp: - xml_str += "true" - cmd_session += " permit-ibgp" - if self.protocol == "rip" or self.protocol == "isis" or self.protocol == "ospf": - xml_str += "%s" % self.processid - cmd_session += " %s" % self.processid - if self.inheritcost: - xml_str += "%s" % self.inheritcost - cmd_session += " inherit-cost" - if self.cost_type: - if self.cost_type == "external": - xml_str += "external" - cmd_session += " cost-type external" - elif self.cost_type == "internal": - xml_str += "internal" - cmd_session += " cost-type internal" - if self.import_cost: - xml_str += "%s" % self.import_cost - cmd_session += " cost %s" % self.import_cost - if self.import_tag: - xml_str += "%s" % self.import_tag - cmd_session += " tag %s" % self.import_tag - if self.import_route_policy: - xml_str += "routePolicy" - xml_str += "%s" % self.import_route_policy - cmd_session += " route-policy %s" % self.import_route_policy - if self.impotr_leveltype: - if self.impotr_leveltype == "level_1": - cmd_session += " level-1" - elif self.impotr_leveltype == "level_2": - cmd_session += " level-2" - elif self.impotr_leveltype == "level_1_2": - cmd_session += " level-1-2" - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - - if self.bfd_min_rx or self.bfd_min_tx or self.bfd_multiplier_num: - xml_str = "" - self.updates_cmd.append("bfd all-interfaces enable") - cmd_session = "bfd all-interfaces" - if self.bfd_min_rx: - xml_str += "%s" % self.bfd_min_rx - cmd_session += " min-rx-interval %s" % self.bfd_min_rx - if self.bfd_min_tx: - xml_str += "%s" % self.bfd_min_tx - cmd_session += " min-tx-interval %s" % self.bfd_min_tx - if self.bfd_multiplier_num: - xml_str += "%s" % self.bfd_multiplier_num - cmd_session += " detect-multiplier %s" % self.bfd_multiplier_num - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - - if self.export_protocol: - cmd_session = "filter-policy" - if self.export_aclnumorname: - xml_str = "aclNumOrName" - xml_str += "%s" % self.export_aclnumorname - if isinstance(self.export_aclnumorname, int): - cmd_session += " %s" % self.export_aclnumorname - elif isinstance(self.export_aclnumorname, str): - cmd_session += " acl-name %s" % self.export_aclnumorname - if self.export_ipprefix: - xml_str = "ipPrefix" - xml_str += "%s" % self.export_ipprefix - cmd_session += " ip-prefix %s" % self.export_ipprefix - if self.export_routepolicyname: - xml_str = "routePolicy" - xml_str += "%s" % self.export_routepolicyname - cmd_session += " route-policy %s" % self.export_routepolicyname - xml_str += "%s" % self.export_protocol - cmd_session += " export %s" % self.export_protocol - if self.export_processid is not None: - xml_str += "%s" % self.export_processid - cmd_session += " %s" % self.export_processid - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - - if self.import_ipprefix or self.import_aclnumorname or self.import_routepolicyname: - cmd_session = "filter-policy" - if self.import_aclnumorname: - xml_str = "aclNumOrName" - xml_str += "%s" % self.import_aclnumorname - if isinstance(self.import_aclnumorname, int): - cmd_session += " %s" % self.import_aclnumorname - elif isinstance(self.import_aclnumorname, str): - cmd_session += " acl-name %s" % self.import_aclnumorname - if self.import_ipprefix: - xml_str = "ipPrefix" - xml_str += "%s" % self.import_ipprefix - cmd_session += " ip-prefix %s" % self.import_ipprefix - if self.import_routepolicyname: - xml_str = "routePolicy" - xml_str += "%s" % self.import_routepolicyname - cmd_session += " route-policy %s" % self.import_routepolicyname - cmd_session += "import" - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - else: - # absent - if self.description and self.description == instance.get("description"): - xml_str += "%s" % self.description - self.updates_cmd.append("undo description") - - if self.islevel and self.islevel == instance.get("isLevel"): - xml_str += "level_1_2" - self.updates_cmd.append("undo is-level") - - if self.coststyle and self.coststyle == instance.get("costStyle"): - xml_str += "%s" % ("narrow") - xml_str += "false" - self.updates_cmd.append("undo cost-style") - - if self.stdlevel1cost and str(self.stdlevel1cost) == instance.get("stdLevel1Cost"): - xml_str += "%s" % self.stdlevel1cost - self.updates_cmd.append("undo circuit-cost %s level-1" % self.stdlevel1cost) - - if self.stdlevel2cost and str(self.stdlevel2cost) == instance.get("stdLevel2Cost"): - xml_str += "%s" % self.stdlevel2cost - self.updates_cmd.append("undo circuit-cost %s level-2" % self.stdlevel2cost) - - if self.stdbandwidth and str(self.stdbandwidth) == instance.get("stdbandwidth"): - xml_str += "100" - self.updates_cmd.append("undo bandwidth-reference") - - if self.netentity and self.netentity == instance.get("netEntity"): - xml_str = CE_NC_DELATE_ISIS_ENTITY % (self.instance_id, self.netentity) - self.updates_cmd.append("undo network-entity %s" % self.netentity) - - if self.preference_value or self.route_policy_name: - xml_str = "" - if self.preference_value and str(self.preference_value) == instance.get("preferenceValue"): - xml_str = "%s" % self.preference_value - if self.route_policy_name and self.route_policy_name == instance.get("routePolicyName"): - xml_str += "%s" % self.route_policy_name - self.updates_cmd.append("undo preference") - elif not self.preference_value and self.route_policy_name and self.route_policy_name == instance.get("routePolicyName"): - xml_str = "%s" % self.route_policy_name - self.updates_cmd.append("undo preference") - xml_str = CE_NC_DELETE_ISIS_PREFERENCE % (self.instance_id, xml_str) - - if self.max_load and str(self.max_load) == instance.get("maxLoadBalancing"): - xml_str = CE_NC_DELETE_ISIS_MAXLOAD % self.instance_id - self.updates_cmd.append("undo maximum load-balancing") - - if self.ip_address: - xml_str = CE_NC_DELETE_ISIS_NEXTHOP % (self.instance_id, self.ip_address) - self.updates_cmd.append("undo nexthop %s" % self.ip_address) - - if self.penetration_direct: - if self.penetration_direct == "level2-level1": - self.updates_cmd.append("undo import-route isis level-2 into level-1") - elif self.penetration_direct == "level1-level2": - self.updates_cmd.append("undo import-route isis level-1 into level-2") - self.updates_cmd.append("import-route isis level-1 into level-2 disable") - - if self.bfd_min_rx or self.bfd_min_tx or self.bfd_multiplier_num is not None: - xml_str = CE_NC_DELETE_ISIS_BFDLINK % self.instance_id - self.updates_cmd.append("undo bfd all-interfaces enable") - cmd_session = "undo bfd all-interfaces" - if self.bfd_min_rx: - cmd_session += " min-rx-interval %s" % self.bfd_min_rx - if self.bfd_min_tx: - cmd_session += " min-tx-interval %s" % self.bfd_min_tx - if self.bfd_multiplier_num: - cmd_session += " detect-multiplier %s" % self.bfd_multiplier_num - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - - if self.defaultmode: - xml_str = CE_NC_DELETE_ISIS_DEFAULTROUTE % self.instance_id - self.updates_cmd.append("undo default-route-advertise") - - if self.protocol: - if self.protocol == "rip" or self.protocol == "isis" or self.protocol == "ospf": - self.updates_cmd.append("undo import-route %s %s" % (self.protocol, self.processid)) - else: - self.updates_cmd.append("undo import-route %s" % self.protocol) - - if self.export_protocol: - cmd_session = "undo filter-policy" - if self.export_aclnumorname: - if isinstance(self.export_aclnumorname, int): - cmd_session += " %s" % self.export_aclnumorname - elif isinstance(self.export_aclnumorname, str): - cmd_session += " acl-name %s" % self.export_aclnumorname - if self.export_ipprefix: - cmd_session += " ip-prefix %s" % self.export_ipprefix - if self.export_routepolicyname: - cmd_session += " route-policy %s" % self.export_routepolicyname - cmd_session += " export %s" % self.export_protocol - if self.export_processid is not None: - cmd_session += " %s" % self.export_processid - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - if self.import_ipprefix or self.import_aclnumorname or self.import_routepolicyname: - cmd_session = "undo filter-policy" - if self.import_aclnumorname: - if isinstance(self.import_aclnumorname, int): - cmd_session += " %s" % self.import_aclnumorname - elif isinstance(self.import_aclnumorname, str): - cmd_session += " acl-name %s" % self.import_aclnumorname - if self.import_ipprefix: - cmd_session += " ip-prefix %s" % self.import_ipprefix - if self.import_routepolicyname: - cmd_session += " route-policy %s" % self.import_routepolicyname - cmd_session += " import" - cmd_list.insert(0, cmd_session) - self.updates_cmd.extend(cmd_list) - - if self.autocostenable and instance.get("stdAutoCostEnable", "false") == "false": - xml_str += "true" - self.updates_cmd.append("auto-cost enable") - elif not self.autocostenable and instance.get("stdAutoCostEnable", "false") == "true": - xml_str += "false" - xml_str += "false" - self.updates_cmd.append("undo auto-cost enable") - - if self.autocostenable: - if self.autocostenablecompatible and instance.get("stdAutoCostEnableCompatible", "false") == "false": - xml_str += "true" - self.updates_cmd.append("auto-cost enable compatible") - elif not self.autocostenablecompatible and instance.get("stdAutoCostEnableCompatible", "false") == "true": - xml_str += "false" - self.updates_cmd.append("auto-cost enable") - - if self.state == "present": - if self.netentity or self.preference_value or self.route_policy_name or self.max_load or self.ip_address: - return xml_str - elif self.penetration_direct: - if self.penetration_direct == "level2-level1": - return CE_NC_MERGE_ISIS_LEAKROUTELEVEL2 % (self.instance_id, xml_str) - elif self.penetration_direct == "level1-level2": - return CE_NC_MERGE_ISIS_LEAKROUTELEVEL1 % (self.instance_id, xml_str) - elif self.defaultmode: - return CE_NC_MERGE_ISIS_DEFAULTROUTE % (self.instance_id, xml_str) - elif self.protocol: - return CE_NC_MERGE_ISIS_IMPORTROUTE % (self.instance_id, xml_str) - elif self.export_protocol: - return CE_NC_MERGE_ISIS_EXPORTROUTE % (self.instance_id, xml_str) - elif self.import_routepolicyname or self.import_aclnumorname or self.import_ipprefix: - return CE_NC_MERGE_ISIS_IMPORTIPROUTE % (self.instance_id, xml_str) - elif self.bfd_min_rx or self.bfd_min_tx or self.bfd_multiplier_num: - return CE_NC_MERGE_ISIS_BFDLINK % (self.instance_id, xml_str) - else: - return '' + xml_str + '' - else: - if self.netentity or self.preference_value or self.route_policy_name or self.max_load \ - or self.ip_address or self.defaultmode or self.bfd_min_rx or self.bfd_min_tx or self.bfd_multiplier_num is not None: - return xml_str - else: - return '' + xml_str + '' - - def netconf_load_config(self, xml_str): - """load bfd config by netconf""" - - if not xml_str: - return - if xml_str == "%s" % self.instance_id: - pass - else: - xml_cfg = """ - - - %s - - """ % xml_str - set_nc_config(self.module, xml_cfg) - self.changed = True - - def check_params(self): - """Check all input params""" - levelcost = 16777215 - if not self.instance_id: - self.module.fail_json(msg="Error: Missing required arguments: instance_id.") - - if self.instance_id: - if self.instance_id < 1 or self.instance_id > 4294967295: - self.module.fail_json(msg="Error: Instance id is not ranges from 1 to 4294967295.") - - # check description - if self.description: - if len(self.description) < 1 or len(self.description) > 80: - self.module.fail_json(msg="Error: description is invalid.") - - # - if self.stdbandwidth: - if self.stdbandwidth < 1 or self.stdbandwidth > 2147483648: - self.module.fail_json(msg="Error: stdbandwidth is not ranges from 1 to 2147483648.") - - if self.relaxspfLimit is not None and not self.coststyle: - self.module.fail_json(msg="Error: relaxspfLimit must set after coststyle.") - - if self.coststyle: - if self.coststyle != "wide" and self.coststyle != "wtransition": - levelcost = 63 - else: - levelcost = 16777215 - if self.stdlevel1cost: - if self.stdlevel1cost < 1 or self.stdlevel1cost > levelcost: - self.module.fail_json(msg="Error: stdlevel1cost is not ranges from 1 to %s." % levelcost) - - if self.stdlevel2cost: - if self.stdlevel2cost < 1 or self.stdlevel2cost > levelcost: - self.module.fail_json(msg="Error: stdlevel2cost is not ranges from 1 to %s." % levelcost) - - if self.coststyle: - if self.coststyle != "ntransition" and self.coststyle != "transition": - if self.relaxspfLimit: - self.module.fail_json(msg="Error: relaxspfLimit can not be set while the coststyle is not ntransition or transition") - - if self.autocostenablecompatible: - if not self.autocostenable: - self.module.fail_json(msg="Error: you shoule enable the autocostenable first.") - - if self.preference_value: - if self.preference_value < 1 or self.preference_value > 255: - self.module.fail_json(msg="Error: preference_value is not ranges from 1 to 255.") - - if self.route_policy_name: - if len(self.route_policy_name) < 1 or len(self.route_policy_name) > 200: - self.module.fail_json(msg="Error: route_policy_name is invalid.") - - if self.max_load: - if self.max_load < 1 or self.max_load > 32: - self.module.fail_json(msg="Error: max_load is not ranges from 1 to 32.") - - if self.weight: - if self.weight < 1 or self.weight > 254: - self.module.fail_json(msg="Error: weight is not ranges from 1 to 254.") - - if self.aclnum_or_name: - if isinstance(self.aclnum_or_name, int): - if self.aclnum_or_name < 2000 or self.aclnum_or_name > 2999: - self.module.fail_json(msg="Error: acl_num is not ranges from 2000 to 2999.") - elif isinstance(self.aclnum_or_name, str): - if len(self.aclnum_or_name) < 1 or len(self.aclnum_or_name) > 32: - self.module.fail_json(msg="Error: acl_name is invalid.") - if self.ip_prefix_name: - if len(self.ip_prefix_name) < 1 or len(self.ip_prefix_name) > 169: - self.module.fail_json(msg="Error: ip_prefix_name is invalid.") - if self.import_routepolicy_name: - if len(self.import_routepolicy_name) < 1 or len(self.import_routepolicy_name) > 200: - self.module.fail_json(msg="Error: import_routepolicy_name is invalid.") - if self.tag: - if self.tag < 1 or self.tag > 4294967295: - self.module.fail_json(msg="Error: tag is not ranges from 1 to 4294967295.") - - if self.mode_routepolicyname: - if len(self.mode_routepolicyname) < 1 or len(self.mode_routepolicyname) > 200: - self.module.fail_json(msg="Error: mode_routepolicyname is invalid.") - if self.cost is not None: - if self.cost < 0 or self.cost > 4261412864: - self.module.fail_json(msg="Error: cost is not ranges from 0 to 4261412864.") - if self.mode_tag: - if self.mode_tag < 1 or self.mode_tag > 4294967295: - self.module.fail_json(msg="Error: mode_tag is not ranges from 1 to 4294967295.") - - if self.processid is not None: - if self.processid < 0 or self.processid > 4294967295: - self.module.fail_json(msg="Error: processid is not ranges from 0 to 4294967295.") - - if self.import_cost is not None: - if self.import_cost < 0 or self.import_cost > 4261412864: - self.module.fail_json(msg="Error: import_cost is not ranges from 0 to 4261412864.") - - if self.import_tag: - if self.import_tag < 1 or self.import_tag > 4294967295: - self.module.fail_json(msg="Error: import_tag is not ranges from 1 to 4294967295.") - - if self.export_aclnumorname: - if isinstance(self.export_aclnumorname, int): - if self.export_aclnumorname < 2000 or self.export_aclnumorname > 2999: - self.module.fail_json(msg="Error: acl_num is not ranges from 2000 to 2999.") - elif isinstance(self.export_aclnumorname, str): - if len(self.export_aclnumorname) < 1 or len(self.export_aclnumorname) > 32: - self.module.fail_json(msg="Error: acl_name is invalid.") - - if self.export_processid: - if self.export_processid < 1 or self.export_processid > 4294967295: - self.module.fail_json(msg="Error: export_processid is not ranges from 1 to 4294967295.") - - if self.export_ipprefix: - if len(self.export_ipprefix) < 1 or len(self.export_ipprefix) > 169: - self.module.fail_json(msg="Error: export_ipprefix is invalid.") - - if self.export_routepolicyname: - if len(self.export_routepolicyname) < 1 or len(self.export_routepolicyname) > 200: - self.module.fail_json(msg="Error: export_routepolicyname is invalid.") - - if self.bfd_min_rx: - if self.bfd_min_rx < 50 or self.bfd_min_rx > 1000: - self.module.fail_json(msg="Error: bfd_min_rx is not ranges from 50 to 1000.") - - if self.bfd_min_tx: - if self.bfd_min_tx < 50 or self.bfd_min_tx > 1000: - self.module.fail_json(msg="Error: bfd_min_tx is not ranges from 50 to 1000.") - - if self.bfd_multiplier_num: - if self.bfd_multiplier_num < 3 or self.bfd_multiplier_num > 50: - self.module.fail_json(msg="Error: bfd_multiplier_num is not ranges from 3 to 50.") - - if self.import_routepolicyname: - if len(self.import_routepolicyname) < 1 or len(self.import_routepolicyname) > 200: - self.module.fail_json(msg="Error: import_routepolicyname is invalid.") - - if self.import_aclnumorname: - if isinstance(self.import_aclnumorname, int): - if self.import_aclnumorname < 2000 or self.import_aclnumorname > 2999: - self.module.fail_json(msg="Error: acl_num is not ranges from 2000 to 2999.") - elif isinstance(self.import_aclnumorname, str): - if len(self.import_aclnumorname) < 1 or len(self.import_aclnumorname) > 32: - self.module.fail_json(msg="Error: acl_name is invalid.") - - def get_proposed(self): - """get proposed info""" - # base config - self.proposed["instance_id"] = self.instance_id - self.proposed["description"] = self.description - self.proposed["islevel"] = self.islevel - self.proposed["coststyle"] = self.coststyle - self.proposed["relaxspfLimit"] = self.relaxspfLimit - self.proposed["stdlevel1cost"] = self.stdlevel1cost - self.proposed["stdlevel2cost"] = self.stdlevel2cost - self.proposed["stdbandwidth"] = self.stdbandwidth - self.proposed["autocostenable"] = self.autocostenable - self.proposed["autocostenablecompatible"] = self.autocostenablecompatible - self.proposed["netentity"] = self.netentity - self.proposed["preference_value"] = self.preference_value - self.proposed["route_policy_name"] = self.route_policy_name - self.proposed["max_load"] = self.max_load - self.proposed["ip_address"] = self.ip_address - self.proposed["weight"] = self.weight - self.proposed["penetration_direct"] = self.penetration_direct - self.proposed["aclnum_or_name"] = self.aclnum_or_name - self.proposed["ip_prefix_name"] = self.ip_prefix_name - self.proposed["import_routepolicy_name"] = self.import_routepolicy_name - self.proposed["tag"] = self.tag - self.proposed["allow_filter"] = self.allow_filter - self.proposed["allow_up_down"] = self.allow_up_down - self.proposed["enablelevel1tolevel2"] = self.enablelevel1tolevel2 - self.proposed["protocol"] = self.protocol - self.proposed["processid"] = self.processid - self.proposed["cost_type"] = self.cost_type - self.proposed["import_cost"] = self.import_cost - self.proposed["import_tag"] = self.import_tag - self.proposed["import_route_policy"] = self.import_route_policy - self.proposed["impotr_leveltype"] = self.impotr_leveltype - self.proposed["inheritcost"] = self.inheritcost - self.proposed["permitibgp"] = self.permitibgp - self.proposed["export_protocol"] = self.export_protocol - self.proposed["export_policytype"] = self.export_policytype - self.proposed["export_processid"] = self.export_processid - self.proposed["export_aclnumorname"] = self.export_aclnumorname - self.proposed["export_ipprefix"] = self.export_ipprefix - self.proposed["export_routepolicyname"] = self.export_routepolicyname - self.proposed["import_aclnumorname"] = self.import_aclnumorname - self.proposed["import_ipprefix"] = self.import_ipprefix - self.proposed["import_routepolicyname"] = self.import_routepolicyname - self.proposed["bfd_min_rx"] = self.bfd_min_rx - self.proposed["bfd_min_tx"] = self.bfd_min_tx - self.proposed["bfd_multiplier_num"] = self.bfd_multiplier_num - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.isis_dict: - self.existing["instance"] = None - else: - self.existing["instance"] = self.isis_dict.get("instance") - - def get_end_state(self): - """get end state info""" - - isis_dict = self.get_isis_dict() - if not isis_dict: - self.end_state["instance"] = None - else: - self.end_state["instance"] = isis_dict.get("instance") - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.isis_dict = self.get_isis_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - xml_str = '' - if self.instance_id: - xml_str += self.config_session() - # update to device - if xml_str: - self.netconf_load_config(xml_str) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - instance_id=dict(required=True, type='int'), - description=dict(required=False, type='str'), - islevel=dict(required=False, type='str', choices=['level_1', 'level_2', 'level_1_2']), - coststyle=dict(required=False, type='str', choices=['narrow', 'wide', 'transition', 'ntransition', 'wtransition']), - relaxspfLimit=dict(required=False, type='bool'), - stdlevel1cost=dict(required=False, type='int'), - stdlevel2cost=dict(required=False, type='int'), - stdbandwidth=dict(required=False, type='int'), - autocostenable=dict(required=False, type='bool'), - autocostenablecompatible=dict(required=False, type='bool'), - netentity=dict(required=False, type='str'), - preference_value=dict(required=False, type='int'), - route_policy_name=dict(required=False, type='str'), - max_load=dict(required=False, type='int'), - ip_address=dict(required=False, type='str'), - weight=dict(required=False, type='int'), - penetration_direct=dict(required=False, type='str', choices=['level2-level1', 'level1-level2']), - aclnum_or_name=dict(required=False, type='str'), - ip_prefix_name=dict(required=False, type='str'), - import_routepolicy_name=dict(required=False, type='str'), - tag=dict(required=False, type='int'), - allow_filter=dict(required=False, type='bool'), - allow_up_down=dict(required=False, type='bool'), - enablelevel1tolevel2=dict(required=False, type='bool'), - defaultmode=dict(required=False, type='str', choices=['always', 'matchDefault', 'matchAny']), - mode_routepolicyname=dict(required=False, type='str'), - cost=dict(required=False, type='int'), - mode_tag=dict(required=False, type='int'), - level_type=dict(required=False, type='str', choices=['level_1', 'level_2', 'level_1_2']), - avoid_learning=dict(required=False, type='bool'), - protocol=dict(required=False, type='str', choices=['direct', 'ospf', 'isis', 'static', 'rip', 'bgp', 'ospfv3', 'all']), - processid=dict(required=False, type='int'), - cost_type=dict(required=False, type='str', choices=['external', 'internal']), - import_cost=dict(required=False, type='int'), - import_tag=dict(required=False, type='int'), - import_route_policy=dict(required=False, type='str'), - impotr_leveltype=dict(required=False, type='str', choices=['level_1', 'level_2', 'level_1_2']), - inheritcost=dict(required=False, type='bool'), - permitibgp=dict(required=False, type='bool'), - export_protocol=dict(required=False, type='str', choices=['direct', 'ospf', 'isis', 'static', 'rip', 'bgp', 'ospfv3', 'all']), - export_policytype=dict(required=False, type='str', choices=['aclNumOrName', 'ipPrefix', 'routePolicy']), - export_processid=dict(required=False, type='int'), - export_aclnumorname=dict(required=False, type='str'), - export_ipprefix=dict(required=False, type='str'), - export_routepolicyname=dict(required=False, type='str'), - import_aclnumorname=dict(required=False, type='str'), - import_ipprefix=dict(required=False, type='str'), - import_routepolicyname=dict(required=False, type='str'), - bfd_min_rx=dict(required=False, type='int'), - bfd_min_tx=dict(required=False, type='int'), - bfd_multiplier_num=dict(required=False, type='int'), - state=dict(required=False, default='present', choices=['present', 'absent']) - ) - - module = ISIS_View(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_lacp.py b/plugins/modules/network/cloudengine/ce_lacp.py deleted file mode 100644 index 769499a8ad..0000000000 --- a/plugins/modules/network/cloudengine/ce_lacp.py +++ /dev/null @@ -1,492 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: ce_lacp -short_description: Manages Eth-Trunk interfaces on HUAWEI CloudEngine switches -description: - - Manages Eth-Trunk specific configuration parameters on HUAWEI CloudEngine switches. -author: xuxiaowei0512 (@CloudEngine-Ansible) -notes: - - C(state=absent) removes the Eth-Trunk config and interface if it already exists. If members to be removed are not explicitly - passed, all existing members (if any), are removed, and Eth-Trunk removed. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - trunk_id: - description: - - Eth-Trunk interface number. - The value is an integer. - The value range depends on the assign forward eth-trunk mode command. - When 256 is specified, the value ranges from 0 to 255. - When 512 is specified, the value ranges from 0 to 511. - When 1024 is specified, the value ranges from 0 to 1023. - type: int - mode: - description: - - Specifies the working mode of an Eth-Trunk interface. - default: null - choices: ['Manual','Dynamic','Static'] - type: str - preempt_enable: - description: - - Specifies lacp preempt enable of Eth-Trunk lacp. - The value is an boolean 'true' or 'false'. - type: bool - state_flapping: - description: - - Lacp dampening state-flapping. - type: bool - port_id_extension_enable: - description: - - Enable the function of extending the LACP negotiation port number. - type: bool - unexpected_mac_disable: - description: - - Lacp dampening unexpected-mac disable. - type: bool - system_id: - description: - - Link Aggregation Control Protocol System ID,interface Eth-Trunk View. - - Formate 'X-X-X',X is hex(a,aa,aaa, or aaaa) - type: str - timeout_type: - description: - - Lacp timeout type,that may be 'Fast' or 'Slow'. - choices: ['Slow', 'Fast'] - type: str - fast_timeout: - description: - - When lacp timeout type is 'Fast', user-defined time can be a number(3~90). - type: int - mixed_rate_link_enable: - description: - - Value of max active linknumber. - type: bool - preempt_delay: - description: - - Value of preemption delay time. - type: int - collector_delay: - description: - - Value of delay time in units of 10 microseconds. - type: int - max_active_linknumber: - description: - - Max active linknumber in link aggregation group. - type: int - select: - description: - - Select priority or speed to preempt. - choices: ['Speed', 'Prority'] - type: str - priority: - description: - - The priority of eth-trunk member interface. - type: int - global_priority: - description: - - Configure lacp priority on system-view. - type: int - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] - type: str -''' -EXAMPLES = r''' - - name: Ensure Eth-Trunk100 is created, and set to mode lacp-static - ce_lacp: - trunk_id: 100 - mode: 'lacp-static' - state: present - - name: Ensure Eth-Trunk100 is created, add two members, and set global priority to 1231 - ce_lacp: - trunk_id: 100 - global_priority: 1231 - state: present - - name: Ensure Eth-Trunk100 is created, and set mode to Dynamic and configure other options - ce_lacp: - trunk_id: 100 - mode: Dynamic - preempt_enable: True, - state_flapping: True, - port_id_extension_enable: True, - unexpected_mac_disable: True, - timeout_type: Fast, - fast_timeout: 123, - mixed_rate_link_enable: True, - preempt_delay: 23, - collector_delay: 33, - state: present -''' - -RETURN = r''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"trunk_id": "100", "members": ['10GE1/0/24','10GE1/0/25'], "mode": "lacp-static"} -existing: - description: k/v pairs of existing Eth-Trunk - returned: always - type: dict - sample: {"trunk_id": "100", "hash_type": "mac", "members_detail": [ - {"memberIfName": "10GE1/0/25", "memberIfState": "Down"}], - "min_links": "1", "mode": "manual"} -end_state: - description: k/v pairs of Eth-Trunk info after module execution - returned: always - type: dict - sample: {"trunk_id": "100", "hash_type": "mac", "members_detail": [ - {"memberIfName": "10GE1/0/24", "memberIfState": "Down"}, - {"memberIfName": "10GE1/0/25", "memberIfState": "Down"}], - "min_links": "1", "mode": "lacp-static"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["interface Eth-Trunk 100", - "mode lacp-static", - "interface 10GE1/0/25", - "eth-trunk 100"] -''' - -import xml.etree.ElementTree as ET -import re -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config - -LACP = {'trunk_id': 'ifName', - 'mode': 'workMode', - 'preempt_enable': 'isSupportPrmpt', - 'state_flapping': 'dampStaFlapEn', - 'port_id_extension_enable': 'trunkPortIdExt', - 'unexpected_mac_disable': 'dampUnexpMacEn', - 'system_id': 'trunkSysMac', - 'timeout_type': 'rcvTimeoutType', - 'fast_timeout': 'fastTimeoutUserDefinedValue', - 'mixed_rate_link_enable': 'mixRateEnable', - 'preempt_delay': 'promptDelay', - 'collector_delay': 'collectMaxDelay', - 'max_active_linknumber': 'maxActiveNum', - 'select': 'selectPortStd', - 'weight': 'weight', - 'priority': 'portPriority', - 'global_priority': 'priority' - } - - -def has_element(parent, xpath): - """get or create a element by xpath""" - ele = parent.find('./' + xpath) - if ele is not None: - return ele - ele = parent - lpath = xpath.split('/') - for p in lpath: - e = parent.find('.//' + p) - if e is None: - e = ET.SubElement(ele, p) - ele = e - return ele - - -def bulid_xml(kwargs, operation='get'): - """create a xml tree by dictionary with operation,get,merge and delete""" - attrib = {'xmlns': "http://www.huawei.com/netconf/vrp", - 'content-version': "1.0", 'format-version': "1.0"} - - root = ET.Element('ifmtrunk') - for key in kwargs.keys(): - if key in ('global_priority',): - xpath = 'lacpSysInfo' - elif key in ('priority',): - xpath = 'TrunkIfs/TrunkIf/TrunkMemberIfs/TrunkMemberIf/lacpPortInfo/lacpPort' - elif key in ['preempt_enable', 'timeout_type', 'fast_timeout', 'select', 'preempt_delay', - 'max_active_linknumber', 'collector_delay', 'mixed_rate_link_enable', - 'state_flapping', 'unexpected_mac_disable', 'system_id', - 'port_id_extension_enable']: - xpath = 'TrunkIfs/TrunkIf/lacpTrunk' - elif key in ('trunk_id', 'mode'): - xpath = 'TrunkIfs/TrunkIf' - if xpath != '': - parent = has_element(root, xpath) - element = ET.SubElement(parent, LACP[key]) - if operation == 'merge': - parent.attrib = dict(operation=operation) - element.text = str(kwargs[key]) - if key == 'mode': - element.text = str(kwargs[key]) - if key == 'trunk_id': - element.text = 'Eth-Trunk' + str(kwargs[key]) - root.attrib = attrib - config = ET.tostring(root) - if operation == 'merge' or operation == 'delete': - return '%s' % to_native(config) - return '%s' % to_native(config) - - -def check_param(kwargs): - """check args list,the boolean or list values cloud not be checked,because they are limit by args list in main""" - - for key in kwargs: - if kwargs[key] is None: - continue - if key == 'trunk_id': - value = int(kwargs[key]) - # maximal value is 1024,although the value is limit by command 'assign forward eth-trunk mode ' - if value < 0 or value > 1024: - return 'Error: Wrong Value of Eth-Trunk interface number' - elif key == 'system_id': - # X-X-X ,X is hex(4 bit) - if not re.match(r'[0-9a-f]{1,4}\-[0-9a-f]{1,4}\-[0-9a-f]{1,4}', kwargs[key], re.IGNORECASE): - return 'Error: The system-id is invalid.' - values = kwargs[key].split('-') - flag = 0 - # all 'X' is 0,that is invalid value - for v in values: - if len(v.strip('0')) < 1: - flag += 1 - if flag == 3: - return 'Error: The system-id is invalid.' - elif key == 'timeout_type': - # select a value from choices, choices=['Slow','Fast'],it's checked by AnsibleModule - pass - elif key == 'fast_timeout': - value = int(kwargs[key]) - if value < 3 or value > 90: - return 'Error: Wrong Value of timeout,fast user-defined value<3-90>' - rtype = str(kwargs.get('timeout_type')) - if rtype == 'Slow': - return 'Error: Short timeout period for receiving packets is need,when user define the time.' - elif key == 'preempt_delay': - value = int(kwargs[key]) - if value < 0 or value > 180: - return 'Error: Value of preemption delay time is from 0 to 180' - elif key == 'collector_delay': - value = int(kwargs[key]) - if value < 0 or value > 65535: - return 'Error: Value of collector delay time is from 0 to 65535' - elif key == 'max_active_linknumber': - value = int(kwargs[key]) - if value < 0 or value > 64: - return 'Error: Value of collector delay time is from 0 to 64' - elif key == 'priority' or key == 'global_priority': - value = int(kwargs[key]) - if value < 0 or value > 65535: - return 'Error: Value of priority is from 0 to 65535' - return 'ok' - - -def xml_to_dict(args): - """transfer xml string into dict """ - rdict = dict() - args = re.sub(r'xmlns=\".+?\"', '', args) - root = ET.fromstring(args) - ifmtrunk = root.find('.//ifmtrunk') - if ifmtrunk is not None: - try: - ifmtrunk_iter = ET.Element.iter(ifmtrunk) - except AttributeError: - ifmtrunk_iter = ifmtrunk.getiterator() - - for ele in ifmtrunk_iter: - if ele.text is not None and len(ele.text.strip()) > 0: - rdict[ele.tag] = ele.text - return rdict - - -def compare_config(module, kwarg_exist, kwarg_end): - """compare config between exist and end""" - dic_command = {'isSupportPrmpt': 'lacp preempt enable', - 'rcvTimeoutType': 'lacp timeout', # lacp timeout fast user-defined 23 - 'fastTimeoutUserDefinedValue': 'lacp timeout user-defined', - 'selectPortStd': 'lacp select', - 'promptDelay': 'lacp preempt delay', - 'maxActiveNum': 'lacp max active-linknumber', - 'collectMaxDelay': 'lacp collector delay', - 'mixRateEnable': 'lacp mixed-rate link enable', - 'dampStaFlapEn': 'lacp dampening state-flapping', - 'dampUnexpMacEn': 'lacp dampening unexpected-mac disable', - 'trunkSysMac': 'lacp system-id', - 'trunkPortIdExt': 'lacp port-id-extension enable', - 'portPriority': 'lacp priority', # interface 10GE1/0/1 - 'lacpMlagPriority': 'lacp m-lag priority', - 'lacpMlagSysId': 'lacp m-lag system-id', - 'priority': 'lacp priority' - } - rlist = list() - exist = set(kwarg_exist.keys()) - end = set(kwarg_end.keys()) - undo = exist - end - add = end - exist - update = end & exist - - for key in undo: - if key in dic_command: - rlist.append('undo ' + dic_command[key]) - for key in add: - if key in dic_command: - rlist.append(dic_command[key] + ' ' + kwarg_end[key]) - for key in update: - if kwarg_exist[key] != kwarg_end[key] and key in dic_command: - if kwarg_exist[key] == 'true' and kwarg_end[key] == 'false': - rlist.append('undo ' + dic_command[key]) - elif kwarg_exist[key] == 'false' and kwarg_end[key] == 'true': - rlist.append(dic_command[key]) - else: - rlist.append(dic_command[key] + ' ' + kwarg_end[key].lower()) - return rlist - - -class Lacp(object): - """ - Manages Eth-Trunk interfaces LACP. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.trunk_id = self.module.params['trunk_id'] - self.mode = self.module.params['mode'] - self.param = dict() - - self.state = self.module.params['state'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """ init AnsibleModule """ - - self.module = AnsibleModule( - argument_spec=self.spec, - mutually_exclusive=[['trunk_id', 'global_priority']], - required_one_of=[['trunk_id', 'global_priority']], - supports_check_mode=True) - - def check_params(self): - """check module params """ - for key in self.module.params.keys(): - if key in LACP.keys() and self.module.params[key] is not None: - self.param[key] = self.module.params[key] - if isinstance(self.module.params[key], bool): - self.param[key] = str(self.module.params[key]).lower() - msg = check_param(self.param) - if msg != 'ok': - self.module.fail_json(msg=msg) - - def get_existing(self): - """get existing""" - xml_str = bulid_xml(self.param) - xml = get_nc_config(self.module, xml_str) - return xml_to_dict(xml) - - def get_proposed(self): - """get proposed""" - proposed = dict(state=self.state) - proposed.update(self.param) - return proposed - - def get_end_state(self): - """ get end_state""" - xml_str = bulid_xml(self.param) - xml = get_nc_config(self.module, xml_str) - return xml_to_dict(xml) - - def work(self): - """worker""" - - self.check_params() - existing = self.get_existing() - proposed = self.get_proposed() - - # deal present or absent - if self.state == "present": - operation = 'merge' - else: - operation = 'delete' - - xml_str = bulid_xml(self.param, operation=operation) - set_nc_config(self.module, xml_str) - end_state = self.get_end_state() - - self.results['proposed'] = proposed - self.results['existing'] = existing - self.results['end_state'] = end_state - updates_cmd = compare_config(self.module, existing, end_state) - self.results['updates'] = updates_cmd - if updates_cmd: - self.results['changed'] = True - else: - self.results['changed'] = False - - self.module.exit_json(**self.results) - - -def main(): - - argument_spec = dict( - mode=dict(required=False, - choices=['Manual', 'Dynamic', 'Static'], - type='str'), - trunk_id=dict(required=False, type='int'), - preempt_enable=dict(required=False, type='bool'), - state_flapping=dict(required=False, type='bool'), - port_id_extension_enable=dict(required=False, type='bool'), - unexpected_mac_disable=dict(required=False, type='bool'), - system_id=dict(required=False, type='str'), - timeout_type=dict(required=False, type='str', choices=['Slow', 'Fast']), - fast_timeout=dict(required=False, type='int'), - mixed_rate_link_enable=dict(required=False, type='bool'), - preempt_delay=dict(required=False, type='int'), - collector_delay=dict(required=False, type='int'), - max_active_linknumber=dict(required=False, type='int'), - select=dict(required=False, type='str', choices=['Speed', 'Prority']), - priority=dict(required=False, type='int'), - global_priority=dict(required=False, type='int'), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - - module = Lacp(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_link_status.py b/plugins/modules/network/cloudengine/ce_link_status.py deleted file mode 100644 index c1c58e8a93..0000000000 --- a/plugins/modules/network/cloudengine/ce_link_status.py +++ /dev/null @@ -1,568 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- - -module: ce_link_status -short_description: Get interface link status on HUAWEI CloudEngine switches. -description: - - Get interface link status on HUAWEI CloudEngine switches. -author: - - Zhijin Zhou (@QijunPan) -notes: - - Current physical state shows an interface's physical status. - - Current link state shows an interface's link layer protocol status. - - Current IPv4 state shows an interface's IPv4 protocol status. - - Current IPv6 state shows an interface's IPv6 protocol status. - - Inbound octets(bytes) shows the number of bytes that an interface received. - - Inbound unicast(pkts) shows the number of unicast packets that an interface received. - - Inbound multicast(pkts) shows the number of multicast packets that an interface received. - - Inbound broadcast(pkts) shows the number of broadcast packets that an interface received. - - Inbound error(pkts) shows the number of error packets that an interface received. - - Inbound drop(pkts) shows the total number of packets that were sent to the interface but dropped by an interface. - - Inbound rate(byte/sec) shows the rate at which an interface receives bytes within an interval. - - Inbound rate(pkts/sec) shows the rate at which an interface receives packets within an interval. - - Outbound octets(bytes) shows the number of the bytes that an interface sent. - - Outbound unicast(pkts) shows the number of unicast packets that an interface sent. - - Outbound multicast(pkts) shows the number of multicast packets that an interface sent. - - Outbound broadcast(pkts) shows the number of broadcast packets that an interface sent. - - Outbound error(pkts) shows the total number of packets that an interface sent but dropped by the remote interface. - - Outbound drop(pkts) shows the number of dropped packets that an interface sent. - - Outbound rate(byte/sec) shows the rate at which an interface sends bytes within an interval. - - Outbound rate(pkts/sec) shows the rate at which an interface sends packets within an interval. - - Speed shows the rate for an Ethernet interface. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - interface: - description: - - For the interface parameter, you can enter C(all) to display information about all interfaces, - an interface type such as C(40GE) to display information about interfaces of the specified type, - or full name of an interface such as C(40GE1/0/22) or C(vlanif10) - to display information about the specific interface. - required: true -''' - -EXAMPLES = ''' - -- name: Link status test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Get specified interface link status information - ce_link_status: - interface: 40GE1/0/1 - provider: "{{ cli }}" - - - name: Get specified interface type link status information - ce_link_status: - interface: 40GE - provider: "{{ cli }}" - - - name: Get all interfaces link status information - ce_link_status: - interface: all - provider: "{{ cli }}" -''' - -RETURN = ''' -result: - description: Interface link status information - returned: always - type: dict - sample: { - "40ge2/0/8": { - "Current IPv4 state": "down", - "Current IPv6 state": "down", - "Current link state": "up", - "Current physical state": "up", - "Inbound broadcast(pkts)": "0", - "Inbound drop(pkts)": "0", - "Inbound error(pkts)": "0", - "Inbound multicast(pkts)": "20151", - "Inbound octets(bytes)": "7314813", - "Inbound rate(byte/sec)": "11", - "Inbound rate(pkts/sec)": "0", - "Inbound unicast(pkts)": "0", - "Outbound broadcast(pkts)": "1", - "Outbound drop(pkts)": "0", - "Outbound error(pkts)": "0", - "Outbound multicast(pkts)": "20152", - "Outbound octets(bytes)": "7235021", - "Outbound rate(byte/sec)": "11", - "Outbound rate(pkts/sec)": "0", - "Outbound unicast(pkts)": "0", - "Speed": "40GE" - } - } -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, get_nc_config, get_nc_next - -CE_NC_GET_PORT_SPEED = """ - - - - - %s - - - - - - - -""" - -CE_NC_GET_INT_STATISTICS = """ - - - - - %s - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" - -INTERFACE_ALL = 1 -INTERFACE_TYPE = 2 -INTERFACE_FULL_NAME = 3 - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - if interface.upper().startswith('GE'): - return 'ge' - elif interface.upper().startswith('10GE'): - return '10ge' - elif interface.upper().startswith('25GE'): - return '25ge' - elif interface.upper().startswith('4X10GE'): - return '4x10ge' - elif interface.upper().startswith('40GE'): - return '40ge' - elif interface.upper().startswith('100GE'): - return '100ge' - elif interface.upper().startswith('VLANIF'): - return 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - return 'loopback' - elif interface.upper().startswith('METH'): - return 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - return 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - return 'vbdif' - elif interface.upper().startswith('NVE'): - return 'nve' - elif interface.upper().startswith('TUNNEL'): - return 'tunnel' - elif interface.upper().startswith('ETHERNET'): - return 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - return 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - return 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - return 'stack-Port' - elif interface.upper().startswith('NULL'): - return 'null' - else: - return None - - -def is_ethernet_port(interface): - """Judge whether it is ethernet port""" - - ethernet_port = ['ge', '10ge', '25ge', '4x10ge', '40ge', '100ge', 'meth'] - if_type = get_interface_type(interface) - if if_type in ethernet_port: - return True - return False - - -class LinkStatus(object): - """Get interface link status information""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # interface name - self.interface = self.module.params['interface'] - self.interface = self.interface.replace(' ', '').lower() - self.param_type = None - self.if_type = None - - # state - self.results = dict() - self.result = dict() - - def check_params(self): - """Check all input params""" - - if not self.interface: - self.module.fail_json(msg='Error: Interface name cannot be empty.') - - if self.interface and self.interface != 'all': - if not self.if_type: - self.module.fail_json( - msg='Error: Interface name of %s is error.' % self.interface) - - def init_module(self): - """Init module object""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def show_result(self): - """Show result""" - - self.results['result'] = self.result - - self.module.exit_json(**self.results) - - def get_intf_dynamic_info(self, dyn_info, intf_name): - """Get interface dynamic information""" - - if not intf_name: - return - - if dyn_info: - for eles in dyn_info: - if eles.tag in ["ifPhyStatus", "ifV4State", "ifV6State", "ifLinkStatus"]: - if eles.tag == "ifPhyStatus": - self.result[intf_name][ - 'Current physical state'] = eles.text - elif eles.tag == "ifLinkStatus": - self.result[intf_name][ - 'Current link state'] = eles.text - elif eles.tag == "ifV4State": - self.result[intf_name][ - 'Current IPv4 state'] = eles.text - elif eles.tag == "ifV6State": - self.result[intf_name][ - 'Current IPv6 state'] = eles.text - - def get_intf_statistics_info(self, stat_info, intf_name): - """Get interface statistics information""" - - if not intf_name: - return - - if_type = get_interface_type(intf_name) - if if_type == 'fcoe-port' or if_type == 'nve' or if_type == 'tunnel' or \ - if_type == 'vbdif' or if_type == 'vlanif': - return - - if stat_info: - for eles in stat_info: - if eles.tag in ["receiveByte", "sendByte", "rcvUniPacket", "rcvMutiPacket", "rcvBroadPacket", - "sendUniPacket", "sendMutiPacket", "sendBroadPacket", "rcvErrorPacket", - "rcvDropPacket", "sendErrorPacket", "sendDropPacket"]: - if eles.tag == "receiveByte": - self.result[intf_name][ - 'Inbound octets(bytes)'] = eles.text - elif eles.tag == "rcvUniPacket": - self.result[intf_name][ - 'Inbound unicast(pkts)'] = eles.text - elif eles.tag == "rcvMutiPacket": - self.result[intf_name][ - 'Inbound multicast(pkts)'] = eles.text - elif eles.tag == "rcvBroadPacket": - self.result[intf_name][ - 'Inbound broadcast(pkts)'] = eles.text - elif eles.tag == "rcvErrorPacket": - self.result[intf_name][ - 'Inbound error(pkts)'] = eles.text - elif eles.tag == "rcvDropPacket": - self.result[intf_name][ - 'Inbound drop(pkts)'] = eles.text - elif eles.tag == "sendByte": - self.result[intf_name][ - 'Outbound octets(bytes)'] = eles.text - elif eles.tag == "sendUniPacket": - self.result[intf_name][ - 'Outbound unicast(pkts)'] = eles.text - elif eles.tag == "sendMutiPacket": - self.result[intf_name][ - 'Outbound multicast(pkts)'] = eles.text - elif eles.tag == "sendBroadPacket": - self.result[intf_name][ - 'Outbound broadcast(pkts)'] = eles.text - elif eles.tag == "sendErrorPacket": - self.result[intf_name][ - 'Outbound error(pkts)'] = eles.text - elif eles.tag == "sendDropPacket": - self.result[intf_name][ - 'Outbound drop(pkts)'] = eles.text - - def get_intf_cleared_stat(self, clr_stat, intf_name): - """Get interface cleared state information""" - - if not intf_name: - return - - if_type = get_interface_type(intf_name) - if if_type == 'fcoe-port' or if_type == 'nve' or if_type == 'tunnel' or \ - if_type == 'vbdif' or if_type == 'vlanif': - return - - if clr_stat: - for eles in clr_stat: - if eles.tag in ["inByteRate", "inPacketRate", "outByteRate", "outPacketRate"]: - if eles.tag == "inByteRate": - self.result[intf_name][ - 'Inbound rate(byte/sec)'] = eles.text - elif eles.tag == "inPacketRate": - self.result[intf_name][ - 'Inbound rate(pkts/sec)'] = eles.text - elif eles.tag == "outByteRate": - self.result[intf_name][ - 'Outbound rate(byte/sec)'] = eles.text - elif eles.tag == "outPacketRate": - self.result[intf_name][ - 'Outbound rate(pkts/sec)'] = eles.text - - def get_all_interface_info(self, intf_type=None): - """Get interface information by all or by interface type""" - - xml_str = CE_NC_GET_INT_STATISTICS % '' - con_obj = get_nc_next(self.module, xml_str) - if "" in con_obj: - return - - xml_str = con_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get link status information - root = ElementTree.fromstring(xml_str) - intfs_info = root.findall("ifm/interfaces/interface") - if not intfs_info: - return - - intf_name = '' - flag = False - for eles in intfs_info: - if eles.tag == "interface": - for ele in eles: - if ele.tag in ["ifName", "ifDynamicInfo", "ifStatistics", "ifClearedStat"]: - if ele.tag == "ifName": - intf_name = ele.text.lower() - if intf_type: - if get_interface_type(intf_name) != intf_type.lower(): - break - else: - flag = True - self.init_interface_data(intf_name) - if is_ethernet_port(intf_name): - self.get_port_info(intf_name) - if ele.tag == "ifDynamicInfo": - self.get_intf_dynamic_info(ele, intf_name) - elif ele.tag == "ifStatistics": - self.get_intf_statistics_info(ele, intf_name) - elif ele.tag == "ifClearedStat": - self.get_intf_cleared_stat(ele, intf_name) - if intf_type and not flag: - self.module.fail_json( - msg='Error: %s interface type does not exist.' % intf_type.upper()) - - def get_interface_info(self): - """Get interface information""" - - xml_str = CE_NC_GET_INT_STATISTICS % self.interface.upper() - con_obj = get_nc_config(self.module, xml_str) - if "" in con_obj: - self.module.fail_json( - msg='Error: %s interface does not exist.' % self.interface.upper()) - return - - xml_str = con_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get link status information - root = ElementTree.fromstring(xml_str) - intf_info = root.find("ifm/interfaces/interface") - if intf_info: - for eles in intf_info: - if eles.tag in ["ifDynamicInfo", "ifStatistics", "ifClearedStat"]: - if eles.tag == "ifDynamicInfo": - self.get_intf_dynamic_info(eles, self.interface) - elif eles.tag == "ifStatistics": - self.get_intf_statistics_info(eles, self.interface) - elif eles.tag == "ifClearedStat": - self.get_intf_cleared_stat(eles, self.interface) - - def init_interface_data(self, intf_name): - """Init interface data""" - - # init link status data - self.result[intf_name] = dict() - self.result[intf_name]['Current physical state'] = 'down' - self.result[intf_name]['Current link state'] = 'down' - self.result[intf_name]['Current IPv4 state'] = 'down' - self.result[intf_name]['Current IPv6 state'] = 'down' - self.result[intf_name]['Inbound octets(bytes)'] = '--' - self.result[intf_name]['Inbound unicast(pkts)'] = '--' - self.result[intf_name]['Inbound multicast(pkts)'] = '--' - self.result[intf_name]['Inbound broadcast(pkts)'] = '--' - self.result[intf_name]['Inbound error(pkts)'] = '--' - self.result[intf_name]['Inbound drop(pkts)'] = '--' - self.result[intf_name]['Inbound rate(byte/sec)'] = '--' - self.result[intf_name]['Inbound rate(pkts/sec)'] = '--' - self.result[intf_name]['Outbound octets(bytes)'] = '--' - self.result[intf_name]['Outbound unicast(pkts)'] = '--' - self.result[intf_name]['Outbound multicast(pkts)'] = '--' - self.result[intf_name]['Outbound broadcast(pkts)'] = '--' - self.result[intf_name]['Outbound error(pkts)'] = '--' - self.result[intf_name]['Outbound drop(pkts)'] = '--' - self.result[intf_name]['Outbound rate(byte/sec)'] = '--' - self.result[intf_name]['Outbound rate(pkts/sec)'] = '--' - self.result[intf_name]['Speed'] = '--' - - def get_port_info(self, interface): - """Get port information""" - - if_type = get_interface_type(interface) - if if_type == 'meth': - xml_str = CE_NC_GET_PORT_SPEED % interface.lower().replace('meth', 'MEth') - else: - xml_str = CE_NC_GET_PORT_SPEED % interface.upper() - con_obj = get_nc_config(self.module, xml_str) - if "" in con_obj: - return - - xml_str = con_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get link status information - root = ElementTree.fromstring(xml_str) - port_info = root.find("devm/ports/port") - if port_info: - for eles in port_info: - if eles.tag == "ethernetPort": - for ele in eles: - if ele.tag == 'speed': - self.result[interface]['Speed'] = ele.text - - def get_link_status(self): - """Get link status information""" - - if self.param_type == INTERFACE_FULL_NAME: - self.init_interface_data(self.interface) - self.get_interface_info() - if is_ethernet_port(self.interface): - self.get_port_info(self.interface) - elif self.param_type == INTERFACE_TYPE: - self.get_all_interface_info(self.interface) - else: - self.get_all_interface_info() - - def get_intf_param_type(self): - """Get the type of input interface parameter""" - - if self.interface == 'all': - self.param_type = INTERFACE_ALL - return - - if self.if_type == self.interface: - self.param_type = INTERFACE_TYPE - return - - self.param_type = INTERFACE_FULL_NAME - - def work(self): - """Worker""" - - self.if_type = get_interface_type(self.interface) - self.check_params() - self.get_intf_param_type() - self.get_link_status() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - interface=dict(required=True, type='str'), - ) - argument_spec.update(ce_argument_spec) - linkstatus_obj = LinkStatus(argument_spec) - linkstatus_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_lldp.py b/plugins/modules/network/cloudengine/ce_lldp.py deleted file mode 100644 index 3502ed3ee6..0000000000 --- a/plugins/modules/network/cloudengine/ce_lldp.py +++ /dev/null @@ -1,791 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# 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: ce_lldp -short_description: Manages LLDP configuration on HUAWEI CloudEngine switches. -description: - - Manages LLDP configuration on HUAWEI CloudEngine switches. -author: - - xuxiaowei0512 (@CloudEngine-Ansible) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - lldpenable: - description: - - Set global LLDP enable state. - required: false - choices: ['enabled', 'disabled'] - type: str - mdnstatus: - description: - - Set global MDN enable state. - required: false - choices: ['rxOnly', 'disabled'] - type: str - interval: - description: - - Frequency at which LLDP advertisements are sent (in seconds). - required: false - type: int - hold_multiplier: - description: - - Time multiplier for device information in neighbor devices. - required: false - type: int - restart_delay: - description: - - Specifies the delay time of the interface LLDP module from disabled state to re enable. - required: false - type: int - transmit_delay: - description: - - Delay time for sending LLDP messages. - required: false - type: int - notification_interval: - description: - - Suppression time for sending LLDP alarm. - required: false - type: int - fast_count: - description: - - The number of LLDP messages sent to the neighbor nodes by the specified device. - required: false - type: int - mdn_notification_interval: - description: - - Delay time for sending MDN neighbor information change alarm. - required: false - type: int - management_address: - description: - - The management IP address of LLDP. - required: false - default: null - type: str - bind_name: - description: - - Binding interface name. - required: false - default: null - type: str - state: - description: - - Manage the state of the resource. - required: false - default: present - type: str - choices: ['present','absent'] -''' - -EXAMPLES = ''' - - name: "Configure global LLDP enable state" - ce_lldp: - lldpenable: enabled - - - name: "Configure global MDN enable state" - ce_lldp: - mdnstatus: rxOnly - - - name: "Configure LLDP transmit interval and ensure global LLDP state is already enabled" - ce_lldp: - enable: enable - interval: 32 - - - name: "Configure LLDP transmit multiplier hold and ensure global LLDP state is already enabled" - ce_lldp: - enable: enable - hold_multiplier: 5 - - - name: "Configure the delay time of the interface LLDP module from disabled state to re enable" - ce_lldp: - enable: enable - restart_delay: 3 - - - name: "Reset the delay time for sending LLDP messages" - ce_lldp: - enable: enable - transmit_delay: 4 - - - name: "Configure device to send neighbor device information change alarm delay time" - ce_lldp: - lldpenable: enabled - notification_interval: 6 - - - name: "Configure the number of LLDP messages sent to the neighbor nodes by the specified device" - ce_lldp: - enable: enable - fast_count: 5 - - - name: "Configure the delay time for sending MDN neighbor information change alarm" - ce_lldp: - enable: enable - mdn_notification_interval: 6 - - name: "Configuring the management IP address of LLDP" - ce_lldp: - enable: enable - management_address: 10.1.0.1 - - - name: "Configuring LLDP to manage the binding relationship between IP addresses and interfaces" - ce_lldp: - enable: enable - bind_name: LoopBack2 -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "lldpenable": "enabled", - "mdnstatus": "rxOnly", - "interval": "32", - "hold_multiplier": "5", - "restart_delay": "3", - "transmit_delay": "4", - "notification_interval": "6", - "fast_count": "5", - "mdn_notification_interval": "6", - "management_address": "10.1.0.1", - "bind_name": "LoopBack2", - "state": "present" - } -existing: - description: k/v pairs of existing global LLDP configuration. - returned: always - type: dict - sample: { - "lldpenable": "disabled", - "mdnstatus": "disabled" - } -end_state: - description: k/v pairs of global LLDP configuration after module execution. - returned: always - type: dict - sample: { - "lldpenable": "enabled", - "mdnstatus": "rxOnly", - "interval": "32", - "hold_multiplier": "5", - "restart_delay": "3", - "transmit_delay": "4", - "notification_interval": "6", - "fast_count": "5", - "mdn_notification_interval": "6", - "management_address": "10.1.0.1", - "bind_name": "LoopBack2" - } -updates: - description: command sent to the device - returned: always - type: list - sample: [ - "lldp enable", - "lldp mdn enable", - "lldp transmit interval 32", - "lldp transmit multiplier 5", - "lldp restart 3", - "lldp transmit delay 4", - "lldp trap-interval 6", - "lldp fast-count 5", - "lldp mdn trap-interval 6", - "lldp management-address 10.1.0.1", - "lldp management-address bind interface LoopBack 2" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import copy -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import set_nc_config, get_nc_config - -CE_NC_GET_GLOBAL_LLDPENABLE_CONFIG = """ - - - - - - - - -""" - -CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG = """ - - - - %s - - - -""" - -CE_NC_MERGE_GLOBA_MDNENABLE_CONFIG = """ - - - - %s - - - -""" - -CE_NC_GET_GLOBAL_LLDP_CONFIG = """ - - - - - - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER = """ - - - - -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_INTERVAL = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HOLD_MULTIPLIER = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_RESTART_DELAY = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TRANSMIT_DELAY = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_NOTIFICATION_INTERVAL = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_FAST_COUNT = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_MDN_NOTIFICATION_INTERVAL = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_MANAGEMENT_ADDRESS = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_BIND_NAME = """ - %s -""" - -CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL = """ - - - - -""" - - -class Lldp(object): - """Manage global lldp enable configuration""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - self.lldpenable = self.module.params['lldpenable'] or None - self.interval = self.module.params['interval'] or None - self.mdnstatus = self.module.params['mdnstatus'] or None - self.hold_multiplier = self.module.params['hold_multiplier'] or None - self.restart_delay = self.module.params['restart_delay'] or None - self.transmit_delay = self.module.params['transmit_delay'] or None - self.notification_interval = self.module.params['notification_interval'] or None - self.fast_count = self.module.params['fast_count'] or None - self.mdn_notification_interval = self.module.params['mdn_notification_interval'] or None - self.management_address = self.module.params['management_address'] - self.bind_name = self.module.params['bind_name'] - self.state = self.module.params['state'] - self.lldp_conf = dict() - self.conf_exsit = False - self.conf_exsit_lldp = False - self.enable_flag = 0 - self.check_params() - self.existing_state_value = dict() - self.existing_end_state_value = dict() - self.changed = False - self.proposed_changed = dict() - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def is_valid_v4addr(self): - """check if ipv4 addr is valid""" - if self.management_address.find('.') != -1: - addr_list = self.management_address.split('.') - if self.management_address == "0.0.0.0": - self.module.fail_json(msg='Error: The management address is 0.0.0.0 .') - if len(addr_list) != 4: - self.module.fail_json(msg='Error: Invalid IPV4 address.') - for each_num in addr_list: - each_num_tmp = str(each_num) - if not each_num_tmp.isdigit(): - self.module.fail_json(msg='Error: The ip address is not digit.') - if (int(each_num) > 255) or (int(each_num) < 0): - self.module.fail_json( - msg='Error: The value of ip address is out of [0 - 255].') - else: - self.module.fail_json(msg='Error: Invalid IP address.') - - def check_params(self): - """Check all input params""" - - if self.interval: - if int(self.interval) < 5 or int(self.interval) > 32768: - self.module.fail_json( - msg='Error: The value of interval is out of [5 - 32768].') - - if self.hold_multiplier: - if int(self.hold_multiplier) < 2 or int(self.hold_multiplier) > 10: - self.module.fail_json( - msg='Error: The value of hold_multiplier is out of [2 - 10].') - - if self.restart_delay: - if int(self.restart_delay) < 1 or int(self.restart_delay) > 10: - self.module.fail_json( - msg='Error: The value of restart_delay is out of [1 - 10].') - - if self.transmit_delay: - if int(self.transmit_delay) < 1 or int(self.transmit_delay) > 8192: - self.module.fail_json( - msg='Error: The value of transmit_delay is out of [1 - 8192].') - - if self.notification_interval: - if int(self.notification_interval) < 5 or int(self.notification_interval) > 3600: - self.module.fail_json( - msg='Error: The value of notification_interval is out of [5 - 3600].') - - if self.fast_count: - if int(self.fast_count) < 1 or int(self.fast_count) > 8: - self.module.fail_json( - msg='Error: The value of fast_count is out of [1 - 8].') - - if self.mdn_notification_interval: - if int(self.mdn_notification_interval) < 5 or int(self.mdn_notification_interval) > 3600: - self.module.fail_json( - msg='Error: The value of mdn_notification_interval is out of [5 - 3600].') - - if self.management_address: - self.is_valid_v4addr() - - if self.bind_name: - if (len(self.bind_name) < 1) or (len(self.bind_name) > 63): - self.module.fail_json( - msg='Error: Bind_name length is between 1 and 63.') - - def init_module(self): - """Init module object""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def config_lldp(self): - """Configure lldp enabled and mdn enabled parameters""" - - if self.state == 'present': - if (self.enable_flag == 1 and self.lldpenable == 'enabled') and not self.conf_exsit: - if self.mdnstatus: - xml_str = CE_NC_MERGE_GLOBA_MDNENABLE_CONFIG % self.mdnstatus - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "MDN_ENABLE_CONFIG") - - if self.lldpenable == 'enabled' and not self.conf_exsit: - xml_str = CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG % self.lldpenable - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_ENABLE_CONFIG") - - if self.mdnstatus: - xml_str = CE_NC_MERGE_GLOBA_MDNENABLE_CONFIG % self.mdnstatus - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "MDN_ENABLE_CONFIG") - - if (self.enable_flag == 1) and not self.conf_exsit: - if self.mdnstatus: - xml_str = CE_NC_MERGE_GLOBA_MDNENABLE_CONFIG % self.mdnstatus - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "MDN_ENABLE_CONFIG") - - if (self.lldpenable == 'enabled' or self.enable_flag == 1) and not self.conf_exsit_lldp: - if self.hold_multiplier: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HOLD_MULTIPLIER % self.hold_multiplier) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.interval: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_INTERVAL % self.interval) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.restart_delay: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_RESTART_DELAY % self.restart_delay) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.transmit_delay: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TRANSMIT_DELAY % self.transmit_delay) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.notification_interval: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_NOTIFICATION_INTERVAL % self.notification_interval) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.fast_count: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_FAST_COUNT % self.fast_count) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.mdn_notification_interval: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_MDN_NOTIFICATION_INTERVAL % self.mdn_notification_interval) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.management_address: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_MANAGEMENT_ADDRESS % self.management_address) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.bind_name: - xml_str = CE_NC_MERGE_GLOBAL_LLDP_CONFIG_HEADER + \ - (CE_NC_MERGE_GLOBAL_LLDP_CONFIG_BIND_NAME % self.bind_name) + \ - CE_NC_MERGE_GLOBAL_LLDP_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_CONFIG_INTERVAL") - - if self.lldpenable == 'disabled' and not self.conf_exsit: - xml_str = CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG % self.lldpenable - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_DISABLE_CONFIG") - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def get_lldp_exist_config(self): - """Get lldp existed configure""" - - lldp_config = list() - lldp_dict = dict() - - conf_enable_str = CE_NC_GET_GLOBAL_LLDPENABLE_CONFIG - conf_enable_obj = get_nc_config(self.module, conf_enable_str) - - xml_enable_str = conf_enable_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get lldp enable config info - root_enable = ElementTree.fromstring(xml_enable_str) - ntpsite_enable = root_enable.findall("lldp/lldpSys") - for nexthop_enable in ntpsite_enable: - for ele_enable in nexthop_enable: - if ele_enable.tag in ["lldpEnable", "mdnStatus"]: - lldp_dict[ele_enable.tag] = ele_enable.text - - if self.state == "present": - cur_lldp_cfg = dict(lldpenable=lldp_dict['lldpEnable'], mdnstatus=lldp_dict['mdnStatus']) - exp_lldp_cfg = dict(lldpenable=self.lldpenable, mdnstatus=self.mdnstatus) - if lldp_dict['lldpEnable'] == 'enabled': - self.enable_flag = 1 - if cur_lldp_cfg == exp_lldp_cfg: - self.conf_exsit = True - lldp_config.append(dict(lldpenable=lldp_dict['lldpEnable'], mdnstatus=lldp_dict['mdnStatus'])) - - conf_str = CE_NC_GET_GLOBAL_LLDP_CONFIG - conf_obj = get_nc_config(self.module, conf_str) - if "" in conf_obj: - pass - - else: - xml_str = conf_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get all ntp config info - root = ElementTree.fromstring(xml_str) - ntpsite = root.findall("lldp/lldpSys/lldpSysParameter") - for nexthop in ntpsite: - for ele in nexthop: - if ele.tag in ["messageTxInterval", "messageTxHoldMultiplier", "reinitDelay", "txDelay", - "notificationInterval", "fastMessageCount", "mdnNotificationInterval", - "configManAddr", "bindifName"]: - lldp_dict[ele.tag] = ele.text - - if self.state == "present": - cur_ntp_cfg = dict(interval=lldp_dict['messageTxInterval'], - hold_multiplier=lldp_dict['messageTxHoldMultiplier'], - restart_delay=lldp_dict['reinitDelay'], - transmit_delay=lldp_dict['txDelay'], - notification_interval=lldp_dict['notificationInterval'], - fast_count=lldp_dict['fastMessageCount'], - mdn_notification_interval=lldp_dict['mdnNotificationInterval'], - management_address=lldp_dict['configManAddr'], - bind_name=lldp_dict['bindifName']) - - exp_ntp_cfg = dict(interval=self.interval, hold_multiplier=self.hold_multiplier, - restart_delay=self.restart_delay, transmit_delay=self.transmit_delay, - notification_interval=self.notification_interval, - fast_count=self.fast_count, mdn_notification_interval=self.mdn_notification_interval, - management_address=self.management_address, bind_name=self.bind_name) - - if cur_ntp_cfg == exp_ntp_cfg: - self.conf_exsit_lldp = True - - lldp_config.append(dict(interval=lldp_dict['messageTxInterval'], - hold_multiplier=lldp_dict['messageTxHoldMultiplier'], - restart_delay=lldp_dict['reinitDelay'], transmit_delay=lldp_dict['txDelay'], - notification_interval=lldp_dict['notificationInterval'], - fast_count=lldp_dict['fastMessageCount'], - mdn_notification_interval=lldp_dict['mdnNotificationInterval'], - management_address=lldp_dict['configManAddr'], - bind_name=lldp_dict['bindifName'])) - - tmp_dict = dict() - str_1 = str(lldp_config) - temp_1 = str_1.replace('[', '').replace(']', '').replace('{', '').replace('}', '').replace('\'', '') - if temp_1: - tmp_2 = temp_1.split(',') - for i in tmp_2: - tmp_value = re.match(r'(.*):(.*)', i) - key_tmp = tmp_value.group(1) - key_value = tmp_value.group(2) - tmp_dict[key_tmp] = key_value - return tmp_dict - - def get_existing(self): - """Get existing info""" - - self.existing = self.get_lldp_exist_config() - - def get_proposed(self): - """Get proposed info""" - - if self.enable_flag == 1: - if self.lldpenable == 'enabled': - self.proposed = dict(lldpenable=self.lldpenable) - if self.mdnstatus: - self.proposed = dict(mdnstatus=self.mdnstatus) - elif self.lldpenable == 'disabled': - self.proposed = dict(lldpenable=self.lldpenable) - self.changed = True - else: - if self.mdnstatus: - self.proposed = dict(mdnstatus=self.mdnstatus) - else: - if self.lldpenable == 'enabled': - self.proposed = dict(lldpenable=self.lldpenable) - self.changed = True - if self.mdnstatus: - self.proposed = dict(mdnstatus=self.mdnstatus) - if self.enable_flag == 1 or self.lldpenable == 'enabled': - if self.interval: - self.proposed = dict(interval=self.interval) - if self.hold_multiplier: - self.proposed = dict(hold_multiplier=self.hold_multiplier) - if self.restart_delay: - self.proposed = dict(restart_delay=self.restart_delay) - if self.transmit_delay: - self.proposed = dict(transmit_delay=self.transmit_delay) - if self.notification_interval: - self.proposed = dict(notification_interval=self.notification_interval) - if self.fast_count: - self.proposed = dict(fast_count=self.fast_count) - if self.mdn_notification_interval: - self.proposed = dict(mdn_notification_interval=self.mdn_notification_interval) - if self.management_address: - self.proposed = dict(management_address=self.management_address) - if self.bind_name: - self.proposed = dict(bind_name=self.bind_name) - - def get_end_state(self): - """Get end state info""" - - self.end_state = self.get_lldp_exist_config() - existing_key_list = self.existing.keys() - end_state_key_list = self.end_state.keys() - for i in end_state_key_list: - for j in existing_key_list: - if i == j and self.existing[i] != self.end_state[j]: - self.changed = True - - def get_update_cmd(self): - """Get updated commands""" - - if self.conf_exsit and self.conf_exsit_lldp: - return - - if self.state == "present": - if self.lldpenable == "enabled": - self.updates_cmd.append("lldp enable") - - if self.mdnstatus: - self.updates_cmd.append("lldp mdn enable") - if self.mdnstatus == "rxOnly": - self.updates_cmd.append("lldp mdn enable") - else: - self.updates_cmd.append("undo lldp mdn enable") - if self.interval: - self.updates_cmd.append("lldp transmit interval %s" % self.interval) - if self.hold_multiplier: - self.updates_cmd.append("lldp transmit multiplier %s" % self.hold_multiplier) - if self.restart_delay: - self.updates_cmd.append("lldp restart %s" % self.restart_delay) - if self.transmit_delay: - self.updates_cmd.append("lldp transmit delay %s" % self.transmit_delay) - if self.notification_interval: - self.updates_cmd.append("lldp trap-interval %s" % self.notification_interval) - if self.fast_count: - self.updates_cmd.append("lldp fast-count %s" % self.fast_count) - if self.mdn_notification_interval: - self.updates_cmd.append("lldp mdn trap-interval %s" % self.mdn_notification_interval) - if self.management_address: - self.updates_cmd.append("lldp management-address %s" % self.management_address) - if self.bind_name: - self.updates_cmd.append("lldp management-address bind interface %s" % self.bind_name) - elif self.lldpenable == "disabled": - self.updates_cmd.append("undo lldp enable") - else: - if self.enable_flag == 1: - if self.mdnstatus: - if self.mdnstatus == "rxOnly": - self.updates_cmd.append("lldp mdn enable") - else: - self.updates_cmd.append("undo lldp mdn enable") - if self.interval: - self.updates_cmd.append("lldp transmit interval %s" % self.interval) - if self.hold_multiplier: - self.updates_cmd.append("lldp transmit multiplier %s" % self.hold_multiplier) - if self.restart_delay: - self.updates_cmd.append("lldp restart %s" % self.restart_delay) - if self.transmit_delay: - self.updates_cmd.append("lldp transmit delay %s" % self.transmit_delay) - if self.notification_interval: - self.updates_cmd.append("lldp trap-interval %s" % self.notification_interval) - if self.fast_count: - self.updates_cmd.append("lldp fast-count %s" % self.fast_count) - if self.mdn_notification_interval: - self.updates_cmd.append("lldp mdn trap-interval %s" % self.mdn_notification_interval) - if self.management_address: - self.updates_cmd.append("lldp management-address %s" % self.management_address) - if self.bind_name: - self.updates_cmd.append("lldp management-address bind interface %s" % self.bind_name) - - def work(self): - """Execute task""" - self.check_params() - self.get_existing() - self.get_proposed() - self.config_lldp() - self.get_update_cmd() - self.get_end_state() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - lldpenable=dict(required=False, choices=['enabled', 'disabled']), - mdnstatus=dict(required=False, choices=['rxOnly', 'disabled']), - interval=dict(required=False, type='int'), - hold_multiplier=dict(required=False, type='int'), - restart_delay=dict(required=False, type='int'), - transmit_delay=dict(required=False, type='int'), - notification_interval=dict(required=False, type='int'), - fast_count=dict(required=False, type='int'), - mdn_notification_interval=dict(required=False, type='int'), - management_address=dict(required=False, type='str'), - bind_name=dict(required=False, type='str'), - state=dict(choices=['absent', 'present'], default='present'), - ) - lldp_obj = Lldp(argument_spec) - lldp_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_lldp_interface.py b/plugins/modules/network/cloudengine/ce_lldp_interface.py deleted file mode 100644 index 67a2355880..0000000000 --- a/plugins/modules/network/cloudengine/ce_lldp_interface.py +++ /dev/null @@ -1,1384 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = r''' ---- -module: ce_lldp_interface -short_description: Manages INTERFACE LLDP configuration on HUAWEI CloudEngine switches. -description: - - Manages INTERFACE LLDP configuration on HUAWEI CloudEngine switches. -author: xuxiaowei0512 (@CloudEngine-Ansible) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - lldpenable: - description: - - Set global LLDP enable state. - type: str - choices: ['enabled', 'disabled'] - function_lldp_interface_flag: - description: - - Used to distinguish between command line functions. - type: str - choices: ['disableINTERFACE','tlvdisableINTERFACE','tlvenableINTERFACE','intervalINTERFACE'] - type_tlv_disable: - description: - - Used to distinguish between command line functions. - type: str - choices: ['basic_tlv', 'dot3_tlv'] - type_tlv_enable: - description: - - Used to distinguish between command line functions. - type: str - choices: ['dot1_tlv','dcbx'] - lldpadminstatus: - description: - - Set interface lldp enable state. - type: str - choices: ['txOnly', 'rxOnly', 'txAndRx', 'disabled'] - ifname: - description: - - Interface name. - type: str - txinterval: - description: - - LLDP send message interval. - type: int - txprotocolvlanid: - description: - - Set tx protocol vlan id. - type: int - txvlannameid: - description: - - Set tx vlan name id. - type: int - vlannametxenable: - description: - - Set vlan name tx enable or not. - type: bool - manaddrtxenable: - description: - - Make it able to send management address TLV. - type: bool - portdesctxenable: - description: - - Enabling the ability to send a description of TLV. - type: bool - syscaptxenable: - description: - - Enable the ability to send system capabilities TLV. - type: bool - sysdesctxenable: - description: - - Enable the ability to send system description TLV. - type: bool - sysnametxenable: - description: - - Enable the ability to send system name TLV. - type: bool - portvlantxenable: - description: - - Enable port vlan tx. - type: bool - protovlantxenable: - description: - - Enable protocol vlan tx. - type: bool - protoidtxenable: - description: - - Enable the ability to send protocol identity TLV. - type: bool - macphytxenable: - description: - - Enable MAC/PHY configuration and state TLV to be sent. - type: bool - linkaggretxenable: - description: - - Enable the ability to send link aggregation TLV. - type: bool - maxframetxenable: - description: - - Enable the ability to send maximum frame length TLV. - type: bool - eee: - description: - - Enable the ability to send EEE TLV. - type: bool - dcbx: - description: - - Enable the ability to send DCBX TLV. - type: bool - state: - description: - - Manage the state of the resource. - type: str - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' - - name: "Configure global LLDP enable state" - ce_lldp_interface_interface: - lldpenable: enabled - - - name: "Configure interface lldp enable state" - ce_lldp_interface: - function_lldp_interface_flag: disableINTERFACE - ifname: 10GE1/0/1 - lldpadminstatus: rxOnly - - name: "Configure LLDP transmit interval and ensure global LLDP state is already enabled" - ce_lldp_interface: - function_lldp_interface_flag: intervalINTERFACE - ifname: 10GE1/0/1 - txinterval: 4 - - - name: "Configure basic-tlv: management-address TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: basic_tlv - ifname: 10GE1/0/1 - manaddrtxenable: true - - - name: "Configure basic-tlv: prot description TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: basic_tlv - ifname: 10GE1/0/1 - portdesctxenable: true - - - name: "Configure basic-tlv: system capabilities TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: basic_tlv - ifname: 10GE1/0/1 - syscaptxenable: true - - - name: "Configure basic-tlv: system description TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: basic_tlv - ifname: 10GE1/0/1 - sysdesctxenable: true - - - name: "Configure basic-tlv: system name TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: basic_tlv - ifname: 10GE1/0/1 - sysnametxenable: true - - - name: "TLV types that are forbidden to be published on the configuration interface, link aggregation TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: dot3_tlv - ifname: 10GE1/0/1 - linkAggreTxEnable: true - - - name: "TLV types that are forbidden to be published on the configuration interface, MAC/PHY configuration/status TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: dot3_tlv - ifname: 10GE1/0/1 - macPhyTxEnable: true - - - name: "TLV types that are forbidden to be published on the configuration interface, maximum frame size TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: dot3_tlv - ifname: 10GE1/0/1 - maxFrameTxEnable: true - - - name: "TLV types that are forbidden to be published on the configuration interface, EEE TLV" - ce_lldp_interface: - function_lldp_interface_flag: tlvdisableINTERFACE - type_tlv_disable: dot3_tlv - ifname: 10GE1/0/1 - eee: true - - - name: "Configure the interface to publish an optional DCBX TLV type " - ce_lldp_interface: - function_lldp_interface_flag: tlvenableINTERFACE - ifname: 10GE1/0/1 - type_tlv_enable: dcbx -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "lldpenable": "enabled", - "lldpadminstatus": "rxOnly", - "function_lldp_interface_flag": "tlvenableINTERFACE", - "type_tlv_enable": "dot1_tlv", - "ifname": "10GE1/0/1", - "state": "present" - } -existing: - description: k/v pairs of existing global LLDP configration - returned: always - type: dict - sample: { - "lldpenable": "disabled", - "ifname": "10GE1/0/1", - "lldpadminstatus": "txAndRx" - } -end_state: - description: k/v pairs of global DLDP configration after module execution - returned: always - type: dict - sample: { - "lldpenable": "enabled", - "lldpadminstatus": "rxOnly", - "function_lldp_interface_flag": "tlvenableINTERFACE", - "type_tlv_enable": "dot1_tlv", - "ifname": "10GE1/0/1" - } -updates: - description: command sent to the device - returned: always - type: list - sample: [ - "lldp enable", - "interface 10ge 1/0/1", - "undo lldp disable", - "lldp tlv-enable dot1-tlv vlan-name 4", - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import copy -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import set_nc_config, get_nc_config - -CE_NC_GET_GLOBAL_LLDPENABLE_CONFIG = """ - - - - - - - -""" - -CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG = """ - - - - %s - - - -""" - -CE_NC_GET_INTERFACE_LLDP_CONFIG = """ - - - - - - - - - - -""" - -CE_NC_MERGE_INTERFACE_LLDP_CONFIG = """ - - - - - %s - %s - - - - -""" - -CE_NC_GET_INTERFACE_INTERVAl_CONFIG = """ - - - - - - - - - - - - -""" - -CE_NC_MERGE_INTERFACE_INTERVAl_CONFIG = """ - - - - - %s - - %s - - - - - -""" - -CE_NC_GET_INTERFACE_TLV_ENABLE_CONFIG = """ - - - - - - - - - - - - - -""" - -CE_NC_GET_INTERFACE_TLV_DISABLE_CONFIG = """ - - - - - - - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER = """ - - - - - %s - -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_ENABLE_PROTOIDTXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_ENABLE_DCBX = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_MANADDRTXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_PORTDESCTXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_SYSCAPTXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_SYSDESCTXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_SYSNAMETXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_LINKAGGRETXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_MACPHYTXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_MAXFRAMETXENABLE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_EEE = """ - %s -""" - -CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL = """ - - - - - -""" - -CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG = """ - - - - %s - - - -""" - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('PORT-GROUP'): - iftype = 'stack-Port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - return iftype.lower() - - -class Lldp_interface(object): - """Manage global lldp enable configuration""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - self.lldpenable = self.module.params['lldpenable'] or None - self.function_lldp_interface_flag = self.module.params['function_lldp_interface_flag'] - self.type_tlv_disable = self.module.params['type_tlv_disable'] - self.type_tlv_enable = self.module.params['type_tlv_enable'] - self.ifname = self.module.params['ifname'] - if self.function_lldp_interface_flag == 'disableINTERFACE': - self.ifname = self.module.params['ifname'] - self.lldpadminstatus = self.module.params['lldpadminstatus'] - elif self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - if self.type_tlv_disable == 'basic_tlv': - self.ifname = self.module.params['ifname'] - self.manaddrtxenable = self.module.params['manaddrtxenable'] - self.portdesctxenable = self.module.params['portdesctxenable'] - self.syscaptxenable = self.module.params['syscaptxenable'] - self.sysdesctxenable = self.module.params['sysdesctxenable'] - self.sysnametxenable = self.module.params['sysnametxenable'] - if self.type_tlv_disable == 'dot3_tlv': - self.ifname = self.module.params['ifname'] - self.macphytxenable = self.module.params['macphytxenable'] - self.linkaggretxenable = self.module.params['linkaggretxenable'] - self.maxframetxenable = self.module.params['maxframetxenable'] - self.eee = self.module.params['eee'] - elif self.function_lldp_interface_flag == 'tlvenableINTERFACE': - if self.type_tlv_enable == 'dot1_tlv': - self.ifname = self.module.params['ifname'] - self.protoidtxenable = self.module.params['protoidtxenable'] - if self.type_tlv_enable == 'dcbx': - self.ifname = self.module.params['ifname'] - self.dcbx = self.module.params['dcbx'] - elif self.function_lldp_interface_flag == 'intervalINTERFACE': - self.ifname = self.module.params['ifname'] - self.txinterval = self.module.params['txinterval'] - self.state = self.module.params['state'] - - self.lldp_conf = dict() - self.conf_disable_exsit = False - self.conf_interface_lldp_disable_exsit = False - self.conf_interval_exsit = False - self.conf_tlv_disable_exsit = False - self.conf_tlv_enable_exsit = False - self.enable_flag = 0 - self.check_params() - self.existing_state_value = dict() - self.existing_end_state_value = dict() - self.interface_lldp_info = list() - - # state - self.changed = False - self.proposed_changed = dict() - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def check_params(self): - """Check all input params""" - - if self.ifname: - intf_type = get_interface_type(self.ifname) - if not intf_type: - self.module.fail_json(msg='Error: ifname name of %s is error.' % self.ifname) - if (len(self.ifname) < 1) or (len(self.ifname) > 63): - self.module.fail_json(msg='Error: Ifname length is beetween 1 and 63.') - - if self.function_lldp_interface_flag == 'intervalINTERFACE': - if self.txinterval: - if int(self.txinterval) < 1 or int(self.txinterval) > 32768: - self.module.fail_json( - msg='Error: The value of txinterval is out of [1 - 32768].') - if self.ifname: - intf_type = get_interface_type(self.ifname) - if not intf_type: - self.module.fail_json( - msg='Error: ifname name of %s ' - 'is error.' % self.ifname) - if (len(self.ifname) < 1) or (len(self.ifname) > 63): - self.module.fail_json( - msg='Error: Ifname length is beetween 1 and 63.') - - if self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - if self.type_tlv_disable == 'dot1_tlv': - if self.ifname: - intf_type = get_interface_type(self.ifname) - if not intf_type: - self.module.fail_json( - msg='Error: ifname name of %s ' - 'is error.' % self.ifname) - if (len(self.ifname) < 1) or (len(self.ifname) > 63): - self.module.fail_json( - msg='Error: Ifname length is beetween 1 and 63.') - - if self.function_lldp_interface_flag == 'tlvenableINTERFACE': - if self.type_tlv_enable == 'dot1_tlv': - if self.ifname: - intf_type = get_interface_type(self.ifname) - if not intf_type: - self.module.fail_json( - msg='Error: ifname name of %s ' - 'is error.' % self.ifname) - if (len(self.ifname) < 1) or (len(self.ifname) > 63): - self.module.fail_json( - msg='Error: Ifname length is beetween 1 and 63.') - - def check_response(self, xml_str, xml_name): - """Check if response message is already OK""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def get_lldp_enable_pre_config(self): - """Get lldp enable configure""" - - lldp_dict = dict() - lldp_config = list() - conf_enable_str = CE_NC_GET_GLOBAL_LLDPENABLE_CONFIG - conf_enable_obj = get_nc_config(self.module, conf_enable_str) - xml_enable_str = conf_enable_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get lldp enable config info - root_enable = ElementTree.fromstring(xml_enable_str) - ntpsite_enable = root_enable.findall("lldp/lldpSys") - for nexthop_enable in ntpsite_enable: - for ele_enable in nexthop_enable: - if ele_enable.tag in ["lldpEnable"]: - lldp_dict[ele_enable.tag] = ele_enable.text - if lldp_dict['lldpEnable'] == 'enabled': - self.enable_flag = 1 - lldp_config.append(dict(lldpenable=lldp_dict['lldpEnable'])) - return lldp_config - - def get_interface_lldp_disable_pre_config(self): - """Get interface undo lldp disable configure""" - lldp_dict = dict() - interface_lldp_disable_dict = dict() - if self.enable_flag == 1: - conf_enable_str = CE_NC_GET_INTERFACE_LLDP_CONFIG - conf_enable_obj = get_nc_config(self.module, conf_enable_str) - if "" in conf_enable_obj: - return - xml_enable_str = conf_enable_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_enable_str) - lldp_disable_enable = root.findall("lldp/lldpInterfaces/lldpInterface") - for nexthop_enable in lldp_disable_enable: - name = nexthop_enable.find("ifName") - status = nexthop_enable.find("lldpAdminStatus") - if name is not None and status is not None: - interface_lldp_disable_dict[name.text] = status.text - return interface_lldp_disable_dict - - def get_interface_lldp_disable_config(self): - lldp_config = list() - interface_lldp_disable_dict_tmp = dict() - if self.state == "present": - if self.ifname: - interface_lldp_disable_dict_tmp = self.get_interface_lldp_disable_pre_config() - key_list = interface_lldp_disable_dict_tmp.keys() - if len(key_list) != 0: - for key in key_list: - if key == self.ifname: - if interface_lldp_disable_dict_tmp[key] != self.lldpadminstatus: - self.conf_interface_lldp_disable_exsit = True - else: - self.conf_interface_lldp_disable_exsit = False - elif self.ifname not in key_list: - self.conf_interface_lldp_disable_exsit = True - elif (len(key_list) == 0) and self.ifname and self.lldpadminstatus: - self.conf_interface_lldp_disable_exsit = True - lldp_config.append(interface_lldp_disable_dict_tmp) - return lldp_config - - def get_interface_tlv_disable_config(self): - lldp_config = list() - lldp_dict = dict() - cur_interface_mdn_cfg = dict() - exp_interface_mdn_cfg = dict() - - if self.enable_flag == 1: - conf_str = CE_NC_GET_INTERFACE_TLV_DISABLE_CONFIG - conf_obj = get_nc_config(self.module, conf_str) - if "" in conf_obj: - return lldp_config - xml_str = conf_obj.replace('\r', '').replace('\n', '') - xml_str = xml_str.replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "") - xml_str = xml_str.replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - lldp_tlvdisable_ifname = root.findall("lldp/lldpInterfaces/lldpInterface") - for ele in lldp_tlvdisable_ifname: - ifname_tmp = ele.find("ifName") - manaddrtxenable_tmp = ele.find("tlvTxEnable/manAddrTxEnable") - portdesctxenable_tmp = ele.find("tlvTxEnable/portDescTxEnable") - syscaptxenable_tmp = ele.find("tlvTxEnable/sysCapTxEnable") - sysdesctxenable_tmp = ele.find("tlvTxEnable/sysDescTxEnable") - sysnametxenable_tmp = ele.find("tlvTxEnable/sysNameTxEnable") - linkaggretxenable_tmp = ele.find("tlvTxEnable/linkAggreTxEnable") - macphytxenable_tmp = ele.find("tlvTxEnable/macPhyTxEnable") - maxframetxenable_tmp = ele.find("tlvTxEnable/maxFrameTxEnable") - eee_tmp = ele.find("tlvTxEnable/eee") - if ifname_tmp is not None: - if ifname_tmp.text is not None: - cur_interface_mdn_cfg["ifname"] = ifname_tmp.text - if ifname_tmp is not None and manaddrtxenable_tmp is not None: - if manaddrtxenable_tmp.text is not None: - cur_interface_mdn_cfg["manaddrtxenable"] = manaddrtxenable_tmp.text - if ifname_tmp is not None and portdesctxenable_tmp is not None: - if portdesctxenable_tmp.text is not None: - cur_interface_mdn_cfg['portdesctxenable'] = portdesctxenable_tmp.text - if ifname_tmp is not None and syscaptxenable_tmp is not None: - if syscaptxenable_tmp.text is not None: - cur_interface_mdn_cfg['syscaptxenable'] = syscaptxenable_tmp.text - if ifname_tmp is not None and sysdesctxenable_tmp is not None: - if sysdesctxenable_tmp.text is not None: - cur_interface_mdn_cfg['sysdesctxenable'] = sysdesctxenable_tmp.text - if ifname_tmp is not None and sysnametxenable_tmp is not None: - if sysnametxenable_tmp.text is not None: - cur_interface_mdn_cfg['sysnametxenable'] = sysnametxenable_tmp.text - if ifname_tmp is not None and linkaggretxenable_tmp is not None: - if linkaggretxenable_tmp.text is not None: - cur_interface_mdn_cfg['linkaggretxenable'] = linkaggretxenable_tmp.text - if ifname_tmp is not None and macphytxenable_tmp is not None: - if macphytxenable_tmp.text is not None: - cur_interface_mdn_cfg['macphytxenable'] = macphytxenable_tmp.text - if ifname_tmp is not None and maxframetxenable_tmp is not None: - if maxframetxenable_tmp.text is not None: - cur_interface_mdn_cfg['maxframetxenable'] = maxframetxenable_tmp.text - if ifname_tmp is not None and eee_tmp is not None: - if eee_tmp.text is not None: - cur_interface_mdn_cfg['eee'] = eee_tmp.text - if self.state == "present": - if self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - if self.type_tlv_disable == 'basic_tlv': - if self.ifname: - exp_interface_mdn_cfg['ifname'] = self.ifname - if self.manaddrtxenable: - exp_interface_mdn_cfg['manaddrtxenable'] = self.manaddrtxenable - if self.portdesctxenable: - exp_interface_mdn_cfg['portdesctxenable'] = self.portdesctxenable - if self.syscaptxenable: - exp_interface_mdn_cfg['syscaptxenable'] = self.syscaptxenable - if self.sysdesctxenable: - exp_interface_mdn_cfg['sysdesctxenable'] = self.sysdesctxenable - if self.sysnametxenable: - exp_interface_mdn_cfg['sysnametxenable'] = self.sysnametxenable - if self.ifname == ifname_tmp.text: - key_list = exp_interface_mdn_cfg.keys() - key_list_cur = cur_interface_mdn_cfg.keys() - if len(key_list) != 0: - for key in key_list: - if key == "ifname" and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(ifname=cur_interface_mdn_cfg['ifname'])) - if "manaddrtxenable" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(manaddrtxenable=cur_interface_mdn_cfg['manaddrtxenable'])) - if "portdesctxenable" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(portdesctxenable=cur_interface_mdn_cfg['portdesctxenable'])) - if "syscaptxenable" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(syscaptxenable=cur_interface_mdn_cfg['syscaptxenable'])) - if "sysdesctxenable" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(sysdesctxenable=cur_interface_mdn_cfg['sysdesctxenable'])) - if "sysnametxenable" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(sysnametxenable=cur_interface_mdn_cfg['sysnametxenable'])) - if key in key_list_cur: - if str(exp_interface_mdn_cfg[key]) != str(cur_interface_mdn_cfg[key]): - self.conf_tlv_disable_exsit = True - self.changed = True - return lldp_config - else: - self.conf_tlv_disable_exsit = True - return lldp_config - - if self.type_tlv_disable == 'dot3_tlv': - if self.ifname: - exp_interface_mdn_cfg['ifname'] = self.ifname - if self.linkaggretxenable: - exp_interface_mdn_cfg['linkaggretxenable'] = self.linkaggretxenable - if self.macphytxenable: - exp_interface_mdn_cfg['macphytxenable'] = self.macphytxenable - if self.maxframetxenable: - exp_interface_mdn_cfg['maxframetxenable'] = self.maxframetxenable - if self.eee: - exp_interface_mdn_cfg['eee'] = self.eee - if self.ifname == ifname_tmp.text: - key_list = exp_interface_mdn_cfg.keys() - key_list_cur = cur_interface_mdn_cfg.keys() - if len(key_list) != 0: - for key in key_list: - if key == "ifname" and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(ifname=cur_interface_mdn_cfg['ifname'])) - if "linkaggretxenable" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(linkaggretxenable=cur_interface_mdn_cfg['linkaggretxenable'])) - if "macphytxenable" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(macphytxenable=cur_interface_mdn_cfg['macphytxenable'])) - if "maxframetxenable" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(maxframetxenable=cur_interface_mdn_cfg['maxframetxenable'])) - if "eee" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(eee=cur_interface_mdn_cfg['eee'])) - if key in key_list_cur: - if str(exp_interface_mdn_cfg[key]) != str(cur_interface_mdn_cfg[key]): - self.conf_tlv_disable_exsit = True - self.changed = True - return lldp_config - else: - self.conf_tlv_disable_exsit = True - return lldp_config - return lldp_config - - def get_interface_tlv_enable_config(self): - lldp_config = list() - lldp_dict = dict() - cur_interface_mdn_cfg = dict() - exp_interface_mdn_cfg = dict() - if self.enable_flag == 1: - conf_str = CE_NC_GET_INTERFACE_TLV_ENABLE_CONFIG - conf_obj = get_nc_config(self.module, conf_str) - if "" in conf_obj: - return lldp_config - xml_str = conf_obj.replace('\r', '').replace('\n', '') - xml_str = xml_str.replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "") - xml_str = xml_str.replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - lldpenablesite = root.findall("lldp/lldpInterfaces/lldpInterface") - for ele in lldpenablesite: - ifname_tmp = ele.find("ifName") - protoidtxenable_tmp = ele.find("tlvTxEnable/protoIdTxEnable") - dcbx_tmp = ele.find("tlvTxEnable/dcbx") - if ifname_tmp is not None: - if ifname_tmp.text is not None: - cur_interface_mdn_cfg["ifname"] = ifname_tmp.text - if ifname_tmp is not None and protoidtxenable_tmp is not None: - if protoidtxenable_tmp.text is not None: - cur_interface_mdn_cfg["protoidtxenable"] = protoidtxenable_tmp.text - if ifname_tmp is not None and dcbx_tmp is not None: - if dcbx_tmp.text is not None: - cur_interface_mdn_cfg['dcbx'] = dcbx_tmp.text - if self.state == "present": - if self.function_lldp_interface_flag == 'tlvenableINTERFACE': - if self.type_tlv_enable == 'dot1_tlv': - if self.ifname: - exp_interface_mdn_cfg['ifname'] = self.ifname - if self.protoidtxenable: - exp_interface_mdn_cfg['protoidtxenable'] = self.protoidtxenable - if self.ifname == ifname_tmp.text: - key_list = exp_interface_mdn_cfg.keys() - key_list_cur = cur_interface_mdn_cfg.keys() - if len(key_list) != 0: - for key in key_list: - if "protoidtxenable" == str(key) and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(protoidtxenable=cur_interface_mdn_cfg['protoidtxenable'])) - if key in key_list_cur: - if str(exp_interface_mdn_cfg[key]) != str(cur_interface_mdn_cfg[key]): - self.conf_tlv_enable_exsit = True - self.changed = True - return lldp_config - else: - self.conf_tlv_enable_exsit = True - return lldp_config - if self.type_tlv_enable == 'dcbx': - if self.ifname: - exp_interface_mdn_cfg['ifname'] = self.ifname - if self.dcbx: - exp_interface_mdn_cfg['dcbx'] = self.dcbx - if self.ifname == ifname_tmp.text: - key_list = exp_interface_mdn_cfg.keys() - key_list_cur = cur_interface_mdn_cfg.keys() - if len(key_list) != 0: - for key in key_list: - if "dcbx" == key and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(dcbx=cur_interface_mdn_cfg['dcbx'])) - if key in key_list_cur: - if str(exp_interface_mdn_cfg[key]) != str(cur_interface_mdn_cfg[key]): - self.conf_tlv_enable_exsit = True - self.changed = True - return lldp_config - else: - self.conf_tlv_enable_exsit = True - return lldp_config - return lldp_config - - def get_interface_interval_config(self): - lldp_config = list() - lldp_dict = dict() - cur_interface_mdn_cfg = dict() - exp_interface_mdn_cfg = dict() - interface_lldp_disable_dict_tmp2 = self.get_interface_lldp_disable_pre_config() - if self.enable_flag == 1: - if interface_lldp_disable_dict_tmp2[self.ifname] != 'disabled': - conf_str = CE_NC_GET_INTERFACE_INTERVAl_CONFIG - conf_obj = get_nc_config(self.module, conf_str) - if "" in conf_obj: - return lldp_config - xml_str = conf_obj.replace('\r', '').replace('\n', '') - xml_str = xml_str.replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "") - xml_str = xml_str.replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - txintervalsite = root.findall("lldp/lldpInterfaces/lldpInterface") - for ele in txintervalsite: - ifname_tmp = ele.find("ifName") - txinterval_tmp = ele.find("msgInterval/txInterval") - if ifname_tmp is not None: - if ifname_tmp.text is not None: - cur_interface_mdn_cfg["ifname"] = ifname_tmp.text - if txinterval_tmp is not None: - if txinterval_tmp.text is not None: - cur_interface_mdn_cfg["txinterval"] = txinterval_tmp.text - if self.state == "present": - if self.ifname: - exp_interface_mdn_cfg["ifname"] = self.ifname - if self.txinterval: - exp_interface_mdn_cfg["txinterval"] = self.txinterval - if self.ifname == ifname_tmp.text: - key_list = exp_interface_mdn_cfg.keys() - key_list_cur = cur_interface_mdn_cfg.keys() - if len(key_list) != 0: - for key in key_list: - if "txinterval" == str(key) and self.ifname == cur_interface_mdn_cfg['ifname']: - lldp_config.append(dict(ifname=cur_interface_mdn_cfg['ifname'], txinterval=exp_interface_mdn_cfg['txinterval'])) - if key in key_list_cur: - if str(exp_interface_mdn_cfg[key]) != str(cur_interface_mdn_cfg[key]): - self.conf_interval_exsit = True - lldp_config.append(cur_interface_mdn_cfg) - return lldp_config - else: - self.conf_interval_exsit = True - return lldp_config - return lldp_config - - def config_global_lldp_enable(self): - if self.state == 'present': - if self.enable_flag == 0 and self.lldpenable == 'enabled': - xml_str = CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG % self.lldpenable - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_ENABLE_CONFIG") - self.changed = True - elif self.enable_flag == 1 and self.lldpenable == 'disabled': - xml_str = CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG % self.lldpenable - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_ENABLE_CONFIG") - self.changed = True - - def config_interface_lldp_disable_config(self): - if self.function_lldp_interface_flag == 'disableINTERFACE': - if self.enable_flag == 1 and self.conf_interface_lldp_disable_exsit: - if self.ifname: - xml_str = CE_NC_MERGE_INTERFACE_LLDP_CONFIG % (self.ifname, self.lldpadminstatus) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "INTERFACE_LLDP_DISABLE_CONFIG") - self.changed = True - - def config_interface_tlv_disable_config(self): - if self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - if self.enable_flag == 1 and self.conf_tlv_disable_exsit: - if self.type_tlv_disable == 'basic_tlv': - if self.ifname: - if self.portdesctxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_PORTDESCTXENABLE % self.portdesctxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_PORTDESCTXENABLE") - self.changed = True - if self.manaddrtxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_MANADDRTXENABLE % self.manaddrtxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_MANADDRTXENABLE") - self.changed = True - if self.syscaptxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_SYSCAPTXENABLE % self.syscaptxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_SYSCAPTXENABLE") - self.changed = True - if self.sysdesctxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_SYSDESCTXENABLE % self.sysdesctxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_SYSDESCTXENABLE") - self.changed = True - if self.sysnametxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_SYSNAMETXENABLE % self.sysnametxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_SYSNAMETXENABLE") - self.changed = True - if self.type_tlv_disable == 'dot3_tlv': - if self.ifname: - if self.linkaggretxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_LINKAGGRETXENABLE % self.linkaggretxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_LINKAGGRETXENABLE") - self.changed = True - if self.macphytxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_MACPHYTXENABLE % self.macphytxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_MACPHYTXENABLE") - self.changed = True - if self.maxframetxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_MAXFRAMETXENABLE % self.maxframetxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_MAXFRAMETXENABLE") - self.changed = True - if self.eee: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_DISABLE_EEE % self.eee) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_DISABLE_EEE") - self.changed = True - - def config_interface_tlv_enable_config(self): - if self.function_lldp_interface_flag == 'tlvenableINTERFACE': - if self.enable_flag == 1 and self.conf_tlv_enable_exsit: - if self.type_tlv_enable == 'dot1_tlv': - if self.ifname: - if self.protoidtxenable: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_ENABLE_PROTOIDTXENABLE % self.protoidtxenable) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_ENABLE_DOT1_PORT_VLAN") - self.changed = True - if self.type_tlv_enable == 'dcbx': - if self.ifname: - if self.dcbx: - xml_str = (CE_NC_MERGE_INTERFACE_TLV_CONFIG_HEADER % self.ifname) + \ - (CE_NC_MERGE_INTERFACE_TLV_CONFIG_ENABLE_DCBX % self.dcbx) + \ - CE_NC_MERGE_INTERFACE_TLV_CONFIG_TAIL - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "TLV_ENABLE_DCBX_VLAN") - self.changed = True - - def config_interface_interval_config(self): - if self.function_lldp_interface_flag == 'intervalINTERFACE': - tmp = self.get_interface_lldp_disable_pre_config() - if self.enable_flag == 1 and self.conf_interval_exsit and tmp[self.ifname] != 'disabled': - if self.ifname: - if self.txinterval: - xml_str = CE_NC_MERGE_INTERFACE_INTERVAl_CONFIG % (self.ifname, self.txinterval) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "INTERFACE_INTERVAL_CONFIG") - self.changed = True - - def get_existing(self): - """get existing information""" - self.get_lldp_enable_pre_config() - if self.lldpenable: - self.existing['globalLLDPENABLE'] = self.get_lldp_enable_pre_config() - if self.function_lldp_interface_flag == 'disableINTERFACE': - self.existing['disableINTERFACE'] = self.get_interface_lldp_disable_config() - if self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - self.existing['tlvdisableINTERFACE'] = self.get_interface_tlv_disable_config() - if self.function_lldp_interface_flag == 'tlvenableINTERFACE': - self.existing['tlvenableINTERFACE'] = self.get_interface_tlv_enable_config() - if self.function_lldp_interface_flag == 'intervalINTERFACE': - self.existing['intervalINTERFACE'] = self.get_interface_interval_config() - - def get_proposed(self): - """get proposed""" - if self.lldpenable: - self.proposed = dict(lldpenable=self.lldpenable) - if self.function_lldp_interface_flag == 'disableINTERFACE': - if self.enable_flag == 1: - self.proposed = dict(ifname=self.ifname, lldpadminstatus=self.lldpadminstatus) - if self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - if self.enable_flag == 1: - if self.type_tlv_disable == 'basic_tlv': - if self.ifname: - if self.manaddrtxenable: - self.proposed = dict(ifname=self.ifname, manaddrtxenable=self.manaddrtxenable) - if self.portdesctxenable: - self.proposed = dict(ifname=self.ifname, portdesctxenable=self.portdesctxenable) - if self.syscaptxenable: - self.proposed = dict(ifname=self.ifname, syscaptxenable=self.syscaptxenable) - if self.sysdesctxenable: - self.proposed = dict(ifname=self.ifname, sysdesctxenable=self.sysdesctxenable) - if self.sysnametxenable: - self.proposed = dict(ifname=self.ifname, sysnametxenable=self.sysnametxenable) - if self.type_tlv_disable == 'dot3_tlv': - if self.ifname: - if self.linkaggretxenable: - self.proposed = dict(ifname=self.ifname, linkaggretxenable=self.linkaggretxenable) - if self.macphytxenable: - self.proposed = dict(ifname=self.ifname, macphytxenable=self.macphytxenable) - if self.maxframetxenable: - self.proposed = dict(ifname=self.ifname, maxframetxenable=self.maxframetxenable) - if self.eee: - self.proposed = dict(ifname=self.ifname, eee=self.eee) - if self.function_lldp_interface_flag == 'tlvenableINTERFACE': - if self.enable_flag == 1: - if self.type_tlv_enable == 'dot1_tlv': - if self.ifname: - if self.protoidtxenable: - self.proposed = dict(ifname=self.ifname, protoidtxenable=self.protoidtxenable) - if self.type_tlv_enable == 'dcbx': - if self.ifname: - if self.dcbx: - self.proposed = dict(ifname=self.ifname, dcbx=self.dcbx) - if self.function_lldp_interface_flag == 'intervalINTERFACE': - tmp1 = self.get_interface_lldp_disable_pre_config() - if self.enable_flag == 1 and tmp1[self.ifname] != 'disabled': - self.proposed = dict(ifname=self.ifname, txinterval=self.txinterval) - - def config_lldp_interface(self): - """config lldp interface""" - if self.lldpenable: - self.config_global_lldp_enable() - if self.function_lldp_interface_flag == 'disableINTERFACE': - self.config_interface_lldp_disable_config() - elif self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - self.config_interface_tlv_disable_config() - elif self.function_lldp_interface_flag == 'tlvenableINTERFACE': - self.config_interface_tlv_enable_config() - elif self.function_lldp_interface_flag == 'intervalINTERFACE': - self.config_interface_interval_config() - - def get_end_state(self): - """get end_state information""" - self.get_lldp_enable_pre_config() - if self.lldpenable: - self.end_state['globalLLDPENABLE'] = self.get_lldp_enable_pre_config() - if self.function_lldp_interface_flag == 'disableINTERFACE': - self.end_state['disableINTERFACE'] = self.get_interface_lldp_disable_config() - if self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - self.end_state['tlvdisableINTERFACE'] = self.get_interface_tlv_disable_config() - if self.function_lldp_interface_flag == 'tlvenableINTERFACE': - self.end_state['tlvenableINTERFACE'] = self.get_interface_tlv_enable_config() - if self.function_lldp_interface_flag == 'intervalINTERFACE': - self.end_state['intervalINTERFACE'] = self.get_interface_interval_config() - - def get_update_cmd(self): - """Get updated commands""" - - cmds = [] - if self.state == "present": - if self.lldpenable == "enabled": - cmds.append("lldp enable") - if self.function_lldp_interface_flag == 'disableINTERFACE': - if self.ifname: - cmds.append("%s %s" % ("interface", self.ifname)) - if self.lldpadminstatus == 'disabled': - cmds.append("lldp disable") - else: - cmds.append("undo lldp disable") - elif self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - if self.type_tlv_disable == 'basic_tlv': - if self.ifname: - cmds.append("%s %s" % ("interface", self.ifname)) - if self.manaddrtxenable: - if self.manaddrtxenable == "false": - cmds.append("lldp tlv-disable basic-tlv management-address") - if self.manaddrtxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv management-address") - if self.portdesctxenable: - if self.portdesctxenable == "false": - cmds.append("lldp tlv-disable basic-tlv port-description") - if self.portdesctxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv port-description") - if self.syscaptxenable: - if self.syscaptxenable == "false": - cmds.append("lldp tlv-disable basic-tlv system-capability") - if self.syscaptxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv system-capability") - if self.sysdesctxenable: - if self.sysdesctxenable == "false": - cmds.append("lldp tlv-disable basic-tlv system-description") - if self.sysdesctxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv system-description") - if self.sysnametxenable: - if self.sysnametxenable == "false": - cmds.append("lldp tlv-disable basic-tlv system-name") - if self.sysnametxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv system-name") - if self.type_tlv_disable == 'dot3_tlv': - if self.ifname: - cmds.append("%s %s" % ("interface", self.ifname)) - if self.linkaggretxenable: - if self.linkaggretxenable == "false": - cmds.append("lldp tlv-disable dot3-tlv link-aggregation") - if self.linkaggretxenable == "true": - cmds.append("undo lldp tlv-disable dot3-tlv link-aggregation") - if self.macphytxenable: - if self.macphytxenable == "false": - cmds.append("lldp tlv-disable dot3-tlv mac-physic") - if self.macphytxenable == "true": - cmds.append("undo lldp tlv-disable dot3-tlv mac-physic") - if self.maxframetxenable: - if self.maxframetxenable == "false": - cmds.append("lldp tlv-disable dot3-tlv max-frame-size") - if self.maxframetxenable == "true": - cmds.append("undo lldp tlv-disable dot3-tlv max-frame-size") - if self.eee: - if self.eee == "false": - cmds.append("lldp tlv-disable dot3-tlv eee") - if self.eee == "true": - cmds.append("undo lldp tlv-disable dot3-tlv eee") - elif self.function_lldp_interface_flag == 'tlvenableINTERFACE': - if self.type_tlv_enable == 'dot1_tlv': - if self.ifname: - cmds.append("%s %s" % ("interface", self.ifname)) - if self.protoidtxenable: - if self.protoidtxenable == "false": - cmds.append("undo lldp tlv-enable dot1-tlv protocol-identity") - if self.protoidtxenable == "true": - cmds.append("lldp tlv-enable dot1-tlv protocol-identity") - if self.type_tlv_enable == 'dcbx': - if self.ifname: - cmds.append("%s %s" % ("interface", self.ifname)) - if self.dcbx: - if self.dcbx == "false": - cmds.append("undo lldp tlv-enable dcbx") - if self.dcbx == "true": - cmds.append("lldp tlv-enable dcbx") - elif self.function_lldp_interface_flag == 'intervalINTERFACE': - if self.ifname: - cmds.append("%s %s" % ("interface", self.ifname)) - if self.txinterval: - cmds.append("lldp transmit fast-mode interval %s" % self.txinterval) - elif self.lldpenable == "disabled": - cmds.append("undo lldp enable") - else: - if self.enable_flag == 1: - if self.function_lldp_interface_flag == 'disableINTERFACE': - if self.ifname: - cmds.append("interface %s" % self.ifname) - if self.lldpadminstatus == 'disabled': - cmds.append("lldp disable") - else: - cmds.append("undo lldp disable") - elif self.function_lldp_interface_flag == 'tlvdisableINTERFACE': - if self.type_tlv_disable == 'basic_tlv': - if self.ifname: - cmds.append("interface %s" % self.ifname) - if self.manaddrtxenable: - if self.manaddrtxenable == "false": - cmds.append("lldp tlv-disable basic-tlv management-address") - if self.manaddrtxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv management-address") - if self.portdesctxenable: - if self.portdesctxenable == "false": - cmds.append("lldp tlv-disable basic-tlv port-description") - if self.portdesctxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv port-description") - if self.syscaptxenable: - if self.syscaptxenable == "false": - cmds.append("lldp tlv-disable basic-tlv system-capability") - if self.syscaptxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv system-capability") - if self.sysdesctxenable: - if self.sysdesctxenable == "false": - cmds.append("lldp tlv-disable basic-tlv system-description") - if self.sysdesctxenable == "true": - cli_str = "%s %s\n" % (cli_str, "undo lldp tlv-disable basic-tlv system-description") - if self.sysnametxenable: - if self.sysnametxenable == "false": - cmds.append("lldp tlv-disable basic-tlv system-name") - if self.sysnametxenable == "true": - cmds.append("undo lldp tlv-disable basic-tlv system-name") - if self.type_tlv_disable == 'dot3_tlv': - if self.ifname: - cmds.append("interface %s" % self.ifname) - if self.linkaggretxenable: - if self.linkaggretxenable == "false": - cmds.append("lldp tlv-disable dot3-tlv link-aggregation") - if self.linkaggretxenable == "true": - cmds.append("undo lldp tlv-disable dot3-tlv link-aggregation") - if self.macphytxenable: - if self.macphytxenable == "false": - cmds.append("lldp tlv-disable dot3-tlv mac-physic") - if self.macphytxenable == "true": - cli_str = "%s %s\n" % (cli_str, "undo lldp tlv-disable dot3-tlv mac-physic") - if self.maxframetxenable: - if self.maxframetxenable == "false": - cmds.append("lldp tlv-disable dot3-tlv max-frame-size") - if self.maxframetxenable == "true": - cmds.append("undo lldp tlv-disable dot3-tlv max-frame-size") - if self.eee: - if self.eee == "false": - cmds.append("lldp tlv-disable dot3-tlv eee") - if self.eee == "true": - cmds.append("undo lldp tlv-disable dot3-tlv eee") - elif self.function_lldp_interface_flag == 'tlvenableINTERFACE': - if self.type_tlv_enable == 'dot1_tlv': - if self.ifname: - cmds.append("interface %s" % self.ifname) - if self.protoidtxenable: - if self.protoidtxenable == "false": - cmds.append("undo lldp tlv-enable dot1-tlv protocol-identity") - if self.protoidtxenable == "true": - cmds.append("lldp tlv-enable dot1-tlv protocol-identity") - if self.type_tlv_enable == 'dcbx': - if self.ifname: - cmds.append("interface %s" % self.ifname) - if self.dcbx: - if self.dcbx == "false": - cmds.append("undo lldp tlv-enable dcbx") - if self.dcbx == "true": - cmds.append("lldp tlv-enable dcbx") - elif self.function_lldp_interface_flag == 'intervalINTERFACE': - if self.ifname: - cmds.append("interface %s" % self.ifname) - if self.txinterval: - cmds.append("lldp transmit fast-mode interval %s" % self.txinterval) - self.updates_cmd = cmds - - def work(self): - """Execute task""" - self.check_params() - self.get_existing() - self.get_proposed() - self.config_lldp_interface() - self.get_update_cmd() - self.get_end_state() - self.show_result() - - -def main(): - """Main function""" - - argument_spec = dict( - lldpenable=dict(choices=['enabled', 'disabled']), - function_lldp_interface_flag=dict(choices=['disableINTERFACE', 'tlvdisableINTERFACE', 'tlvenableINTERFACE', 'intervalINTERFACE'], type='str'), - type_tlv_disable=dict(choices=['basic_tlv', 'dot3_tlv'], type='str'), - type_tlv_enable=dict(choices=['dot1_tlv', 'dcbx'], type='str'), - ifname=dict(type='str'), - lldpadminstatus=dict(choices=['txOnly', 'rxOnly', 'txAndRx', 'disabled'], type='str'), - manaddrtxenable=dict(type='bool'), - portdesctxenable=dict(type='bool'), - syscaptxenable=dict(type='bool'), - sysdesctxenable=dict(type='bool'), - sysnametxenable=dict(type='bool'), - portvlantxenable=dict(type='bool'), - protovlantxenable=dict(type='bool'), - txprotocolvlanid=dict(type='int'), - vlannametxenable=dict(type='bool'), - txvlannameid=dict(type='int'), - txinterval=dict(type='int'), - protoidtxenable=dict(type='bool'), - macphytxenable=dict(type='bool'), - linkaggretxenable=dict(type='bool'), - maxframetxenable=dict(type='bool'), - eee=dict(type='bool'), - dcbx=dict(type='bool'), - state=dict(type='str', choices=['absent', 'present'], default='present'), - ) - - lldp_interface_obj = Lldp_interface(argument_spec) - lldp_interface_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_mdn_interface.py b/plugins/modules/network/cloudengine/ce_mdn_interface.py deleted file mode 100644 index 583d81504f..0000000000 --- a/plugins/modules/network/cloudengine/ce_mdn_interface.py +++ /dev/null @@ -1,402 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# 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: ce_mdn_interface -short_description: Manages MDN configuration on HUAWEI CloudEngine switches. -description: - - Manages MDN configuration on HUAWEI CloudEngine switches. -author: xuxiaowei0512 (@CloudEngine-Ansible) -options: - lldpenable: - description: - - Set global LLDP enable state. - type: str - choices: ['enabled', 'disabled'] - mdnstatus: - description: - - Set interface MDN enable state. - type: str - choices: ['rxOnly', 'disabled'] - ifname: - description: - - Interface name. - type: str - state: - description: - - Manage the state of the resource. - default: present - type: str - choices: ['present','absent'] -notes: - - This module requires the netconf system service be enabled on - the remote device being managed. - - This module works with connection C(netconf). -''' - -EXAMPLES = ''' - - name: "Configure global LLDP enable state" - ce_mdn_interface: - lldpenable: enabled - - - name: "Configure interface MDN enable state" - ce_mdn_interface: - ifname: 10GE1/0/1 - mdnstatus: rxOnly -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "lldpenable": "enabled", - "ifname": "10GE1/0/1", - "mdnstatus": "rxOnly", - "state":"present" - } -existing: - description: k/v pairs of existing global LLDP configration - returned: always - type: dict - sample: { - "lldpenable": "enabled", - "ifname": "10GE1/0/1", - "mdnstatus": "disabled" - } -end_state: - description: k/v pairs of global LLDP configration after module execution - returned: always - type: dict - sample: { - "lldpenable": "enabled", - "ifname": "10GE1/0/1", - "mdnstatus": "rxOnly" - } -updates: - description: command sent to the device - returned: always - type: list - sample: [ - "interface 10ge 1/0/1", - "lldp mdn enable", - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import copy -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import set_nc_config, get_nc_config, execute_nc_action - -CE_NC_GET_GLOBAL_LLDPENABLE_CONFIG = """ - - - - - - - -""" - -CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG = """ - - - - %s - - - -""" - -CE_NC_GET_INTERFACE_MDNENABLE_CONFIG = """ - - - - - - - - - - -""" - -CE_NC_MERGE_INTERFACE_MDNENABLE_CONFIG = """ - - - - - %s - %s - - - - -""" - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('PORT-GROUP'): - iftype = 'stack-Port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - return iftype.lower() - - -class Interface_mdn(object): - """Manage global lldp enable configration""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # LLDP global configration info - self.lldpenable = self.module.params['lldpenable'] or None - self.ifname = self.module.params['ifname'] - self.mdnstatus = self.module.params['mdnstatus'] or None - self.state = self.module.params['state'] - self.lldp_conf = dict() - self.conf_exsit = False - self.enable_flag = 0 - self.check_params() - - # state - self.changed = False - self.proposed_changed = dict() - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def check_params(self): - """Check all input params""" - - if self.ifname: - intf_type = get_interface_type(self.ifname) - if not intf_type: - self.module.fail_json( - msg='Error: ifname name of %s ' - 'is error.' % self.ifname) - if (len(self.ifname) < 1) or (len(self.ifname) > 63): - self.module.fail_json( - msg='Error: Ifname length is beetween 1 and 63.') - - def init_module(self): - """Init module object""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def config_interface_mdn(self): - """Configure lldp enabled and interface mdn enabled parameters""" - - if self.state == 'present': - if self.enable_flag == 0 and self.lldpenable == 'enabled': - xml_str = CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG % self.lldpenable - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_ENABLE_CONFIG") - self.changed = True - elif self.enable_flag == 1 and self.lldpenable == 'disabled': - xml_str = CE_NC_MERGE_GLOBA_LLDPENABLE_CONFIG % self.lldpenable - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "LLDP_ENABLE_CONFIG") - self.changed = True - elif self.enable_flag == 1 and self.conf_exsit: - xml_str = CE_NC_MERGE_INTERFACE_MDNENABLE_CONFIG % (self.ifname, self.mdnstatus) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "INTERFACE_MDN_ENABLE_CONFIG") - self.changed = True - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def get_interface_mdn_exist_config(self): - """Get lldp existed configure""" - - lldp_config = list() - lldp_dict = dict() - conf_enable_str = CE_NC_GET_GLOBAL_LLDPENABLE_CONFIG - conf_enable_obj = get_nc_config(self.module, conf_enable_str) - xml_enable_str = conf_enable_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get lldp enable config info - root_enable = ElementTree.fromstring(xml_enable_str) - ntpsite_enable = root_enable.findall("lldp/lldpSys") - for nexthop_enable in ntpsite_enable: - for ele_enable in nexthop_enable: - if ele_enable.tag in ["lldpEnable"]: - lldp_dict[ele_enable.tag] = ele_enable.text - - if self.state == "present": - if lldp_dict['lldpEnable'] == 'enabled': - self.enable_flag = 1 - lldp_config.append(dict(lldpenable=lldp_dict['lldpEnable'])) - - if self.enable_flag == 1: - conf_str = CE_NC_GET_INTERFACE_MDNENABLE_CONFIG - conf_obj = get_nc_config(self.module, conf_str) - if "" in conf_obj: - return lldp_config - xml_str = conf_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - # get all ntp config info - root = ElementTree.fromstring(xml_str) - ntpsite = root.findall("lldp/mdnInterfaces/mdnInterface") - for nexthop in ntpsite: - for ele in nexthop: - if ele.tag in ["ifName", "mdnStatus"]: - lldp_dict[ele.tag] = ele.text - if self.state == "present": - cur_interface_mdn_cfg = dict(ifname=lldp_dict['ifName'], mdnstatus=lldp_dict['mdnStatus']) - exp_interface_mdn_cfg = dict(ifname=self.ifname, mdnstatus=self.mdnstatus) - if self.ifname == lldp_dict['ifName']: - if cur_interface_mdn_cfg != exp_interface_mdn_cfg: - self.conf_exsit = True - lldp_config.append(dict(ifname=lldp_dict['ifName'], mdnstatus=lldp_dict['mdnStatus'])) - return lldp_config - lldp_config.append(dict(ifname=lldp_dict['ifName'], mdnstatus=lldp_dict['mdnStatus'])) - return lldp_config - - def get_existing(self): - """Get existing info""" - - self.existing = self.get_interface_mdn_exist_config() - - def get_proposed(self): - """Get proposed info""" - - if self.lldpenable: - self.proposed = dict(lldpenable=self.lldpenable) - if self.enable_flag == 1: - if self.ifname: - self.proposed = dict(ifname=self.ifname, mdnstatus=self.mdnstatus) - - def get_end_state(self): - """Get end state info""" - - self.end_state = self.get_interface_mdn_exist_config() - - def get_update_cmd(self): - """Get updated commands""" - - update_list = list() - if self.state == "present": - if self.lldpenable == "enabled": - cli_str = "lldp enable" - update_list.append(cli_str) - if self.ifname: - cli_str = "%s %s" % ("interface", self.ifname) - update_list.append(cli_str) - if self.mdnstatus: - if self.mdnstatus == "rxOnly": - cli_str = "lldp mdn enable" - update_list.append(cli_str) - else: - cli_str = "undo lldp mdn enable" - update_list.append(cli_str) - - elif self.lldpenable == "disabled": - cli_str = "undo lldp enable" - update_list.append(cli_str) - else: - if self.enable_flag == 1: - if self.ifname: - cli_str = "%s %s" % ("interface", self.ifname) - update_list.append(cli_str) - if self.mdnstatus: - if self.mdnstatus == "rxOnly": - cli_str = "lldp mdn enable" - update_list.append(cli_str) - else: - cli_str = "undo lldp mdn enable" - update_list.append(cli_str) - - self.updates_cmd.append(update_list) - - def work(self): - """Excute task""" - self.check_params() - self.get_existing() - self.get_proposed() - self.config_interface_mdn() - self.get_update_cmd() - self.get_end_state() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - lldpenable=dict(type='str', choices=['enabled', 'disabled']), - mdnstatus=dict(type='str', choices=['rxOnly', 'disabled']), - ifname=dict(type='str'), - state=dict(choices=['absent', 'present'], default='present'), - ) - lldp_obj = Interface_mdn(argument_spec) - lldp_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_mlag_config.py b/plugins/modules/network/cloudengine/ce_mlag_config.py deleted file mode 100644 index 1e40ae94a7..0000000000 --- a/plugins/modules/network/cloudengine/ce_mlag_config.py +++ /dev/null @@ -1,916 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_mlag_config -short_description: Manages MLAG configuration on HUAWEI CloudEngine switches. -description: - - Manages MLAG configuration on HUAWEI CloudEngine switches. -author: - - Li Yanfeng (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - dfs_group_id: - description: - - ID of a DFS group. The value is 1. - default: present - nickname: - description: - - The nickname bound to a DFS group. The value is an integer that ranges from 1 to 65471. - pseudo_nickname: - description: - - A pseudo nickname of a DFS group. The value is an integer that ranges from 1 to 65471. - pseudo_priority: - description: - - The priority of a pseudo nickname. The value is an integer that ranges from 128 to 255. - The default value is 192. A larger value indicates a higher priority. - ip_address: - description: - - IP address bound to the DFS group. The value is in dotted decimal notation. - vpn_instance_name: - description: - - Name of the VPN instance bound to the DFS group. The value is a string of 1 to 31 case-sensitive - characters without spaces. If the character string is quoted by double quotation marks, the character - string can contain spaces. The value _public_ is reserved and cannot be used as the VPN instance name. - priority_id: - description: - - Priority of a DFS group. The value is an integer that ranges from 1 to 254. The default value is 100. - eth_trunk_id: - description: - - Name of the peer-link interface. The value is in the range from 0 to 511. - peer_link_id: - description: - - Number of the peer-link interface. The value is 1. - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: mlag config module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Create DFS Group id - ce_mlag_config: - dfs_group_id: 1 - provider: "{{ cli }}" - - name: Set dfs-group priority - ce_mlag_config: - dfs_group_id: 1 - priority_id: 3 - state: present - provider: "{{ cli }}" - - name: Set pseudo nickname - ce_mlag_config: - dfs_group_id: 1 - pseudo_nickname: 3 - pseudo_priority: 130 - state: present - provider: "{{ cli }}" - - name: Set ip - ce_mlag_config: - dfs_group_id: 1 - ip_address: 11.1.1.2 - vpn_instance_name: 6 - provider: "{{ cli }}" - - name: Set peer link - ce_mlag_config: - eth_trunk_id: 3 - peer_link_id: 2 - state: present - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { "eth_trunk_id": "3", - "peer_link_id": "1", - "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: { } -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: { "eth_trunk_id": "Eth-Trunk3", - "peer_link_id": "1"} -updates: - description: command sent to the device - returned: always - type: list - sample: {"peer-link 1"} -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_DFS_GROUP_INFO = """ - - - - - - - - - - - - - - - - - -""" -CE_NC_GET_PEER_LINK_INFO = """ - - - - - - - - - - - -""" - -CE_NC_CREATE_DFS_GROUP_INFO_HEADER = """ - - - - - %s -""" - -CE_NC_CREATE_DFS_GROUP_INFO_TAIL = """ - - - - -""" - -CE_NC_MERGE_DFS_GROUP_INFO_HEADER = """ - - - - - %s -""" - -CE_NC_MERGE_DFS_GROUP_INFO_TAIL = """ - - - - -""" - -CE_NC_DELETE_DFS_GROUP_ATTRIBUTE_HEADER = """ - - - - - %s -""" - -CE_NC_DELETE_DFS_GROUP_ATTRIBUTE_TAIL = """ - - - - -""" - -CE_NC_DELETE_DFS_GROUP_INFO_HEADER = """ - - - - - %s -""" - -CE_NC_DELETE_DFS_GROUP_INFO_TAIL = """ - - - - -""" - -CE_NC_CREATE_PEER_LINK_INFO = """ - - - - - 1 - %s - %s - - - - -""" - -CE_NC_MERGE_PEER_LINK_INFO = """ - - - - - 1 - %s - %s - - - - -""" -CE_NC_DELETE_PEER_LINK_INFO = """ - - - - - 1 - %s - %s - - - - -""" - - -def is_valid_address(address): - """check ip-address is valid""" - - if address.find('.') != -1: - addr_list = address.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - -class MlagConfig(object): - """ - Manages Manages MLAG config information. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.dfs_group_id = self.module.params['dfs_group_id'] - self.nickname = self.module.params['nickname'] - self.pseudo_nickname = self.module.params['pseudo_nickname'] - self.pseudo_priority = self.module.params['pseudo_priority'] - self.ip_address = self.module.params['ip_address'] - self.vpn_instance_name = self.module.params['vpn_instance_name'] - self.priority_id = self.module.params['priority_id'] - self.eth_trunk_id = self.module.params['eth_trunk_id'] - self.peer_link_id = self.module.params['peer_link_id'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.existing = dict() - self.proposed = dict() - self.end_state = dict() - - self.commands = list() - # DFS group info - self.dfs_group_info = None - # peer link info - self.peer_link_info = None - - def init_module(self): - """ init module """ - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, con_obj, xml_name): - """Check if response message is already succeed.""" - - xml_str = con_obj.xml - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_dfs_group_info(self): - """ get dfs group attributes info.""" - - dfs_group_info = dict() - conf_str = CE_NC_GET_DFS_GROUP_INFO - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return dfs_group_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - dfs_info = root.findall( - "dfs/groupInstances/groupInstance") - if dfs_info: - for tmp in dfs_info: - for site in tmp: - if site.tag in ["groupId", "priority", "ipAddress", "srcVpnName"]: - dfs_group_info[site.tag] = site.text - - dfs_nick_info = root.findall( - "dfs/groupInstances/groupInstance/trillType") - - if dfs_nick_info: - for tmp in dfs_nick_info: - for site in tmp: - if site.tag in ["localNickname", "pseudoNickname", "pseudoPriority"]: - dfs_group_info[site.tag] = site.text - return dfs_group_info - - def get_peer_link_info(self): - """ get peer link info.""" - - peer_link_info = dict() - conf_str = CE_NC_GET_PEER_LINK_INFO - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return peer_link_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - link_info = root.findall( - "mlag/peerlinks/peerlink") - if link_info: - for tmp in link_info: - for site in tmp: - if site.tag in ["linkId", "portName"]: - peer_link_info[site.tag] = site.text - return peer_link_info - - def is_dfs_group_info_change(self): - """whether dfs group info""" - if not self.dfs_group_info: - return False - - if self.priority_id and self.dfs_group_info["priority"] != self.priority_id: - return True - if self.ip_address and self.dfs_group_info["ipAddress"] != self.ip_address: - return True - if self.vpn_instance_name and self.dfs_group_info["srcVpnName"] != self.vpn_instance_name: - return True - if self.nickname and self.dfs_group_info["localNickname"] != self.nickname: - return True - if self.pseudo_nickname and self.dfs_group_info["pseudoNickname"] != self.pseudo_nickname: - return True - if self.pseudo_priority and self.dfs_group_info["pseudoPriority"] != self.pseudo_priority: - return True - return False - - def check_dfs_group_info_change(self): - """check dfs group info""" - if not self.dfs_group_info: - return True - - if self.priority_id and self.dfs_group_info["priority"] == self.priority_id: - return True - if self.ip_address and self.dfs_group_info["ipAddress"] == self.ip_address: - return True - if self.vpn_instance_name and self.dfs_group_info["srcVpnName"] == self.vpn_instance_name: - return True - if self.nickname and self.dfs_group_info["localNickname"] == self.nickname: - return True - if self.pseudo_nickname and self.dfs_group_info["pseudoNickname"] == self.pseudo_nickname: - return True - if self.pseudo_priority and self.dfs_group_info["pseudoPriority"] == self.pseudo_priority: - return True - return False - - def modify_dfs_group(self): - """modify dfs group info""" - - if self.is_dfs_group_info_change(): - - conf_str = CE_NC_MERGE_DFS_GROUP_INFO_HEADER % self.dfs_group_id - if self.priority_id and self.dfs_group_info["priority"] != self.priority_id: - conf_str += "%s" % self.priority_id - if self.ip_address and self.dfs_group_info["ipAddress"] != self.ip_address: - conf_str += "%s" % self.ip_address - if self.vpn_instance_name and self.dfs_group_info["srcVpnName"] != self.vpn_instance_name: - if not self.ip_address: - self.module.fail_json( - msg='Error: ip_address can not be null if vpn_instance_name is exist.') - conf_str += "%s" % self.vpn_instance_name - - if self.nickname or self.pseudo_nickname or self.pseudo_priority: - conf_str += "" - if self.nickname and self.dfs_group_info["localNickname"] != self.nickname: - conf_str += "%s" % self.nickname - if self.pseudo_nickname and self.dfs_group_info["pseudoNickname"] != self.pseudo_nickname: - conf_str += "%s" % self.pseudo_nickname - - if self.pseudo_priority and self.dfs_group_info["pseudoPriority"] != self.pseudo_priority: - if not self.pseudo_nickname: - self.module.fail_json( - msg='Error: pseudo_nickname can not be null if pseudo_priority is exist.') - conf_str += "%s" % self.pseudo_priority - conf_str += "" - - conf_str += CE_NC_MERGE_DFS_GROUP_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge DFS group info failed.') - - self.updates_cmd.append("dfs-group 1") - if self.priority_id: - self.updates_cmd.append("priority %s" % self.priority_id) - if self.ip_address: - if self.vpn_instance_name: - self.updates_cmd.append( - "source ip %s vpn-instance %s" % (self.ip_address, self.vpn_instance_name)) - else: - self.updates_cmd.append("source ip %s" % self.ip_address) - if self.nickname: - self.updates_cmd.append("source nickname %s" % self.nickname) - if self.pseudo_nickname: - if self.pseudo_priority: - self.updates_cmd.append( - "pseudo-nickname %s priority %s" % (self.pseudo_nickname, self.pseudo_priority)) - else: - self.updates_cmd.append( - "pseudo-nickname %s" % self.pseudo_nickname) - - self.changed = True - - def create_dfs_group(self): - """create dfs group info""" - - conf_str = CE_NC_CREATE_DFS_GROUP_INFO_HEADER % self.dfs_group_id - if self.priority_id and self.priority_id != 100: - conf_str += "%s" % self.priority_id - if self.ip_address: - conf_str += "%s" % self.ip_address - if self.vpn_instance_name: - if not self.ip_address: - self.module.fail_json( - msg='Error: ip_address can not be null if vpn_instance_name is exist.') - conf_str += "%s" % self.vpn_instance_name - - if self.nickname or self.pseudo_nickname or self.pseudo_priority: - conf_str += "" - if self.nickname: - conf_str += "%s" % self.nickname - if self.pseudo_nickname: - conf_str += "%s" % self.pseudo_nickname - if self.pseudo_priority: - if not self.pseudo_nickname: - self.module.fail_json( - msg='Error: pseudo_nickname can not be null if pseudo_priority is exist.') - conf_str += "%s" % self.pseudo_priority - conf_str += "" - - conf_str += CE_NC_CREATE_DFS_GROUP_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge DFS group info failed.') - - self.updates_cmd.append("dfs-group 1") - if self.priority_id: - self.updates_cmd.append("priority %s" % self.priority_id) - if self.ip_address: - if self.vpn_instance_name: - self.updates_cmd.append( - "source ip %s vpn-instance %s" % (self.ip_address, self.vpn_instance_name)) - else: - self.updates_cmd.append("source ip %s" % self.ip_address) - if self.nickname: - self.updates_cmd.append("source nickname %s" % self.nickname) - if self.pseudo_nickname: - if self.pseudo_priority: - self.updates_cmd.append( - "pseudo-nickname %s priority %s" % (self.pseudo_nickname, self.pseudo_priority)) - else: - self.updates_cmd.append( - "pseudo-nickname %s" % self.pseudo_nickname) - - self.changed = True - - def delete_dfs_group(self): - """delete dfg group""" - - conf_str = CE_NC_DELETE_DFS_GROUP_INFO_HEADER % self.dfs_group_id - conf_str += CE_NC_DELETE_DFS_GROUP_INFO_TAIL - - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Delete DFS group id failed.') - self.updates_cmd.append("undo dfs-group 1") - self.changed = True - - def delete_dfs_group_attribute(self): - """delete dfg group attribute info""" - - conf_str = CE_NC_DELETE_DFS_GROUP_ATTRIBUTE_HEADER % self.dfs_group_id - change = False - if self.priority_id and self.dfs_group_info["priority"] == self.priority_id: - conf_str += "%s" % self.priority_id - change = True - self.updates_cmd.append("undo priority %s" % self.priority_id) - if self.ip_address and self.dfs_group_info["ipAddress"] == self.ip_address: - if self.vpn_instance_name and self.dfs_group_info["srcVpnName"] == self.vpn_instance_name: - conf_str += "%s" % self.ip_address - conf_str += "%s" % self.vpn_instance_name - self.updates_cmd.append( - "undo source ip %s vpn-instance %s" % (self.ip_address, self.vpn_instance_name)) - else: - conf_str += "%s" % self.ip_address - self.updates_cmd.append("undo source ip %s" % self.ip_address) - change = True - - conf_str += CE_NC_DELETE_DFS_GROUP_ATTRIBUTE_TAIL - - if change: - self.updates_cmd.append("undo dfs-group 1") - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Delete DFS group attribute failed.') - self.changed = True - - def delete_dfs_group_nick(self): - - conf_str = CE_NC_DELETE_DFS_GROUP_ATTRIBUTE_HEADER % self.dfs_group_id - conf_str = conf_str.replace('', '') - change = False - - if self.nickname or self.pseudo_nickname: - conf_str += "" - if self.nickname and self.dfs_group_info["localNickname"] == self.nickname: - conf_str += "%s" % self.nickname - change = True - self.updates_cmd.append("undo source nickname %s" % self.nickname) - if self.pseudo_nickname and self.dfs_group_info["pseudoNickname"] == self.pseudo_nickname: - conf_str += "%s" % self.pseudo_nickname - if self.pseudo_priority and self.dfs_group_info["pseudoPriority"] == self.pseudo_priority: - self.updates_cmd.append( - "undo pseudo-nickname %s priority %s" % (self.pseudo_nickname, self.pseudo_priority)) - if not self.pseudo_priority: - self.updates_cmd.append( - "undo pseudo-nickname %s" % self.pseudo_nickname) - change = True - conf_str += "" - - conf_str += CE_NC_DELETE_DFS_GROUP_ATTRIBUTE_TAIL - - if change: - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Delete DFS group attribute failed.') - self.changed = True - - def modify_peer_link(self): - """modify peer link info""" - - eth_trunk_id = "Eth-Trunk" - eth_trunk_id += self.eth_trunk_id - if self.eth_trunk_id and eth_trunk_id != self.peer_link_info.get("portName"): - conf_str = CE_NC_MERGE_PEER_LINK_INFO % ( - self.peer_link_id, eth_trunk_id) - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Merge peer link failed.') - self.updates_cmd.append("peer-link %s" % self.peer_link_id) - self.changed = True - - def delete_peer_link(self): - """delete peer link info""" - - eth_trunk_id = "Eth-Trunk" - eth_trunk_id += self.eth_trunk_id - if self.eth_trunk_id and eth_trunk_id == self.peer_link_info.get("portName"): - conf_str = CE_NC_DELETE_PEER_LINK_INFO % ( - self.peer_link_id, eth_trunk_id) - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: Delete peer link failed.') - self.updates_cmd.append("undo peer-link %s" % self.peer_link_id) - self.changed = True - - def check_params(self): - """Check all input params""" - - # dfs_group_id check - if self.dfs_group_id: - if self.dfs_group_id != "1": - self.module.fail_json( - msg='Error: The value of dfs_group_id must be 1.') - - # nickname check - if self.nickname: - if not self.nickname.isdigit(): - self.module.fail_json( - msg='Error: The value of nickname is an integer.') - if int(self.nickname) < 1 or int(self.nickname) > 65471: - self.module.fail_json( - msg='Error: The nickname is not in the range from 1 to 65471.') - - # pseudo_nickname check - if self.pseudo_nickname: - if not self.pseudo_nickname.isdigit(): - self.module.fail_json( - msg='Error: The value of pseudo_nickname is an integer.') - if int(self.pseudo_nickname) < 1 or int(self.pseudo_nickname) > 65471: - self.module.fail_json( - msg='Error: The pseudo_nickname is not in the range from 1 to 65471.') - - # pseudo_priority check - if self.pseudo_priority: - if not self.pseudo_priority.isdigit(): - self.module.fail_json( - msg='Error: The value of pseudo_priority is an integer.') - if int(self.pseudo_priority) < 128 or int(self.pseudo_priority) > 255: - self.module.fail_json( - msg='Error: The pseudo_priority is not in the range from 128 to 255.') - - # ip_address check - if self.ip_address: - if not is_valid_address(self.ip_address): - self.module.fail_json( - msg='Error: The %s is not a valid ip address.' % self.ip_address) - - # vpn_instance_name check - if self.vpn_instance_name: - if len(self.vpn_instance_name) > 31 \ - or len(self.vpn_instance_name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: The length of vpn_instance_name is not in the range from 1 to 31.') - - # priority_id check - if self.priority_id: - if not self.priority_id.isdigit(): - self.module.fail_json( - msg='Error: The value of priority_id is an integer.') - if int(self.priority_id) < 1 or int(self.priority_id) > 254: - self.module.fail_json( - msg='Error: The priority_id is not in the range from 1 to 254.') - - # peer_link_id check - if self.peer_link_id: - if self.peer_link_id != "1": - self.module.fail_json( - msg='Error: The value of peer_link_id must be 1.') - - # eth_trunk_id check - if self.eth_trunk_id: - if not self.eth_trunk_id.isdigit(): - self.module.fail_json( - msg='Error: The value of eth_trunk_id is an integer.') - if int(self.eth_trunk_id) < 0 or int(self.eth_trunk_id) > 511: - self.module.fail_json( - msg='Error: The value of eth_trunk_id is not in the range from 0 to 511.') - - def get_proposed(self): - """get proposed info""" - - if self.dfs_group_id: - self.proposed["dfs_group_id"] = self.dfs_group_id - if self.nickname: - self.proposed["nickname"] = self.nickname - if self.pseudo_nickname: - self.proposed["pseudo_nickname"] = self.pseudo_nickname - if self.pseudo_priority: - self.proposed["pseudo_priority"] = self.pseudo_priority - if self.ip_address: - self.proposed["ip_address"] = self.ip_address - if self.vpn_instance_name: - self.proposed["vpn_instance_name"] = self.vpn_instance_name - if self.priority_id: - self.proposed["priority_id"] = self.priority_id - if self.eth_trunk_id: - self.proposed["eth_trunk_id"] = self.eth_trunk_id - if self.peer_link_id: - self.proposed["peer_link_id"] = self.peer_link_id - if self.state: - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - if self.dfs_group_id: - self.dfs_group_info = self.get_dfs_group_info() - if self.peer_link_id and self.eth_trunk_id: - self.peer_link_info = self.get_peer_link_info() - if self.dfs_group_info: - if self.dfs_group_id: - self.existing["dfs_group_id"] = self.dfs_group_info["groupId"] - if self.nickname: - self.existing["nickname"] = self.dfs_group_info[ - "localNickname"] - if self.pseudo_nickname: - self.existing["pseudo_nickname"] = self.dfs_group_info[ - "pseudoNickname"] - if self.pseudo_priority: - self.existing["pseudo_priority"] = self.dfs_group_info[ - "pseudoPriority"] - if self.ip_address: - self.existing["ip_address"] = self.dfs_group_info["ipAddress"] - if self.vpn_instance_name: - self.existing["vpn_instance_name"] = self.dfs_group_info[ - "srcVpnName"] - if self.priority_id: - self.existing["priority_id"] = self.dfs_group_info["priority"] - if self.peer_link_info: - if self.eth_trunk_id: - self.existing["eth_trunk_id"] = self.peer_link_info["portName"] - if self.peer_link_id: - self.existing["peer_link_id"] = self.peer_link_info["linkId"] - - def get_end_state(self): - """get end state info""" - if self.dfs_group_id: - self.dfs_group_info = self.get_dfs_group_info() - if self.peer_link_id and self.eth_trunk_id: - self.peer_link_info = self.get_peer_link_info() - - if self.dfs_group_info: - if self.dfs_group_id: - self.end_state["dfs_group_id"] = self.dfs_group_info["groupId"] - if self.nickname: - self.end_state["nickname"] = self.dfs_group_info[ - "localNickname"] - if self.pseudo_nickname: - self.end_state["pseudo_nickname"] = self.dfs_group_info[ - "pseudoNickname"] - if self.pseudo_priority: - self.end_state["pseudo_priority"] = self.dfs_group_info[ - "pseudoPriority"] - if self.ip_address: - self.end_state["ip_address"] = self.dfs_group_info["ipAddress"] - if self.vpn_instance_name: - self.end_state["vpn_instance_name"] = self.dfs_group_info[ - "srcVpnName"] - if self.priority_id: - self.end_state["priority_id"] = self.dfs_group_info["priority"] - if self.peer_link_info: - if self.eth_trunk_id: - self.end_state[ - "eth_trunk_id"] = self.peer_link_info["portName"] - if self.peer_link_id: - self.end_state["peer_link_id"] = self.peer_link_info["linkId"] - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.get_existing() - self.get_proposed() - if self.dfs_group_id: - if self.state == "present": - if self.dfs_group_info: - if self.nickname or self.pseudo_nickname or self.pseudo_priority or self.priority_id \ - or self.ip_address or self.vpn_instance_name: - if self.nickname: - if self.dfs_group_info["ipAddress"] not in ["0.0.0.0", None]: - self.module.fail_json(msg='Error: nickname and ip_address can not be exist at the ' - 'same time.') - if self.ip_address: - if self.dfs_group_info["localNickname"] not in ["0", None]: - self.module.fail_json(msg='Error: nickname and ip_address can not be exist at the ' - 'same time.') - self.modify_dfs_group() - else: - self.create_dfs_group() - else: - if not self.dfs_group_info: - self.module.fail_json( - msg='Error: DFS Group does not exist.') - if not self.nickname and not self.pseudo_nickname and not self.pseudo_priority and not self.priority_id\ - and not self.ip_address and not self.vpn_instance_name: - self.delete_dfs_group() - else: - self.updates_cmd.append("dfs-group 1") - self.delete_dfs_group_attribute() - self.delete_dfs_group_nick() - if "undo dfs-group 1" in self.updates_cmd: - self.updates_cmd = ["undo dfs-group 1"] - - if self.eth_trunk_id and not self.peer_link_id: - self.module.fail_json( - msg='Error: eth_trunk_id and peer_link_id must be config at the same time.') - if self.peer_link_id and not self.eth_trunk_id: - self.module.fail_json( - msg='Error: eth_trunk_id and peer_link_id must be config at the same time.') - - if self.eth_trunk_id and self.peer_link_id: - if self.state == "present": - self.modify_peer_link() - else: - if self.peer_link_info: - self.delete_peer_link() - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - dfs_group_id=dict(type='str'), - nickname=dict(type='str'), - pseudo_nickname=dict(type='str'), - pseudo_priority=dict(type='str'), - ip_address=dict(type='str'), - vpn_instance_name=dict(type='str'), - priority_id=dict(type='str'), - eth_trunk_id=dict(type='str'), - peer_link_id=dict(type='str'), - state=dict(type='str', default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = MlagConfig(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_mlag_interface.py b/plugins/modules/network/cloudengine/ce_mlag_interface.py deleted file mode 100644 index b5ec70554e..0000000000 --- a/plugins/modules/network/cloudengine/ce_mlag_interface.py +++ /dev/null @@ -1,1042 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_mlag_interface -short_description: Manages MLAG interfaces on HUAWEI CloudEngine switches. -description: - - Manages MLAG interface attributes on HUAWEI CloudEngine switches. -author: - - Li Yanfeng (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - eth_trunk_id: - description: - - Name of the local M-LAG interface. The value is ranging from 0 to 511. - dfs_group_id: - description: - - ID of a DFS group.The value is 1. - default: present - mlag_id: - description: - - ID of the M-LAG. The value is an integer that ranges from 1 to 2048. - mlag_system_id: - description: - - M-LAG global LACP system MAC address. The value is a string of 0 to 255 characters. The default value - is the MAC address of the Ethernet port of MPU. - mlag_priority_id: - description: - - M-LAG global LACP system priority. The value is an integer ranging from 0 to 65535. - The default value is 32768. - interface: - description: - - Name of the interface that enters the Error-Down state when the peer-link fails. - The value is a string of 1 to 63 characters. - mlag_error_down: - description: - - Configure the interface on the slave device to enter the Error-Down state. - choices: ['enable','disable'] - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] - -''' - -EXAMPLES = ''' -- name: mlag interface module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Set interface mlag error down - ce_mlag_interface: - interface: 10GE2/0/1 - mlag_error_down: enable - provider: "{{ cli }}" - - name: Create mlag - ce_mlag_interface: - eth_trunk_id: 1 - dfs_group_id: 1 - mlag_id: 4 - provider: "{{ cli }}" - - name: Set mlag global attribute - ce_mlag_interface: - mlag_system_id: 0020-1409-0407 - mlag_priority_id: 5 - provider: "{{ cli }}" - - name: Set mlag interface attribute - ce_mlag_interface: - eth_trunk_id: 1 - mlag_system_id: 0020-1409-0400 - mlag_priority_id: 3 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { "interface": "eth-trunk1", - "mlag_error_down": "disable", - "state": "present" - } -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: { "mlagErrorDownInfos": [ - { - "dfsgroupId": "1", - "portName": "Eth-Trunk1" - } - ] - } -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {} -updates: - description: command sent to the device - returned: always - type: list - sample: { "interface eth-trunk1", - "undo m-lag unpaired-port suspend"} -''' - -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_MLAG_INFO = """ - - - - - %s - - - - -""" - -CE_NC_CREATE_MLAG_INFO = """ - - - - - %s - %s - %s - - - - -""" - -CE_NC_DELETE_MLAG_INFO = """ - - - - - %s - %s - - - - -""" - -CE_NC_GET_LACP_MLAG_INFO = """ - - - - - %s - - - - - - - - -""" - -CE_NC_SET_LACP_MLAG_INFO_HEAD = """ - - - - - %s - -""" - -CE_NC_SET_LACP_MLAG_INFO_TAIL = """ - - - - - -""" - -CE_NC_GET_GLOBAL_LACP_MLAG_INFO = """ - - - - - - - - - - -""" - -CE_NC_SET_GLOBAL_LACP_MLAG_INFO_HEAD = """ - - - - -""" - -CE_NC_SET_GLOBAL_LACP_MLAG_INFO_TAIL = """ - - - - -""" - -CE_NC_GET_MLAG_ERROR_DOWN_INFO = """ - - - - - - - - - - - - -""" - -CE_NC_CREATE_MLAG_ERROR_DOWN_INFO = """ - - - - - 1 - %s - - - - -""" - -CE_NC_DELETE_MLAG_ERROR_DOWN_INFO = """ - - - - - 1 - %s - - - - - -""" - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class MlagInterface(object): - """ - Manages Manages MLAG interface information. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.eth_trunk_id = self.module.params['eth_trunk_id'] - self.dfs_group_id = self.module.params['dfs_group_id'] - self.mlag_id = self.module.params['mlag_id'] - self.mlag_system_id = self.module.params['mlag_system_id'] - self.mlag_priority_id = self.module.params['mlag_priority_id'] - self.interface = self.module.params['interface'] - self.mlag_error_down = self.module.params['mlag_error_down'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.existing = dict() - self.proposed = dict() - self.end_state = dict() - - # mlag info - self.commands = list() - self.mlag_info = None - self.mlag_global_info = None - self.mlag_error_down_info = None - self.mlag_trunk_attribute_info = None - - def init_module(self): - """ init module """ - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) # show updates result - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def get_mlag_info(self): - """ get mlag info.""" - - mlag_info = dict() - conf_str = CE_NC_GET_MLAG_INFO % ("Eth-Trunk%s" % self.eth_trunk_id) - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return mlag_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - mlag_info["mlagInfos"] = list() - root = ElementTree.fromstring(xml_str) - dfs_mlag_infos = root.findall( - "./mlag/mlagInstances/mlagInstance") - - if dfs_mlag_infos: - for dfs_mlag_info in dfs_mlag_infos: - mlag_dict = dict() - for ele in dfs_mlag_info: - if ele.tag in ["dfsgroupId", "mlagId", "localMlagPort"]: - mlag_dict[ele.tag] = ele.text - mlag_info["mlagInfos"].append(mlag_dict) - return mlag_info - - def get_mlag_global_info(self): - """ get mlag global info.""" - - mlag_global_info = dict() - conf_str = CE_NC_GET_GLOBAL_LACP_MLAG_INFO - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return mlag_global_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - global_info = root.findall( - "./ifmtrunk/lacpSysInfo/lacpMlagGlobal") - - if global_info: - for tmp in global_info: - for site in tmp: - if site.tag in ["lacpMlagSysId", "lacpMlagPriority"]: - mlag_global_info[site.tag] = site.text - return mlag_global_info - - def get_mlag_trunk_attribute_info(self): - """ get mlag global info.""" - - mlag_trunk_attribute_info = dict() - eth_trunk = "Eth-Trunk" - eth_trunk += self.eth_trunk_id - conf_str = CE_NC_GET_LACP_MLAG_INFO % eth_trunk - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return mlag_trunk_attribute_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - global_info = root.findall( - "./ifmtrunk/TrunkIfs/TrunkIf/lacpMlagIf") - - if global_info: - for tmp in global_info: - for site in tmp: - if site.tag in ["lacpMlagSysId", "lacpMlagPriority"]: - mlag_trunk_attribute_info[site.tag] = site.text - return mlag_trunk_attribute_info - - def get_mlag_error_down_info(self): - """ get error down info.""" - - mlag_error_down_info = dict() - conf_str = CE_NC_GET_MLAG_ERROR_DOWN_INFO - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return mlag_error_down_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - mlag_error_down_info["mlagErrorDownInfos"] = list() - root = ElementTree.fromstring(xml_str) - mlag_error_infos = root.findall( - "./mlag/errordowns/errordown") - - if mlag_error_infos: - for mlag_error_info in mlag_error_infos: - mlag_error_dict = dict() - for ele in mlag_error_info: - if ele.tag in ["dfsgroupId", "portName"]: - mlag_error_dict[ele.tag] = ele.text - mlag_error_down_info[ - "mlagErrorDownInfos"].append(mlag_error_dict) - return mlag_error_down_info - - def check_macaddr(self): - """check mac-address whether valid""" - - valid_char = '0123456789abcdef-' - mac = self.mlag_system_id - - if len(mac) > 16: - return False - - mac_list = re.findall(r'([0-9a-fA-F]+)', mac) - if len(mac_list) != 3: - return False - - if mac.count('-') != 2: - return False - - for _, value in enumerate(mac, start=0): - if value.lower() not in valid_char: - return False - if all((int(mac_list[0], base=16) == 0, int(mac_list[1], base=16) == 0, int(mac_list[2], base=16) == 0)): - return False - a = "000" + mac_list[0] - b = "000" + mac_list[1] - c = "000" + mac_list[2] - self.mlag_system_id = "-".join([a[-4:], b[-4:], c[-4:]]) - return True - - def check_params(self): - """Check all input params""" - - # eth_trunk_id check - if self.eth_trunk_id: - if not self.eth_trunk_id.isdigit(): - self.module.fail_json( - msg='Error: The value of eth_trunk_id is an integer.') - if int(self.eth_trunk_id) < 0 or int(self.eth_trunk_id) > 511: - self.module.fail_json( - msg='Error: The value of eth_trunk_id is not in the range from 0 to 511.') - - # dfs_group_id check - if self.dfs_group_id: - if self.dfs_group_id != "1": - self.module.fail_json( - msg='Error: The value of dfs_group_id must be 1.') - - # mlag_id check - if self.mlag_id: - if not self.mlag_id.isdigit(): - self.module.fail_json( - msg='Error: The value of mlag_id is an integer.') - if int(self.mlag_id) < 1 or int(self.mlag_id) > 2048: - self.module.fail_json( - msg='Error: The value of mlag_id is not in the range from 1 to 2048.') - - # mlag_system_id check - if self.mlag_system_id: - if not self.check_macaddr(): - self.module.fail_json( - msg="Error: mlag_system_id has invalid value %s." % self.mlag_system_id) - - # mlag_priority_id check - if self.mlag_priority_id: - if not self.mlag_priority_id.isdigit(): - self.module.fail_json( - msg='Error: The value of mlag_priority_id is an integer.') - if int(self.mlag_priority_id) < 0 or int(self.mlag_priority_id) > 254: - self.module.fail_json( - msg='Error: The value of mlag_priority_id is not in the range from 0 to 254.') - - # interface check - if self.interface: - intf_type = get_interface_type(self.interface) - if not intf_type: - self.module.fail_json( - msg='Error: Interface name of %s ' - 'is error.' % self.interface) - - def is_mlag_info_change(self): - """whether mlag info change""" - - if not self.mlag_info: - return True - - eth_trunk = "Eth-Trunk" - eth_trunk += self.eth_trunk_id - for info in self.mlag_info["mlagInfos"]: - if info["mlagId"] == self.mlag_id and info["localMlagPort"] == eth_trunk: - return False - return True - - def is_mlag_info_exist(self): - """whether mlag info exist""" - - if not self.mlag_info: - return False - - eth_trunk = "Eth-Trunk" - eth_trunk += self.eth_trunk_id - - for info in self.mlag_info["mlagInfos"]: - if info["localMlagPort"] == eth_trunk: - return True - return False - - def is_mlag_error_down_info_change(self): - """whether mlag error down info change""" - - if not self.mlag_error_down_info: - return True - - for info in self.mlag_error_down_info["mlagErrorDownInfos"]: - if info["portName"].upper() == self.interface.upper(): - return False - return True - - def is_mlag_error_down_info_exist(self): - """whether mlag error down info exist""" - - if not self.mlag_error_down_info: - return False - - for info in self.mlag_error_down_info["mlagErrorDownInfos"]: - if info["portName"].upper() == self.interface.upper(): - return True - return False - - def is_mlag_interface_info_change(self): - """whether mlag interface attribute info change""" - - if not self.mlag_trunk_attribute_info: - return True - - if self.mlag_system_id: - if self.mlag_trunk_attribute_info["lacpMlagSysId"] != self.mlag_system_id: - return True - if self.mlag_priority_id: - if self.mlag_trunk_attribute_info["lacpMlagPriority"] != self.mlag_priority_id: - return True - return False - - def is_mlag_interface_info_exist(self): - """whether mlag interface attribute info exist""" - - if not self.mlag_trunk_attribute_info: - return False - - if self.mlag_system_id: - if self.mlag_priority_id: - if self.mlag_trunk_attribute_info["lacpMlagSysId"] == self.mlag_system_id \ - and self.mlag_trunk_attribute_info["lacpMlagPriority"] == self.mlag_priority_id: - return True - else: - if self.mlag_trunk_attribute_info["lacpMlagSysId"] == self.mlag_system_id: - return True - - if self.mlag_priority_id: - if self.mlag_system_id: - if self.mlag_trunk_attribute_info["lacpMlagSysId"] == self.mlag_system_id \ - and self.mlag_trunk_attribute_info["lacpMlagPriority"] == self.mlag_priority_id: - return True - else: - if self.mlag_trunk_attribute_info["lacpMlagPriority"] == self.mlag_priority_id: - return True - - return False - - def is_mlag_global_info_change(self): - """whether mlag global attribute info change""" - - if not self.mlag_global_info: - return True - - if self.mlag_system_id: - if self.mlag_global_info["lacpMlagSysId"] != self.mlag_system_id: - return True - if self.mlag_priority_id: - if self.mlag_global_info["lacpMlagPriority"] != self.mlag_priority_id: - return True - return False - - def is_mlag_global_info_exist(self): - """whether mlag global attribute info exist""" - - if not self.mlag_global_info: - return False - - if self.mlag_system_id: - if self.mlag_priority_id: - if self.mlag_global_info["lacpMlagSysId"] == self.mlag_system_id \ - and self.mlag_global_info["lacpMlagPriority"] == self.mlag_priority_id: - return True - else: - if self.mlag_global_info["lacpMlagSysId"] == self.mlag_system_id: - return True - - if self.mlag_priority_id: - if self.mlag_system_id: - if self.mlag_global_info["lacpMlagSysId"] == self.mlag_system_id \ - and self.mlag_global_info["lacpMlagPriority"] == self.mlag_priority_id: - return True - else: - if self.mlag_global_info["lacpMlagPriority"] == self.mlag_priority_id: - return True - - return False - - def create_mlag(self): - """create mlag info""" - - if self.is_mlag_info_change(): - mlag_port = "Eth-Trunk" - mlag_port += self.eth_trunk_id - conf_str = CE_NC_CREATE_MLAG_INFO % ( - self.dfs_group_id, self.mlag_id, mlag_port) - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: create mlag info failed.') - - self.updates_cmd.append("interface %s" % mlag_port) - self.updates_cmd.append("dfs-group %s m-lag %s" % - (self.dfs_group_id, self.mlag_id)) - self.changed = True - - def delete_mlag(self): - """delete mlag info""" - - if self.is_mlag_info_exist(): - mlag_port = "Eth-Trunk" - mlag_port += self.eth_trunk_id - conf_str = CE_NC_DELETE_MLAG_INFO % ( - self.dfs_group_id, mlag_port) - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: delete mlag info failed.') - - self.updates_cmd.append("interface %s" % mlag_port) - self.updates_cmd.append( - "undo dfs-group %s m-lag %s" % (self.dfs_group_id, self.mlag_id)) - self.changed = True - - def create_mlag_error_down(self): - """create mlag error down info""" - - if self.is_mlag_error_down_info_change(): - conf_str = CE_NC_CREATE_MLAG_ERROR_DOWN_INFO % self.interface - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: create mlag error down info failed.') - - self.updates_cmd.append("interface %s" % self.interface) - self.updates_cmd.append("m-lag unpaired-port suspend") - self.changed = True - - def delete_mlag_error_down(self): - """delete mlag error down info""" - - if self.is_mlag_error_down_info_exist(): - - conf_str = CE_NC_DELETE_MLAG_ERROR_DOWN_INFO % self.interface - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: delete mlag error down info failed.') - - self.updates_cmd.append("interface %s" % self.interface) - self.updates_cmd.append("undo m-lag unpaired-port suspend") - self.changed = True - - def set_mlag_interface(self): - """set mlag interface attribute info""" - - if self.is_mlag_interface_info_change(): - mlag_port = "Eth-Trunk" - mlag_port += self.eth_trunk_id - conf_str = CE_NC_SET_LACP_MLAG_INFO_HEAD % mlag_port - if self.mlag_priority_id: - conf_str += "%s" % self.mlag_priority_id - if self.mlag_system_id: - conf_str += "%s" % self.mlag_system_id - conf_str += CE_NC_SET_LACP_MLAG_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: set mlag interface attribute info failed.') - - self.updates_cmd.append("interface %s" % mlag_port) - if self.mlag_priority_id: - self.updates_cmd.append( - "lacp m-lag priority %s" % self.mlag_priority_id) - - if self.mlag_system_id: - self.updates_cmd.append( - "lacp m-lag system-id %s" % self.mlag_system_id) - self.changed = True - - def delete_mlag_interface(self): - """delete mlag interface attribute info""" - - if self.is_mlag_interface_info_exist(): - mlag_port = "Eth-Trunk" - mlag_port += self.eth_trunk_id - conf_str = CE_NC_SET_LACP_MLAG_INFO_HEAD % mlag_port - cmd = "interface %s" % mlag_port - self.cli_add_command(cmd) - - if self.mlag_priority_id: - cmd = "lacp m-lag priority %s" % self.mlag_priority_id - conf_str += "" - self.cli_add_command(cmd, True) - - if self.mlag_system_id: - cmd = "lacp m-lag system-id %s" % self.mlag_system_id - conf_str += "" - self.cli_add_command(cmd, True) - - if self.commands: - conf_str += CE_NC_SET_LACP_MLAG_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: set mlag interface atrribute info failed.') - - self.changed = True - - def set_mlag_global(self): - """set mlag global attribute info""" - - if self.is_mlag_global_info_change(): - conf_str = CE_NC_SET_GLOBAL_LACP_MLAG_INFO_HEAD - if self.mlag_priority_id: - conf_str += "%s" % self.mlag_priority_id - if self.mlag_system_id: - conf_str += "%s" % self.mlag_system_id - conf_str += CE_NC_SET_GLOBAL_LACP_MLAG_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: set mlag interface attribute info failed.') - - if self.mlag_priority_id: - self.updates_cmd.append( - "lacp m-lag priority %s" % self.mlag_priority_id) - - if self.mlag_system_id: - self.updates_cmd.append( - "lacp m-lag system-id %s" % self.mlag_system_id) - self.changed = True - - def delete_mlag_global(self): - """delete mlag global attribute info""" - - xml_str = '' - if self.is_mlag_global_info_exist(): - if self.mlag_priority_id: - cmd = "lacp m-lag priority %s" % self.mlag_priority_id - xml_str += '' - self.cli_add_command(cmd, True) - - if self.mlag_system_id: - cmd = "lacp m-lag system-id %s" % self.mlag_system_id - xml_str += '' - self.cli_add_command(cmd, True) - - if xml_str != '': - conf_str = CE_NC_SET_GLOBAL_LACP_MLAG_INFO_HEAD + xml_str + CE_NC_SET_GLOBAL_LACP_MLAG_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: set mlag interface atrribute info failed.') - self.changed = True - - def get_proposed(self): - """get proposed info""" - - if self.eth_trunk_id: - self.proposed["eth_trunk_id"] = self.eth_trunk_id - if self.dfs_group_id: - self.proposed["dfs_group_id"] = self.dfs_group_id - if self.mlag_id: - self.proposed["mlag_id"] = self.mlag_id - if self.mlag_system_id: - self.proposed["mlag_system_id"] = self.mlag_system_id - if self.mlag_priority_id: - self.proposed["mlag_priority_id"] = self.mlag_priority_id - if self.interface: - self.proposed["interface"] = self.interface - if self.mlag_error_down: - self.proposed["mlag_error_down"] = self.mlag_error_down - if self.state: - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - self.mlag_info = self.get_mlag_info() - self.mlag_global_info = self.get_mlag_global_info() - self.mlag_error_down_info = self.get_mlag_error_down_info() - - if self.eth_trunk_id or self.dfs_group_id or self.mlag_id: - if not self.mlag_system_id and not self.mlag_priority_id: - if self.mlag_info: - self.existing["mlagInfos"] = self.mlag_info["mlagInfos"] - - if self.mlag_system_id or self.mlag_priority_id: - if self.eth_trunk_id: - if self.mlag_trunk_attribute_info: - if self.mlag_system_id: - self.existing["lacpMlagSysId"] = self.mlag_trunk_attribute_info[ - "lacpMlagSysId"] - if self.mlag_priority_id: - self.existing["lacpMlagPriority"] = self.mlag_trunk_attribute_info[ - "lacpMlagPriority"] - else: - if self.mlag_global_info: - if self.mlag_system_id: - self.existing["lacpMlagSysId"] = self.mlag_global_info[ - "lacpMlagSysId"] - if self.mlag_priority_id: - self.existing["lacpMlagPriority"] = self.mlag_global_info[ - "lacpMlagPriority"] - - if self.interface or self.mlag_error_down: - if self.mlag_error_down_info: - self.existing["mlagErrorDownInfos"] = self.mlag_error_down_info[ - "mlagErrorDownInfos"] - - def get_end_state(self): - """get end state info""" - - if self.eth_trunk_id or self.dfs_group_id or self.mlag_id: - self.mlag_info = self.get_mlag_info() - if not self.mlag_system_id and not self.mlag_priority_id: - if self.mlag_info: - self.end_state["mlagInfos"] = self.mlag_info["mlagInfos"] - - if self.mlag_system_id or self.mlag_priority_id: - if self.eth_trunk_id: - self.mlag_trunk_attribute_info = self.get_mlag_trunk_attribute_info() - if self.mlag_trunk_attribute_info: - if self.mlag_system_id: - self.end_state["lacpMlagSysId"] = self.mlag_trunk_attribute_info[ - "lacpMlagSysId"] - if self.mlag_priority_id: - self.end_state["lacpMlagPriority"] = self.mlag_trunk_attribute_info[ - "lacpMlagPriority"] - else: - self.mlag_global_info = self.get_mlag_global_info() - if self.mlag_global_info: - if self.mlag_system_id: - self.end_state["lacpMlagSysId"] = self.mlag_global_info[ - "lacpMlagSysId"] - if self.mlag_priority_id: - self.end_state["lacpMlagPriority"] = self.mlag_global_info[ - "lacpMlagPriority"] - - if self.interface or self.mlag_error_down: - self.mlag_error_down_info = self.get_mlag_error_down_info() - if self.mlag_error_down_info: - self.end_state["mlagErrorDownInfos"] = self.mlag_error_down_info[ - "mlagErrorDownInfos"] - - def work(self): - """worker""" - - self.check_params() - self.get_proposed() - self.get_existing() - - if self.eth_trunk_id or self.dfs_group_id or self.mlag_id: - self.mlag_info = self.get_mlag_info() - if self.eth_trunk_id and self.dfs_group_id and self.mlag_id: - if self.state == "present": - self.create_mlag() - else: - self.delete_mlag() - else: - if not self.mlag_system_id and not self.mlag_priority_id: - self.module.fail_json( - msg='Error: eth_trunk_id, dfs_group_id, mlag_id must be config at the same time.') - - if self.mlag_system_id or self.mlag_priority_id: - - if self.eth_trunk_id: - self.mlag_trunk_attribute_info = self.get_mlag_trunk_attribute_info() - if self.mlag_system_id or self.mlag_priority_id: - if self.state == "present": - self.set_mlag_interface() - else: - self.delete_mlag_interface() - else: - self.mlag_global_info = self.get_mlag_global_info() - if self.mlag_system_id or self.mlag_priority_id: - if self.state == "present": - self.set_mlag_global() - else: - self.delete_mlag_global() - - if self.interface or self.mlag_error_down: - self.mlag_error_down_info = self.get_mlag_error_down_info() - if self.interface and self.mlag_error_down: - if self.mlag_error_down == "enable": - self.create_mlag_error_down() - else: - self.delete_mlag_error_down() - else: - self.module.fail_json( - msg='Error: interface, mlag_error_down must be config at the same time.') - - self.get_end_state() - if self.existing == self.end_state: - self.changed = False - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - eth_trunk_id=dict(type='str'), - dfs_group_id=dict(type='str'), - mlag_id=dict(type='str'), - mlag_system_id=dict(type='str'), - mlag_priority_id=dict(type='str'), - interface=dict(type='str'), - mlag_error_down=dict(type='str', choices=['enable', 'disable']), - state=dict(type='str', default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = MlagInterface(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_mtu.py b/plugins/modules/network/cloudengine/ce_mtu.py deleted file mode 100644 index 54d31a664c..0000000000 --- a/plugins/modules/network/cloudengine/ce_mtu.py +++ /dev/null @@ -1,585 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_mtu -short_description: Manages MTU settings on HUAWEI CloudEngine switches. -description: - - Manages MTU settings on HUAWEI CloudEngine switches. -author: QijunPan (@QijunPan) -notes: - - Either C(sysmtu) param is required or C(interface) AND C(mtu) params are req'd. - - C(state=absent) unconfigures a given MTU if that value is currently present. - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - interface: - description: - - Full name of interface, i.e. 40GE1/0/22. - mtu: - description: - - MTU for a specific interface. - The value is an integer ranging from 46 to 9600, in bytes. - jumbo_max: - description: - - Maximum frame size. The default value is 9216. - The value is an integer and expressed in bytes. The value range is 1536 to 12224 for the CE12800 - and 1536 to 12288 for ToR switches. - jumbo_min: - description: - - Non-jumbo frame size threshold. The default value is 1518. - The value is an integer that ranges from 1518 to jumbo_max, in bytes. - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: Mtu test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config jumboframe on 40GE1/0/22" - ce_mtu: - interface: 40GE1/0/22 - jumbo_max: 9000 - jumbo_min: 8000 - provider: "{{ cli }}" - - - name: "Config mtu on 40GE1/0/22 (routed interface)" - ce_mtu: - interface: 40GE1/0/22 - mtu: 1600 - provider: "{{ cli }}" - - - name: "Config mtu on 40GE1/0/23 (switched interface)" - ce_mtu: - interface: 40GE1/0/22 - mtu: 9216 - provider: "{{ cli }}" - - - name: "Config mtu and jumboframe on 40GE1/0/22 (routed interface)" - ce_mtu: - interface: 40GE1/0/22 - mtu: 1601 - jumbo_max: 9001 - jumbo_min: 8001 - provider: "{{ cli }}" - - - name: "Unconfigure mtu and jumboframe on a given interface" - ce_mtu: - state: absent - interface: 40GE1/0/22 - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"mtu": "1700", "jumbo_max": "9000", jumbo_min: "8000"} -existing: - description: k/v pairs of existing mtu/sysmtu on the interface/system - returned: always - type: dict - sample: {"mtu": "1600", "jumbo_max": "9216", "jumbo_min": "1518"} -end_state: - description: k/v pairs of mtu/sysmtu values after module execution - returned: always - type: dict - sample: {"mtu": "1700", "jumbo_max": "9000", jumbo_min: "8000"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["interface 40GE1/0/23", "mtu 1700", "jumboframe enable 9000 8000"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -import copy -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, load_config -from ansible.module_utils.connection import exec_command - - -def is_interface_support_setjumboframe(interface): - """is interface support set jumboframe""" - - if interface is None: - return False - support_flag = False - if interface.upper().startswith('GE'): - support_flag = True - elif interface.upper().startswith('10GE'): - support_flag = True - elif interface.upper().startswith('25GE'): - support_flag = True - elif interface.upper().startswith('4X10GE'): - support_flag = True - elif interface.upper().startswith('40GE'): - support_flag = True - elif interface.upper().startswith('100GE'): - support_flag = True - else: - support_flag = False - return support_flag - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-Port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class Mtu(object): - """set mtu""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # interface info - self.interface = self.module.params['interface'] - self.mtu = self.module.params['mtu'] - self.state = self.module.params['state'] - self.jbf_max = self.module.params['jumbo_max'] or None - self.jbf_min = self.module.params['jumbo_min'] or None - self.jbf_config = list() - self.jbf_cli = "" - self.commands = list() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - self.intf_info = dict() # one interface info - self.intf_type = None # loopback tunnel ... - - def init_module(self): - """ init_module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def get_interface_dict(self, ifname): - """ get one interface attributes dict.""" - intf_info = dict() - - flags = list() - exp = r"| ignore-case section include ^#\s+interface %s\s+" % ifname.replace(" ", "") - flags.append(exp) - output = self.get_config(flags) - output_list = output.split('\n') - if output_list is None: - return intf_info - - mtu = None - for config in output_list: - config = config.strip() - if config.startswith('mtu'): - mtu = re.findall(r'.*mtu\s*([0-9]*)', output)[0] - - intf_info = dict(ifName=ifname, - ifMtu=mtu) - - return intf_info - - def prase_jumboframe_para(self, config_str): - """prase_jumboframe_para""" - - interface_cli = "interface %s" % (self.interface.replace(" ", "").lower()) - if config_str.find(interface_cli) == -1: - self.module.fail_json(msg='Error: Interface does not exist.') - - try: - npos1 = config_str.index('jumboframe enable') - except ValueError: - # return default vale - return [9216, 1518] - try: - npos2 = config_str.index('\n', npos1) - config_str_tmp = config_str[npos1:npos2] - except ValueError: - config_str_tmp = config_str[npos1:] - - return re.findall(r'([0-9]+)', config_str_tmp) - - def cli_load_config(self): - """load config by cli""" - - if not self.module.check_mode: - if len(self.commands) > 1: - load_config(self.module, self.commands) - self.changed = True - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - - def get_jumboframe_config(self): - """ get_jumboframe_config""" - - flags = list() - exp = r"| ignore-case section include ^#\s+interface %s\s+" % self.interface.replace(" ", "") - flags.append(exp) - output = self.get_config(flags) - output = output.replace('*', '').lower() - - return self.prase_jumboframe_para(output) - - def set_jumboframe(self): - """ set_jumboframe""" - - if self.state == "present": - if not self.jbf_max and not self.jbf_min: - return - - jbf_value = self.get_jumboframe_config() - self.jbf_config = copy.deepcopy(jbf_value) - if len(jbf_value) == 1: - jbf_value.append("1518") - self.jbf_config.append("1518") - if not self.jbf_max: - return - - if (len(jbf_value) > 2) or (len(jbf_value) == 0): - self.module.fail_json( - msg='Error: Get jubmoframe config value num error.') - if self.jbf_min is None: - if jbf_value[0] == self.jbf_max: - return - else: - if (jbf_value[0] == self.jbf_max) \ - and (jbf_value[1] == self.jbf_min): - return - if jbf_value[0] != self.jbf_max: - jbf_value[0] = self.jbf_max - if (jbf_value[1] != self.jbf_min) and (self.jbf_min is not None): - jbf_value[1] = self.jbf_min - else: - jbf_value.pop(1) - else: - jbf_value = self.get_jumboframe_config() - self.jbf_config = copy.deepcopy(jbf_value) - if (jbf_value == [9216, 1518]): - return - jbf_value = [9216, 1518] - - if len(jbf_value) == 2: - self.jbf_cli = "jumboframe enable %s %s" % ( - jbf_value[0], jbf_value[1]) - else: - self.jbf_cli = "jumboframe enable %s" % (jbf_value[0]) - self.cli_add_command(self.jbf_cli) - - if self.state == "present": - if self.jbf_min: - self.updates_cmd.append( - "jumboframe enable %s %s" % (self.jbf_max, self.jbf_min)) - else: - self.updates_cmd.append("jumboframe enable %s" % (self.jbf_max)) - else: - self.updates_cmd.append("undo jumboframe enable") - - return - - def merge_interface(self, ifname, mtu): - """ Merge interface mtu.""" - - xmlstr = '' - change = False - - command = "interface %s" % ifname - self.cli_add_command(command) - - if self.state == "present": - if mtu and self.intf_info["ifMtu"] != mtu: - command = "mtu %s" % mtu - self.cli_add_command(command) - self.updates_cmd.append("mtu %s" % mtu) - change = True - else: - if self.intf_info["ifMtu"] != '1500' and self.intf_info["ifMtu"]: - command = "mtu 1500" - self.cli_add_command(command) - self.updates_cmd.append("undo mtu") - change = True - - return - - def check_params(self): - """Check all input params""" - - # interface type check - if self.interface: - self.intf_type = get_interface_type(self.interface) - if not self.intf_type: - self.module.fail_json( - msg='Error: Interface name of %s ' - 'is error.' % self.interface) - - if not self.intf_type: - self.module.fail_json( - msg='Error: Interface %s is error.') - - # mtu check mtu - if self.mtu: - if not self.mtu.isdigit(): - self.module.fail_json(msg='Error: Mtu is invalid.') - # check mtu range - if int(self.mtu) < 46 or int(self.mtu) > 9600: - self.module.fail_json( - msg='Error: Mtu is not in the range from 46 to 9600.') - # get interface info - self.intf_info = self.get_interface_dict(self.interface) - if not self.intf_info: - self.module.fail_json(msg='Error: interface does not exist.') - - # check interface can set jumbo frame - if self.state == 'present': - if self.jbf_max: - if not is_interface_support_setjumboframe(self.interface): - self.module.fail_json( - msg='Error: Interface %s does not support jumboframe set.' % self.interface) - if not self.jbf_max.isdigit(): - self.module.fail_json( - msg='Error: Max jumboframe is not digit.') - if (int(self.jbf_max) > 12288) or (int(self.jbf_max) < 1536): - self.module.fail_json( - msg='Error: Max jumboframe is between 1536 to 12288.') - - if self.jbf_min: - if not self.jbf_min.isdigit(): - self.module.fail_json( - msg='Error: Min jumboframe is not digit.') - if not self.jbf_max: - self.module.fail_json( - msg='Error: please specify max jumboframe value.') - if (int(self.jbf_min) > int(self.jbf_max)) or (int(self.jbf_min) < 1518): - self.module.fail_json( - msg='Error: Min jumboframe is between ' - '1518 to jumboframe max value.') - - if self.jbf_min is not None: - if self.jbf_max is None: - self.module.fail_json( - msg='Error: please input MAX jumboframe ' - 'value.') - - def get_proposed(self): - """ get_proposed""" - - self.proposed['state'] = self.state - if self.interface: - self.proposed["interface"] = self.interface - - if self.state == 'present': - if self.mtu: - self.proposed["mtu"] = self.mtu - if self.jbf_max: - if self.jbf_min: - self.proposed["jumboframe"] = "jumboframe enable %s %s" % ( - self.jbf_max, self.jbf_min) - else: - self.proposed[ - "jumboframe"] = "jumboframe enable %s %s" % (self.jbf_max, 1518) - - def get_existing(self): - """ get_existing""" - - if self.intf_info: - self.existing["interface"] = self.intf_info["ifName"] - self.existing["mtu"] = self.intf_info["ifMtu"] - - if self.intf_info: - if not self.existing["interface"]: - self.existing["interface"] = self.interface - - if len(self.jbf_config) != 2: - return - - self.existing["jumboframe"] = "jumboframe enable %s %s" % ( - self.jbf_config[0], self.jbf_config[1]) - - def get_end_state(self): - """ get_end_state""" - - if self.intf_info: - end_info = self.get_interface_dict(self.interface) - if end_info: - self.end_state["interface"] = end_info["ifName"] - self.end_state["mtu"] = end_info["ifMtu"] - if self.intf_info: - if not self.end_state["interface"]: - self.end_state["interface"] = self.interface - - if self.state == 'absent': - self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( - 9216, 1518) - elif not self.jbf_max and not self.jbf_min: - if len(self.jbf_config) != 2: - return - self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( - self.jbf_config[0], self.jbf_config[1]) - elif self.jbf_min: - self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( - self.jbf_max, self.jbf_min) - else: - self.end_state[ - "jumboframe"] = "jumboframe enable %s %s" % (self.jbf_max, 1518) - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - self.check_params() - - self.get_proposed() - - self.merge_interface(self.interface, self.mtu) - self.set_jumboframe() - self.cli_load_config() - - self.get_existing() - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """ main""" - - argument_spec = dict( - interface=dict(required=True, type='str'), - mtu=dict(type='str'), - state=dict(choices=['absent', 'present'], - default='present', required=False), - jumbo_max=dict(type='str'), - jumbo_min=dict(type='str'), - ) - argument_spec.update(ce_argument_spec) - interface = Mtu(argument_spec) - interface.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_multicast_global.py b/plugins/modules/network/cloudengine/ce_multicast_global.py deleted file mode 100644 index c5cafce220..0000000000 --- a/plugins/modules/network/cloudengine/ce_multicast_global.py +++ /dev/null @@ -1,289 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# 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: ce_multicast_global -author: xuxiaowei0512 (@xuxiaowei0512) -short_description: Manages multicast global configuration on HUAWEI CloudEngine switches. -description: - - Manages multicast global on HUAWEI CloudEngine switches. -notes: - - If no vrf is supplied, vrf is set to default. - - If I(state=absent), the route will be removed, regardless of the non-required parameters. - - This module requires the netconf system service be enabled on the remote device being managed. - - This module works with connection C(netconf). -options: - aftype: - description: - - Destination ip address family type of static route. - required: true - type: str - choices: ['v4','v6'] - vrf: - description: - - VPN instance of destination ip address. - type: str - state: - description: - - Specify desired state of the resource. - type: str - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' ---- - - name: multicast routing-enable - ce_multicast_global: - aftype: v4 - state: absent - provider: "{{ cli }}" - - name: multicast routing-enable - ce_multicast_global: - aftype: v4 - state: present - provider: "{{ cli }}" - - name: multicast routing-enable - ce_multicast_global: - aftype: v4 - vrf: vrf1 - provider: "{{ cli }}" - -''' -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"addressFamily": "ipv4unicast", "state": "present", "vrfName": "_public_"} -existing: - description: k/v pairs of existing switchport - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of switchport after module execution - returned: always - type: dict - sample: {"addressFamily": "ipv4unicast", "state": "present", "vrfName": "_public_"} -updates: - description: command list sent to the device - returned: always - type: list - sample: ["multicast routing-enable"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config - -CE_NC_GET_MULTICAST_GLOBAL = """ - - - - - %s - %s - - - - -""" -CE_NC_MERGE_MULTICAST_GLOBAL = """ - - - - %s - %s - - - -""" -CE_NC_DELETE_MULTICAST_GLOBAL = """ - - - - %s - %s - - - -""" - - -def build_config_xml(xmlstr): - """build config xml""" - - return ' ' + xmlstr + ' ' - - -class MulticastGlobal(object): - """multicast global module""" - - def __init__(self, argument_spec): - """multicast global info""" - self.spec = argument_spec - self.module = None - self._initmodule_() - - self.aftype = self.module.params['aftype'] - self.state = self.module.params['state'] - if self.aftype == "v4": - self.version = "ipv4unicast" - else: - self.version = "ipv6unicast" - # vpn instance info - self.vrf = self.module.params['vrf'] - if self.vrf is None: - self.vrf = "_public_" - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - self.multicast_global_info = dict() - - def _initmodule_(self): - """init module""" - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=False) - - def _checkresponse_(self, xml_str, xml_name): - """check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def set_change_state(self): - """set change state""" - state = self.state - change = False - self.get_multicast_global() - # new or edit - if state == 'present': - if not self.multicast_global_info.get('multicast_global'): - # i.e. self.multicast_global_info['multicast_global'] has not value - change = True - else: - # delete - if self.multicast_global_info.get('multicast_global'): - # i.e. self.multicast_global_info['multicast_global'] has value - change = True - self.changed = change - - def get_multicast_global(self): - """get one data""" - self.multicast_global_info["multicast_global"] = list() - getxmlstr = CE_NC_GET_MULTICAST_GLOBAL % ( - self.version, self.vrf) - xml_str = get_nc_config(self.module, getxmlstr) - if 'data/' in xml_str: - return - xml_str = xml_str.replace('\r', '').replace('\n', ''). \ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - mcast_enable = root.findall( - "mcastbase/mcastAfsEnables/mcastAfsEnable") - if mcast_enable: - # i.e. mcast_enable = [{vrfName:11,addressFamily:'xx'},{vrfName:22,addressFamily:'xx'}...] - for mcast_enable_key in mcast_enable: - # i.e. mcast_enable_key = {vrfName:11,addressFamily:'xx'} - mcast_info = dict() - for ele in mcast_enable_key: - if ele.tag in ["vrfName", "addressFamily"]: - mcast_info[ele.tag] = ele.text - self.multicast_global_info['multicast_global'].append(mcast_info) - - def get_existing(self): - """get existing information""" - self.set_change_state() - self.existing["multicast_global"] = self.multicast_global_info["multicast_global"] - - def get_proposed(self): - """get proposed information""" - self.proposed['addressFamily'] = self.version - self.proposed['state'] = self.state - self.proposed['vrfName'] = self.vrf - - def set_multicast_global(self): - """set multicast global""" - if not self.changed: - return - version = self.version - state = self.state - if state == "present": - configxmlstr = CE_NC_MERGE_MULTICAST_GLOBAL % (self.vrf, version) - else: - configxmlstr = CE_NC_DELETE_MULTICAST_GLOBAL % (self.vrf, version) - - conf_str = build_config_xml(configxmlstr) - recv_xml = set_nc_config(self.module, conf_str) - self._checkresponse_(recv_xml, "SET_MULTICAST_GLOBAL") - - def set_update_cmd(self): - """set update command""" - if not self.changed: - return - if self.state == "present": - self.updates_cmd.append('multicast routing-enable') - else: - self.updates_cmd.append('undo multicast routing-enable') - - def get_end_state(self): - """get end state information""" - self.get_multicast_global() - self.end_state["multicast_global"] = self.multicast_global_info["multicast_global"] - - def work(self): - """worker""" - self.get_existing() - self.get_proposed() - self.set_multicast_global() - self.set_update_cmd() - self.get_end_state() - self.results['changed'] = self.changed - self.results['existing'] = self.existing - self.results['proposed'] = self.proposed - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - self.module.exit_json(**self.results) - - -def main(): - """main""" - - argument_spec = dict( - aftype=dict(choices=['v4', 'v6'], required=True), - vrf=dict(required=False, type='str'), - state=dict(choices=['absent', 'present'], default='present', required=False), - ) - interface = MulticastGlobal(argument_spec) - interface.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_multicast_igmp_enable.py b/plugins/modules/network/cloudengine/ce_multicast_igmp_enable.py deleted file mode 100644 index 9a57337859..0000000000 --- a/plugins/modules/network/cloudengine/ce_multicast_igmp_enable.py +++ /dev/null @@ -1,544 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# 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: ce_multicast_igmp_enable -author: xuxiaowei0512 (@CloudEngine-Ansible) -short_description: Manages multicast igmp enable configuration on HUAWEI CloudEngine switches. -description: - - Manages multicast igmp on HUAWEI CloudEngine switches. -notes: - - If no vrf is supplied, vrf is set to default. - If I(state=absent), the route will be removed, regardless of the - non-required parameters. - - This module requires the netconf system service be enabled on - the remote device being managed. - - This module works with connection C(netconf). -options: - aftype: - description: - - Destination ip address family type of static route. - required: true - type: str - choices: ['v4','v6'] - features: - description: - - Distinguish between Globally Enabled IGMP or - - Enabled IGMP under vlanID. - required: true - type: str - choices: ['global','vlan'] - vlan_id: - description: - - Virtual LAN identity. - type: int - igmp: - description: - - Enable Layer 2 multicast Snooping in a VLAN. - type: bool - version: - description: - - Specifies the IGMP version that can be processed. - default: 2 - type: int - proxy: - description: - - Layer 2 multicast snooping proxy is enabled. - type: bool - state: - description: - - Specify desired state of the resource. - choices: ['present','absent'] - default: present - type: str -''' - -EXAMPLES = ''' - - - name: configure global igmp enable - ce_multicast_igmp_enable: - aftype: v4 - features: 'global' - state: present - - - name: configure global igmp disable - ce_multicast_igmp_enable: - features: 'global' - aftype: v4 - state: absent - - - name: configure vlan igmp enable - ce_multicast_igmp_enable: - features: 'vlan' - aftype: v4 - vlan_id: 1 - igmp: true - - - name: new proxy,igmp,version - ce_multicast_igmp_enable: - features: 'vlan' - aftype: v4 - vlan_id: 1 - proxy: true - igmp: true - version: 1 - - - name: modify proxy,igmp,version - ce_multicast_igmp_enable: - features: 'vlan' - aftype: v4 - vlan_id: 1 - version: 2 - - - name: delete proxy,igmp,version - ce_multicast_igmp_enable: - features: 'vlan' - aftype: v4 - vlan_id: 1 - state: absent -''' -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"addrFamily": "ipv4unicast", "features": "vlan", "proxyEnable": "false", - "snoopingEnable": "false", "state": "absent", "version": 2, "vlanId": 1} -existing: - description: k/v pairs of existing switchport - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of switchport after module execution - returned: always - type: dict - sample: {} -updates: - description: command list sent to the device - returned: always - type: list - sample: ["undo igmp snooping enable", - "undo igmp snooping version", - "undo igmp snooping proxy"] -changed: - description: check if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config - -CE_NC_GET_IGMP_GLOBAL = """ - - - - - %s - - - - -""" -CE_NC_MERGE_IGMP_SYSVIEW = """ - - - - %s - - - -""" -CE_NC_DELETE_IGMP_SYSVIEW = """ - - - - %s - - - -""" -CE_NC_GET_IGMP_VLAN_INFO = """ - - - - - - %s - %s - - - - - - - - -""" -CE_NC_MERGE_IGMP_VLANVIEW = """ - - - - - %s - %s%s%s%s - - - - -""" -CE_NC_MERGE_IGMP_VLANVIEW_SNOENABLE = """ -%s -""" -CE_NC_MERGE_IGMP_VLANVIEW_VERSION = """ -%s -""" -CE_NC_MERGE_IGMP_VLANVIEW_PROXYENABLE = """ -%s -""" -CE_NC_DELETE_IGMP_VLANVIEW = """ - - - - - %s - %s - - - - -""" - - -def get_xml(xml, value): - """operate xml""" - tempxml = xml % value - return tempxml - - -def build_config_xml(xmlstr): - """build config xml""" - - return ' ' + xmlstr + ' ' - - -class IgmpSnoop(object): - """igmp snooping module""" - - def __init__(self, argument_spec): - """igmp snooping info""" - self.spec = argument_spec - self.module = None - self._initmodule_() - - self.aftype = self.module.params['aftype'] - self.state = self.module.params['state'] - if self.aftype == "v4": - self.addr_family = "ipv4unicast" - else: - self.addr_family = "ipv6unicast" - self.features = self.module.params['features'] - self.vlan_id = self.module.params['vlan_id'] - self.igmp = str(self.module.params['igmp']).lower() - self.version = self.module.params['version'] - if self.version is None: - self.version = 2 - self.proxy = str(self.module.params['proxy']).lower() - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - self.igmp_info_data = dict() - - def _initmodule_(self): - """init module""" - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=False) - - def _checkresponse_(self, xml_str, xml_name): - """check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def _checkparams_(self): - """check all input params""" - # check vlan id - if self.features == 'vlan': - if not self.vlan_id: - self.module.fail_json(msg='Error: missing required arguments: vlan_id.') - - if self.vlan_id: - if self.vlan_id <= 0 or self.vlan_id > 4094: - self.module.fail_json( - msg='Error: Vlan id is not in the range from 1 to 4094.') - # check version - if self.version: - if self.version <= 0 or self.version > 3: - self.module.fail_json( - msg='Error: Version id is not in the range from 1 to 3.') - - def set_change_state(self): - """set change state""" - state = self.state - change = False - # vlan view igmp - if self.features == 'vlan': - self.get_igmp_vlan() - change = self.compare_data() - else: - # sys view igmp(global) - self.get_igmp_global() - # new or edit - if state == 'present': - if not self.igmp_info_data["igmp_info"]: - # igmp_info_data has not igmp_info value. - change = True - else: - # delete - if self.igmp_info_data["igmp_info"]: - # igmp_info_data has not igmp_info value. - change = True - self.changed = change - - def compare_data(self): - """compare new data and old data""" - state = self.state - change = False - # new or edit - if state == 'present': - # edit - if self.igmp_info_data["igmp_info"]: - for data in self.igmp_info_data["igmp_info"]: - if self.addr_family == data["addrFamily"] and str(self.vlan_id) == data["vlanId"]: - if self.igmp: - if self.igmp != data["snoopingEnable"]: - change = True - if self.version: - if str(self.version) != data["version"]: - change = True - if self.proxy: - if self.proxy != data["proxyEnable"]: - change = True - # new - else: - change = True - else: - # delete - if self.igmp_info_data["igmp_info"]: - change = True - return change - - def get_igmp_vlan(self): - """get igmp vlan info data""" - self.igmp_info_data["igmp_info"] = list() - getxmlstr = CE_NC_GET_IGMP_VLAN_INFO % (self.addr_family, self.vlan_id) - xml_str = get_nc_config(self.module, getxmlstr) - if 'data/' in xml_str: - return - xml_str = xml_str.replace('\r', '').replace('\n', ''). \ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - igmp_enable = root.findall( - "l2mc/vlan/l2McVlanCfgs/l2McVlanCfg") - if igmp_enable: - # igmp_enable = [{addressFamily:'xx'}] - for igmp_enable_key in igmp_enable: - # igmp_enable_key = {addressFamily:'xx'} - igmp_global_info = dict() - for ele in igmp_enable_key: - if ele.tag in ["addrFamily", "vlanId", "snoopingEnable", "version", "proxyEnable"]: - igmp_global_info[ele.tag] = ele.text - self.igmp_info_data["igmp_info"].append(igmp_global_info) - - def get_igmp_global(self): - """get igmp global data""" - self.igmp_info_data["igmp_info"] = list() - getxmlstr = CE_NC_GET_IGMP_GLOBAL % ( - self.addr_family) - xml_str = get_nc_config(self.module, getxmlstr) - if 'data/' in xml_str: - return - xml_str = xml_str.replace('\r', '').replace('\n', ''). \ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - igmp_enable = root.findall( - 'l2mc/l2McSnpgEnables/l2McSnpgEnable') - if igmp_enable: - # igmp_enable = [{addressFamily:'xx'}] - for igmp_enable_key in igmp_enable: - # igmp_enable_key = {addressFamily:'xx'} - igmp_global_info = dict() - for ele in igmp_enable_key: - if ele.tag in ["addrFamily"]: - igmp_global_info[ele.tag] = ele.text - self.igmp_info_data["igmp_info"].append(igmp_global_info) - - def set_vlanview_igmp(self): - """set igmp of vlanview""" - if not self.changed: - return - addr_family = self.addr_family - state = self.state - igmp_xml = """\n""" - version_xml = """\n""" - proxy_xml = """\n""" - if state == "present": - if self.igmp: - igmp_xml = get_xml(CE_NC_MERGE_IGMP_VLANVIEW_SNOENABLE, self.igmp.lower()) - if str(self.version): - version_xml = get_xml(CE_NC_MERGE_IGMP_VLANVIEW_VERSION, self.version) - if self.proxy: - proxy_xml = get_xml(CE_NC_MERGE_IGMP_VLANVIEW_PROXYENABLE, self.proxy.lower()) - configxmlstr = CE_NC_MERGE_IGMP_VLANVIEW % ( - addr_family, self.vlan_id, igmp_xml, version_xml, proxy_xml) - else: - configxmlstr = CE_NC_DELETE_IGMP_VLANVIEW % (addr_family, self.vlan_id) - conf_str = build_config_xml(configxmlstr) - recv_xml = set_nc_config(self.module, conf_str) - self._checkresponse_(recv_xml, "SET_VLANVIEW_IGMP") - - def set_sysview_igmp(self): - """set igmp of sysview""" - if not self.changed: - return - version = self.addr_family - state = self.state - if state == "present": - configxmlstr = CE_NC_MERGE_IGMP_SYSVIEW % (version) - else: - configxmlstr = CE_NC_DELETE_IGMP_SYSVIEW % (version) - - conf_str = build_config_xml(configxmlstr) - recv_xml = set_nc_config(self.module, conf_str) - self._checkresponse_(recv_xml, "SET_SYSVIEW_IGMP") - - def set_sysview_cmd(self): - """set sysview update command""" - if not self.changed: - return - if self.state == "present": - self.updates_cmd.append('igmp snooping enable') - else: - self.updates_cmd.append('undo igmp snooping enable') - - def set_vlanview_cmd(self): - """set vlanview update command""" - if not self.changed: - return - if self.state == "present": - if self.igmp: - if self.igmp.lower() == 'true': - self.updates_cmd.append('igmp snooping enable') - else: - self.updates_cmd.append('undo igmp snooping enable') - if str(self.version): - self.updates_cmd.append('igmp snooping version %s' % (self.version)) - else: - self.updates_cmd.append('undo igmp snooping version') - if self.proxy: - if self.proxy.lower() == 'true': - self.updates_cmd.append('igmp snooping proxy') - else: - self.updates_cmd.append('undo igmp snooping proxy') - - else: - self.updates_cmd.append('undo igmp snooping enable') - self.updates_cmd.append('undo igmp snooping version') - self.updates_cmd.append('undo igmp snooping proxy') - - def get_existing(self): - """get existing information""" - self.set_change_state() - self.existing["igmp_info"] = self.igmp_info_data["igmp_info"] - - def get_proposed(self): - """get proposed information""" - self.proposed['addrFamily'] = self.addr_family - self.proposed['features'] = self.features - if self.features == 'vlan': - self.proposed['snoopingEnable'] = self.igmp - self.proposed['version'] = self.version - self.proposed['vlanId'] = self.vlan_id - self.proposed['proxyEnable'] = self.proxy - self.proposed['state'] = self.state - - def set_igmp_netconf(self): - """config netconf""" - if self.features == 'vlan': - self.set_vlanview_igmp() - else: - self.set_sysview_igmp() - - def set_update_cmd(self): - """set update command""" - if self.features == 'vlan': - self.set_vlanview_cmd() - else: - self.set_sysview_cmd() - - def get_end_state(self): - """get end state information""" - if self.features == 'vlan': - self.get_igmp_vlan() - else: - # sys view igmp(global) - self.get_igmp_global() - self.end_state["igmp_info"] = self.igmp_info_data["igmp_info"] - - def work(self): - """worker""" - self._checkparams_() - self.get_existing() - self.get_proposed() - self.set_igmp_netconf() - self.set_update_cmd() - self.get_end_state() - self.results['changed'] = self.changed - self.results['existing'] = self.existing - self.results['proposed'] = self.proposed - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - self.module.exit_json(**self.results) - - -def main(): - """main""" - argument_spec = dict( - aftype=dict(choices=['v4', 'v6'], required=True), - features=dict(required=True, choices=['global', 'vlan'], type='str'), - vlan_id=dict(type='int'), - igmp=dict(type='bool', default=False), - version=dict(type='int', default=2), - proxy=dict(type='bool', default=False), - state=dict(choices=['absent', 'present'], default='present'), - ) - interface = IgmpSnoop(argument_spec) - interface.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_netconf.py b/plugins/modules/network/cloudengine/ce_netconf.py deleted file mode 100644 index 141731f08c..0000000000 --- a/plugins/modules/network/cloudengine/ce_netconf.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_netconf -short_description: Run an arbitrary netconf command on HUAWEI CloudEngine switches. -description: - - Sends an arbitrary netconf command on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - rpc: - description: - - The type of rpc. - required: true - choices: ['get', 'edit-config', 'execute-action', 'execute-cli'] - cfg_xml: - description: - - The config xml string. - required: true -''' - -EXAMPLES = ''' - -- name: CloudEngine netconf test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Netconf get operation" - ce_netconf: - rpc: get - cfg_xml: ' - - - - 10 - - - - - - - - - ' - provider: "{{ cli }}" - - - name: "Netconf edit-config operation" - ce_netconf: - rpc: edit-config - cfg_xml: ' - - - - default_wdz - local - invalid - - - - ' - provider: "{{ cli }}" - - - name: "Netconf execute-action operation" - ce_netconf: - rpc: execute-action - cfg_xml: ' - - - ipv4unicast - - - ' - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"result": ["ok"]} -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import execute_nc_action, ce_argument_spec, execute_nc_cli - - -def main(): - """ main """ - - argument_spec = dict( - rpc=dict(choices=['get', 'edit-config', - 'execute-action', 'execute-cli'], required=True), - cfg_xml=dict(required=True) - ) - - argument_spec.update(ce_argument_spec) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - - rpc = module.params['rpc'] - cfg_xml = module.params['cfg_xml'] - changed = False - end_state = dict() - - if rpc == "get": - - response = get_nc_config(module, cfg_xml) - - if "" in response: - end_state["result"] = "" - else: - tmp1 = response.split(r"") - tmp2 = tmp1[1].split(r"") - result = tmp2[0].split("\n") - - end_state["result"] = result - - elif rpc == "edit-config": - - response = set_nc_config(module, cfg_xml) - - if "" not in response: - module.fail_json(msg='rpc edit-config failed.') - - changed = True - end_state["result"] = "ok" - - elif rpc == "execute-action": - - response = execute_nc_action(module, cfg_xml) - - if "" not in response: - module.fail_json(msg='rpc execute-action failed.') - - changed = True - end_state["result"] = "ok" - - elif rpc == "execute-cli": - - response = execute_nc_cli(module, cfg_xml) - - if "" in response: - end_state["result"] = "" - else: - tmp1 = response.split(r"") - tmp2 = tmp1[1].split(r"") - result = tmp2[0].split("\n") - - end_state["result"] = result - - else: - module.fail_json(msg='please input correct rpc.') - - results = dict() - results['changed'] = changed - results['end_state'] = end_state - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_netstream_aging.py b/plugins/modules/network/cloudengine/ce_netstream_aging.py deleted file mode 100644 index f4e6e87f81..0000000000 --- a/plugins/modules/network/cloudengine/ce_netstream_aging.py +++ /dev/null @@ -1,520 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_netstream_aging -short_description: Manages timeout mode of NetStream on HUAWEI CloudEngine switches. -description: - - Manages timeout mode of NetStream on HUAWEI CloudEngine switches. -author: YangYang (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - timeout_interval: - description: - - Netstream timeout interval. - If is active type the interval is 1-60. - If is inactive ,the interval is 5-600. - default: 30 - type: - description: - - Specifies the packet type of netstream timeout active interval. - choices: ['ip', 'vxlan'] - state: - description: - - Specify desired state of the resource. - choices: ['present', 'absent'] - default: present - timeout_type: - description: - - Netstream timeout type. - choices: ['active', 'inactive', 'tcp-session', 'manual'] - manual_slot: - description: - - Specifies the slot number of netstream manual timeout. -''' - -EXAMPLES = ''' -- name: netstream aging module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Configure netstream ip timeout active interval , the interval is 40 minutes. - ce_netstream_aging: - timeout_interval: 40 - type: ip - timeout_type: active - state: present - provider: "{{ cli }}" - - - name: Configure netstream vxlan timeout active interval , the interval is 40 minutes. - ce_netstream_aging: - timeout_interval: 40 - type: vxlan - timeout_type: active - active_state: present - provider: "{{ cli }}" - - - name: Delete netstream ip timeout active interval , set the ip timeout interval to 30 minutes. - ce_netstream_aging: - type: ip - timeout_type: active - state: absent - provider: "{{ cli }}" - - - name: Delete netstream vxlan timeout active interval , set the vxlan timeout interval to 30 minutes. - ce_netstream_aging: - type: vxlan - timeout_type: active - state: absent - provider: "{{ cli }}" - - - name: Enable netstream ip tcp session timeout. - ce_netstream_aging: - type: ip - timeout_type: tcp-session - state: present - provider: "{{ cli }}" - - - name: Enable netstream vxlan tcp session timeout. - ce_netstream_aging: - type: vxlan - timeout_type: tcp-session - state: present - provider: "{{ cli }}" - - - name: Disable netstream ip tcp session timeout. - ce_netstream_aging: - type: ip - timeout_type: tcp-session - state: absent - provider: "{{ cli }}" - - - name: Disable netstream vxlan tcp session timeout. - ce_netstream_aging: - type: vxlan - timeout_type: tcp-session - state: absent - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"timeout_interval": "40", - "type": "ip", - "state": "absent", - "timeout_type": active} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"active_timeout": [ - { - "ip": "40", - "vxlan": 30 - } - ], - "inactive_timeout": [ - { - "ip": 30, - "vxlan": 30 - } - ], - "tcp_timeout": [ - { - "ip": "disable", - "vxlan": "disable" - } - ]} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"active_timeout": [ - { - "ip": 30, - "vxlan": 30 - } - ], - "inactive_timeout": [ - { - "ip": 30, - "vxlan": 30 - } - ], - "tcp_timeout": [ - { - "ip": "disable", - "vxlan": "disable" - } - ]} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["undo netstream timeout ip active 40"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import exec_command, load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec - - -class NetStreamAging(object): - """ - Manages netstream aging. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.timeout_interval = self.module.params['timeout_interval'] - self.type = self.module.params['type'] - self.state = self.module.params['state'] - self.timeout_type = self.module.params['timeout_type'] - self.manual_slot = self.module.params['manual_slot'] - - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - # local parameters - self.existing["active_timeout"] = list() - self.existing["inactive_timeout"] = list() - self.existing["tcp_timeout"] = list() - self.end_state["active_timeout"] = list() - self.end_state["inactive_timeout"] = list() - self.end_state["tcp_timeout"] = list() - self.active_changed = False - self.inactive_changed = False - self.tcp_changed = False - - def init_module(self): - """init module""" - - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) - - def get_exist_timer_out_para(self): - """Get exist netstream timeout parameters""" - - active_tmp = dict() - inactive_tmp = dict() - tcp_tmp = dict() - active_tmp["ip"] = "30" - active_tmp["vxlan"] = "30" - inactive_tmp["ip"] = "30" - inactive_tmp["vxlan"] = "30" - tcp_tmp["ip"] = "absent" - tcp_tmp["vxlan"] = "absent" - - cmd = "display current-configuration | include ^netstream timeout" - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - config = str(out).strip() - if config: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem = config_mem.lstrip() - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 4 and config_mem_list[2] == "ip": - if config_mem_list[3] == "active": - active_tmp["ip"] = config_mem_list[4] - if config_mem_list[3] == "inactive": - inactive_tmp["ip"] = config_mem_list[4] - if config_mem_list[3] == "tcp-session": - tcp_tmp["ip"] = "present" - if len(config_mem_list) > 4 and config_mem_list[2] == "vxlan": - if config_mem_list[4] == "active": - active_tmp["vxlan"] = config_mem_list[5] - if config_mem_list[4] == "inactive": - inactive_tmp["vxlan"] = config_mem_list[5] - if config_mem_list[4] == "tcp-session": - tcp_tmp["vxlan"] = "present" - self.existing["active_timeout"].append(active_tmp) - self.existing["inactive_timeout"].append(inactive_tmp) - self.existing["tcp_timeout"].append(tcp_tmp) - - def get_end_timer_out_para(self): - """Get end netstream timeout parameters""" - - active_tmp = dict() - inactive_tmp = dict() - tcp_tmp = dict() - active_tmp["ip"] = "30" - active_tmp["vxlan"] = "30" - inactive_tmp["ip"] = "30" - inactive_tmp["vxlan"] = "30" - tcp_tmp["ip"] = "absent" - tcp_tmp["vxlan"] = "absent" - cmd = "display current-configuration | include ^netstream timeout" - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - config = str(out).strip() - if config: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem = config_mem.lstrip() - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 4 and config_mem_list[2] == "ip": - if config_mem_list[3] == "active": - active_tmp["ip"] = config_mem_list[4] - if config_mem_list[3] == "inactive": - inactive_tmp["ip"] = config_mem_list[4] - if config_mem_list[3] == "tcp-session": - tcp_tmp["ip"] = "present" - if len(config_mem_list) > 4 and config_mem_list[2] == "vxlan": - if config_mem_list[4] == "active": - active_tmp["vxlan"] = config_mem_list[5] - if config_mem_list[4] == "inactive": - inactive_tmp["vxlan"] = config_mem_list[5] - if config_mem_list[4] == "tcp-session": - tcp_tmp["vxlan"] = "present" - self.end_state["active_timeout"].append(active_tmp) - self.end_state["inactive_timeout"].append(inactive_tmp) - self.end_state["tcp_timeout"].append(tcp_tmp) - - def check_params(self): - """Check all input params""" - - # interval check - if not str(self.timeout_interval).isdigit(): - self.module.fail_json( - msg='Error: Timeout interval should be numerical.') - if self.timeout_type == "active": - if int(self.timeout_interval) < 1 or int(self.timeout_interval) > 60: - self.module.fail_json( - msg="Error: Active interval should between 1 - 60 minutes.") - if self.timeout_type == "inactive": - if int(self.timeout_interval) < 5 or int(self.timeout_interval) > 600: - self.module.fail_json( - msg="Error: Inactive interval should between 5 - 600 seconds.") - if self.timeout_type == "manual": - if not self.manual_slot: - self.module.fail_json( - msg="Error: If use manual timeout mode,slot number is needed.") - if re.match(r'^\d+(\/\d*)?$', self.manual_slot) is None: - self.module.fail_json( - msg='Error: Slot number should be numerical.') - - def get_proposed(self): - """get proposed info""" - - if self.timeout_interval: - self.proposed["timeout_interval"] = self.timeout_interval - if self.timeout_type: - self.proposed["timeout_type"] = self.timeout_type - if self.type: - self.proposed["type"] = self.type - if self.state: - self.proposed["state"] = self.state - if self.manual_slot: - self.proposed["manual_slot"] = self.manual_slot - - def get_existing(self): - """get existing info""" - active_tmp = dict() - inactive_tmp = dict() - tcp_tmp = dict() - - self.get_exist_timer_out_para() - - if self.timeout_type == "active": - for active_tmp in self.existing["active_timeout"]: - if self.state == "present": - if str(active_tmp[self.type]) != self.timeout_interval: - self.active_changed = True - else: - if self.timeout_interval != "30": - if str(active_tmp[self.type]) != "30": - if str(active_tmp[self.type]) != self.timeout_interval: - self.module.fail_json( - msg='Error: The specified active interval do not exist.') - if str(active_tmp[self.type]) != "30": - self.timeout_interval = active_tmp[self.type] - self.active_changed = True - if self.timeout_type == "inactive": - for inactive_tmp in self.existing["inactive_timeout"]: - if self.state == "present": - if str(inactive_tmp[self.type]) != self.timeout_interval: - self.inactive_changed = True - else: - if self.timeout_interval != "30": - if str(inactive_tmp[self.type]) != "30": - if str(inactive_tmp[self.type]) != self.timeout_interval: - self.module.fail_json( - msg='Error: The specified inactive interval do not exist.') - if str(inactive_tmp[self.type]) != "30": - self.timeout_interval = inactive_tmp[self.type] - self.inactive_changed = True - if self.timeout_type == "tcp-session": - for tcp_tmp in self.existing["tcp_timeout"]: - if str(tcp_tmp[self.type]) != self.state: - self.tcp_changed = True - - def operate_time_out(self): - """configure timeout parameters""" - - cmd = "" - if self.timeout_type == "manual": - if self.type == "ip": - self.cli_add_command("quit") - cmd = "reset netstream cache ip slot %s" % self.manual_slot - self.cli_add_command(cmd) - elif self.type == "vxlan": - self.cli_add_command("quit") - cmd = "reset netstream cache vxlan inner-ip slot %s" % self.manual_slot - self.cli_add_command(cmd) - - if not self.active_changed and not self.inactive_changed and not self.tcp_changed: - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - return - - if self.active_changed or self.inactive_changed: - if self.type == "ip": - cmd = "netstream timeout ip %s %s" % (self.timeout_type, self.timeout_interval) - elif self.type == "vxlan": - cmd = "netstream timeout vxlan inner-ip %s %s" % (self.timeout_type, self.timeout_interval) - if self.state == "absent": - self.cli_add_command(cmd, undo=True) - else: - self.cli_add_command(cmd) - if self.timeout_type == "tcp-session" and self.tcp_changed: - if self.type == "ip": - if self.state == "present": - cmd = "netstream timeout ip tcp-session" - else: - cmd = "undo netstream timeout ip tcp-session" - - elif self.type == "vxlan": - if self.state == "present": - cmd = "netstream timeout vxlan inner-ip tcp-session" - else: - cmd = "undo netstream timeout vxlan inner-ip tcp-session" - self.cli_add_command(cmd) - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - - def get_end_state(self): - """get end state info""" - - self.get_end_timer_out_para() - - def work(self): - """worker""" - - self.check_params() - self.get_existing() - self.get_proposed() - self.operate_time_out() - self.get_end_state() - if self.existing == self.end_state: - self.changed = False - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - timeout_interval=dict(required=False, type='str', default='30'), - type=dict(required=False, choices=['ip', 'vxlan']), - state=dict(required=False, choices=['present', 'absent'], default='present'), - timeout_type=dict(required=False, choices=['active', 'inactive', 'tcp-session', 'manual']), - manual_slot=dict(required=False, type='str'), - ) - argument_spec.update(ce_argument_spec) - module = NetStreamAging(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_netstream_export.py b/plugins/modules/network/cloudengine/ce_netstream_export.py deleted file mode 100644 index b5c01d05a7..0000000000 --- a/plugins/modules/network/cloudengine/ce_netstream_export.py +++ /dev/null @@ -1,561 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_netstream_export -short_description: Manages netstream export on HUAWEI CloudEngine switches. -description: - - Configure NetStream flow statistics exporting and versions for exported packets on HUAWEI CloudEngine switches. -author: Zhijin Zhou (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - type: - description: - - Specifies NetStream feature. - required: true - choices: ['ip', 'vxlan'] - source_ip: - description: - - Specifies source address which can be IPv6 or IPv4 of the exported NetStream packet. - host_ip: - description: - - Specifies destination address which can be IPv6 or IPv4 of the exported NetStream packet. - host_port: - description: - - Specifies the destination UDP port number of the exported packets. - The value is an integer that ranges from 1 to 65535. - host_vpn: - description: - - Specifies the VPN instance of the exported packets carrying flow statistics. - Ensure the VPN instance has been created on the device. - version: - description: - - Sets the version of exported packets. - choices: ['5', '9'] - as_option: - description: - - Specifies the AS number recorded in the statistics as the original or the peer AS number. - choices: ['origin', 'peer'] - bgp_nexthop: - description: - - Configures the statistics to carry BGP next hop information. Currently, only V9 supports the exported - packets carrying BGP next hop information. - choices: ['enable','disable'] - default: 'disable' - state: - description: - - Manage the state of the resource. - choices: ['present','absent'] - default: present -''' - -EXAMPLES = ''' -- name: netstream export module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Configures the source address for the exported packets carrying IPv4 flow statistics. - ce_netstream_export: - type: ip - source_ip: 192.8.2.2 - provider: "{{ cli }}" - - - name: Configures the source IP address for the exported packets carrying VXLAN flexible flow statistics. - ce_netstream_export: - type: vxlan - source_ip: 192.8.2.3 - provider: "{{ cli }}" - - - name: Configures the destination IP address and destination UDP port number for the exported packets carrying IPv4 flow statistics. - ce_netstream_export: - type: ip - host_ip: 192.8.2.4 - host_port: 25 - host_vpn: test - provider: "{{ cli }}" - - - name: Configures the destination IP address and destination UDP port number for the exported packets carrying VXLAN flexible flow statistics. - ce_netstream_export: - type: vxlan - host_ip: 192.8.2.5 - host_port: 26 - host_vpn: test - provider: "{{ cli }}" - - - name: Configures the version number of the exported packets carrying IPv4 flow statistics. - ce_netstream_export: - type: ip - version: 9 - as_option: origin - bgp_nexthop: enable - provider: "{{ cli }}" - - - name: Configures the version for the exported packets carrying VXLAN flexible flow statistics. - ce_netstream_export: - type: vxlan - version: 9 - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "as_option": "origin", - "bgp_nexthop": "enable", - "host_ip": "192.8.5.6", - "host_port": "26", - "host_vpn": "test", - "source_ip": "192.8.2.5", - "state": "present", - "type": "ip", - "version": "9" - } -existing: - description: k/v pairs of existing attributes on the device - returned: always - type: dict - sample: { - "as_option": null, - "bgp_nexthop": "disable", - "host_ip": null, - "host_port": null, - "host_vpn": null, - "source_ip": null, - "type": "ip", - "version": null - } -end_state: - description: k/v pairs of end attributes on the device - returned: always - type: dict - sample: { - "as_option": "origin", - "bgp_nexthop": "enable", - "host_ip": "192.8.5.6", - "host_port": "26", - "host_vpn": "test", - "source_ip": "192.8.2.5", - "type": "ip", - "version": "9" - } -updates: - description: command list sent to the device - returned: always - type: list - sample: [ - "netstream export ip source 192.8.2.5", - "netstream export ip host 192.8.5.6 26 vpn-instance test", - "netstream export ip version 9 origin-as bgp-nexthop" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import exec_command, load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec - - -def is_ipv4_addr(ip_addr): - """check ipaddress validate""" - - rule1 = r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.' - rule2 = r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' - ipv4_regex = '%s%s%s%s%s%s' % ('^', rule1, rule1, rule1, rule2, '$') - - return bool(re.match(ipv4_regex, ip_addr)) - - -def is_config_exist(cmp_cfg, test_cfg): - """is configuration exist""" - - test_cfg_tmp = test_cfg + ' *$' + '|' + test_cfg + ' *\n' - obj = re.compile(test_cfg_tmp) - result = re.findall(obj, cmp_cfg) - if not result: - return False - return True - - -class NetstreamExport(object): - """Manage NetStream export""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # NetStream export configuration parameters - self.type = self.module.params['type'] - self.source_ip = self.module.params['source_ip'] - self.host_ip = self.module.params['host_ip'] - self.host_port = self.module.params['host_port'] - self.host_vpn = self.module.params['host_vpn'] - self.version = self.module.params['version'] - self.as_option = self.module.params['as_option'] - self.bgp_netxhop = self.module.params['bgp_nexthop'] - self.state = self.module.params['state'] - - self.commands = list() - self.config = None - self.exist_conf = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def get_netstream_config(self): - """get current netstream configuration""" - - cmd = "display current-configuration | include ^netstream export" - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - config = str(out).strip() - return config - - def get_existing(self): - """get existing config""" - - self.existing = dict(type=self.type, - source_ip=self.exist_conf['source_ip'], - host_ip=self.exist_conf['host_ip'], - host_port=self.exist_conf['host_port'], - host_vpn=self.exist_conf['host_vpn'], - version=self.exist_conf['version'], - as_option=self.exist_conf['as_option'], - bgp_nexthop=self.exist_conf['bgp_netxhop']) - - def get_proposed(self): - """get proposed config""" - - self.proposed = dict(type=self.type, - source_ip=self.source_ip, - host_ip=self.host_ip, - host_port=self.host_port, - host_vpn=self.host_vpn, - version=self.version, - as_option=self.as_option, - bgp_nexthop=self.bgp_netxhop, - state=self.state) - - def get_end_state(self): - """get end config""" - self.get_config_data() - self.end_state = dict(type=self.type, - source_ip=self.exist_conf['source_ip'], - host_ip=self.exist_conf['host_ip'], - host_port=self.exist_conf['host_port'], - host_vpn=self.exist_conf['host_vpn'], - version=self.exist_conf['version'], - as_option=self.exist_conf['as_option'], - bgp_nexthop=self.exist_conf['bgp_netxhop']) - - def show_result(self): - """show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - if command.lower() not in ["quit", "return"]: - if cmd not in self.updates_cmd: - self.updates_cmd.append(cmd) # show updates result - - def config_nets_export_src_addr(self): - """Configures the source address for the exported packets""" - - if is_ipv4_addr(self.source_ip): - if self.type == 'ip': - cmd = "netstream export ip source %s" % self.source_ip - else: - cmd = "netstream export vxlan inner-ip source %s" % self.source_ip - else: - if self.type == 'ip': - cmd = "netstream export ip source ipv6 %s" % self.source_ip - else: - cmd = "netstream export vxlan inner-ip source ipv6 %s" % self.source_ip - - if is_config_exist(self.config, cmd): - self.exist_conf['source_ip'] = self.source_ip - if self.state == 'present': - return - else: - undo = True - else: - if self.state == 'absent': - return - else: - undo = False - - self.cli_add_command(cmd, undo) - - def config_nets_export_host_addr(self): - """Configures the destination IP address and destination UDP port number""" - - if is_ipv4_addr(self.host_ip): - if self.type == 'ip': - cmd = 'netstream export ip host %s %s' % (self.host_ip, self.host_port) - else: - cmd = 'netstream export vxlan inner-ip host %s %s' % (self.host_ip, self.host_port) - else: - if self.type == 'ip': - cmd = 'netstream export ip host ipv6 %s %s' % (self.host_ip, self.host_port) - else: - cmd = 'netstream export vxlan inner-ip host ipv6 %s %s' % (self.host_ip, self.host_port) - - if self.host_vpn: - cmd += " vpn-instance %s" % self.host_vpn - - if is_config_exist(self.config, cmd): - self.exist_conf['host_ip'] = self.host_ip - self.exist_conf['host_port'] = self.host_port - if self.host_vpn: - self.exist_conf['host_vpn'] = self.host_vpn - - if self.state == 'present': - return - else: - undo = True - else: - if self.state == 'absent': - return - else: - undo = False - - self.cli_add_command(cmd, undo) - - def config_nets_export_vxlan_ver(self): - """Configures the version for the exported packets carrying VXLAN flexible flow statistics""" - - cmd = 'netstream export vxlan inner-ip version 9' - - if is_config_exist(self.config, cmd): - self.exist_conf['version'] = self.version - - if self.state == 'present': - return - else: - undo = True - else: - if self.state == 'absent': - return - else: - undo = False - - self.cli_add_command(cmd, undo) - - def config_nets_export_ip_ver(self): - """Configures the version number of the exported packets carrying IPv4 flow statistics""" - - cmd = 'netstream export ip version %s' % self.version - if self.version == '5': - if self.as_option == 'origin': - cmd += ' origin-as' - elif self.as_option == 'peer': - cmd += ' peer-as' - else: - if self.as_option == 'origin': - cmd += ' origin-as' - elif self.as_option == 'peer': - cmd += ' peer-as' - - if self.bgp_netxhop == 'enable': - cmd += ' bgp-nexthop' - - if cmd == 'netstream export ip version 5': - cmd_tmp = "netstream export ip version" - if cmd_tmp in self.config: - if self.state == 'present': - self.cli_add_command(cmd, False) - else: - self.exist_conf['version'] = self.version - return - - if is_config_exist(self.config, cmd): - self.exist_conf['version'] = self.version - self.exist_conf['as_option'] = self.as_option - self.exist_conf['bgp_netxhop'] = self.bgp_netxhop - - if self.state == 'present': - return - else: - undo = True - else: - if self.state == 'absent': - return - else: - undo = False - - self.cli_add_command(cmd, undo) - - def config_netstream_export(self): - """configure netstream export""" - - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - - def check_params(self): - """Check all input params""" - - if not self.type: - self.module.fail_json(msg='Error: The value of type cannot be empty.') - - if self.host_port: - if not self.host_port.isdigit(): - self.module.fail_json(msg='Error: Host port is invalid.') - if int(self.host_port) < 1 or int(self.host_port) > 65535: - self.module.fail_json(msg='Error: Host port is not in the range from 1 to 65535.') - - if self.host_vpn: - if self.host_vpn == '_public_': - self.module.fail_json( - msg='Error: The host vpn name _public_ is reserved.') - if len(self.host_vpn) < 1 or len(self.host_vpn) > 31: - self.module.fail_json(msg='Error: The host vpn name length is not in the range from 1 to 31.') - - if self.type == 'vxlan' and self.version == '5': - self.module.fail_json(msg="Error: When type is vxlan, version must be 9.") - - if self.type == 'ip' and self.version == '5' and self.bgp_netxhop == 'enable': - self.module.fail_json(msg="Error: When type=ip and version=5, bgp_netxhop is not supported.") - - if (self.host_ip and not self.host_port) or (self.host_port and not self.host_ip): - self.module.fail_json(msg="Error: host_ip and host_port must both exist or not exist.") - - def get_config_data(self): - """get configuration commands and current configuration""" - - self.exist_conf['type'] = self.type - self.exist_conf['source_ip'] = None - self.exist_conf['host_ip'] = None - self.exist_conf['host_port'] = None - self.exist_conf['host_vpn'] = None - self.exist_conf['version'] = None - self.exist_conf['as_option'] = None - self.exist_conf['bgp_netxhop'] = 'disable' - - self.config = self.get_netstream_config() - - if self.type and self.source_ip: - self.config_nets_export_src_addr() - - if self.type and self.host_ip and self.host_port: - self.config_nets_export_host_addr() - - if self.type == 'vxlan' and self.version == '9': - self.config_nets_export_vxlan_ver() - - if self.type == 'ip' and self.version: - self.config_nets_export_ip_ver() - - def work(self): - """execute task""" - - self.check_params() - self.get_proposed() - self.get_config_data() - self.get_existing() - - self.config_netstream_export() - - self.get_end_state() - self.show_result() - - -def main(): - """main function entry""" - - argument_spec = dict( - type=dict(required=True, type='str', choices=['ip', 'vxlan']), - source_ip=dict(required=False, type='str'), - host_ip=dict(required=False, type='str'), - host_port=dict(required=False, type='str'), - host_vpn=dict(required=False, type='str'), - version=dict(required=False, type='str', choices=['5', '9']), - as_option=dict(required=False, type='str', choices=['origin', 'peer']), - bgp_nexthop=dict(required=False, type='str', choices=['enable', 'disable'], default='disable'), - state=dict(choices=['absent', 'present'], default='present', required=False) - ) - argument_spec.update(ce_argument_spec) - netstream_export = NetstreamExport(argument_spec) - netstream_export.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_netstream_global.py b/plugins/modules/network/cloudengine/ce_netstream_global.py deleted file mode 100644 index ac8c61e06f..0000000000 --- a/plugins/modules/network/cloudengine/ce_netstream_global.py +++ /dev/null @@ -1,946 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_netstream_global -short_description: Manages global parameters of NetStream on HUAWEI CloudEngine switches. -description: - - Manages global parameters of NetStream on HUAWEI CloudEngine switches. -author: YangYang (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - type: - description: - - Specifies the type of netstream global. - choices: ['ip', 'vxlan'] - default: 'ip' - state: - description: - - Specify desired state of the resource. - choices: ['present', 'absent'] - default: present - interface: - description: - - Netstream global interface. - required: true - sampler_interval: - description: - - Specifies the netstream sampler interval, length is 1 - 65535. - sampler_direction: - description: - - Specifies the netstream sampler direction. - choices: ['inbound', 'outbound'] - statistics_direction: - description: - - Specifies the netstream statistic direction. - choices: ['inbound', 'outbound'] - statistics_record: - description: - - Specifies the flexible netstream statistic record, length is 1 - 32. - index_switch: - description: - - Specifies the netstream index-switch. - choices: ['16', '32'] - default: '16' -''' - -EXAMPLES = ''' -- name: netstream global module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Configure a netstream sampler at interface 10ge1/0/2, direction is outbound,interval is 30. - ce_netstream_global: - interface: 10ge1/0/2 - type: ip - sampler_interval: 30 - sampler_direction: outbound - state: present - provider: "{{ cli }}" - - name: Configure a netstream flexible statistic at interface 10ge1/0/2, record is test1, type is ip. - ce_netstream_global: - type: ip - interface: 10ge1/0/2 - statistics_record: test1 - provider: "{{ cli }}" - - name: Set the vxlan index-switch to 32. - ce_netstream_global: - type: vxlan - interface: all - index_switch: 32 - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"index_switch": "16", - "interface": "10ge1/0/2", - "state": "present", - "statistics_record": "test", - "type": "vxlan"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"flexible_statistic": [ - { - "interface": "10ge1/0/2", - "statistics_record": [], - "type": "ip" - }, - { - "interface": "10ge1/0/2", - "statistics_record": [], - "type": "vxlan" - } - ], - "index-switch": [ - { - "index-switch": "16", - "type": "ip" - }, - { - "index-switch": "16", - "type": "vxlan" - } - ], - "ip_record": [ - "test", - "test1" - ], - "sampler": [ - { - "interface": "all", - "sampler_direction": "null", - "sampler_interval": "null" - } - ], - "statistic": [ - { - "interface": "10ge1/0/2", - "statistics_direction": [], - "type": "null" - } - ], - "vxlan_record": [ - "test" - ]} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"flexible_statistic": [ - { - "interface": "10ge1/0/2", - "statistics_record": [], - "type": "ip" - }, - { - "interface": "10ge1/0/2", - "statistics_record": [ - "test" - ], - "type": "vxlan" - } - ], - "index-switch": [ - { - "index-switch": "16", - "type": "ip" - }, - { - "index-switch": "16", - "type": "vxlan" - } - ], - "sampler": [ - { - "interface": "all", - "sampler_direction": "null", - "sampler_interval": "null" - } - ], - "statistic": [ - { - "interface": "10ge1/0/2", - "statistics_direction": [], - "type": "null" - } - ]} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["interface 10ge1/0/2", - "netstream record test vxlan inner-ip"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_connection, rm_config_prefix -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('ALL'): - iftype = 'all' - else: - return None - - return iftype.lower() - - -def get_config(module, flags): - - """Retrieves the current config from the device or cache - """ - time_stamp_regex = re.compile(r'\s*\d{4}-\d{1,2}-\d{1,2}\s+\d{2}\:\d{2}\:\d{2}\.\d+\s*') - flags = [] if flags is None else flags - if isinstance(flags, str): - flags = [flags] - elif not isinstance(flags, list): - flags = [] - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - conn = get_connection(module) - rc, out, err = conn.exec_command(cmd) - if rc != 0: - module.fail_json(msg=err) - cfg = str(out).strip() - # remove default configuration prefix '~' - for flag in flags: - if "include-default" in flag: - cfg = rm_config_prefix(cfg) - break - lines = cfg.split('\n') - lines = [l for l in lines if time_stamp_regex.match(l) is None] - if cfg.startswith('display'): - if len(lines) > 1: - lines.pop(0) - else: - return '' - return '\n'.join(lines) - - -class NetStreamGlobal(object): - """ - Manages netstream global parameters. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.type = self.module.params['type'] - self.interface = self.module.params['interface'] - self.sampler_interval = self.module.params['sampler_interval'] - self.sampler_direction = self.module.params['sampler_direction'] - self.statistics_direction = self.module.params['statistics_direction'] - self.statistics_record = self.module.params['statistics_record'] - self.index_switch = self.module.params['index_switch'] - self.state = self.module.params['state'] - - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - # local parameters - self.existing["sampler"] = list() - self.existing["statistic"] = list() - self.existing["flexible_statistic"] = list() - self.existing["index-switch"] = list() - self.existing["ip_record"] = list() - self.existing["vxlan_record"] = list() - self.end_state["sampler"] = list() - self.end_state["statistic"] = list() - self.end_state["flexible_statistic"] = list() - self.end_state["index-switch"] = list() - self.sampler_changed = False - self.statistic_changed = False - self.flexible_changed = False - self.index_switch_changed = False - - def init_module(self): - """init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) - - def get_exist_sampler_interval(self): - """get exist netstream sampler interval""" - - sampler_tmp = dict() - sampler_tmp1 = dict() - flags = list() - exp = " | ignore-case include ^netstream sampler random-packets" - flags.append(exp) - config = get_config(self.module, flags) - if not config: - sampler_tmp["sampler_interval"] = "null" - sampler_tmp["sampler_direction"] = "null" - sampler_tmp["interface"] = "null" - else: - config_list = config.split(' ') - config_num = len(config_list) - sampler_tmp["sampler_direction"] = config_list[config_num - 1] - sampler_tmp["sampler_interval"] = config_list[config_num - 2] - sampler_tmp["interface"] = "all" - self.existing["sampler"].append(sampler_tmp) - if self.interface != "all": - flags = list() - exp = r" | ignore-case section include ^#\s+interface %s" \ - r" | include netstream sampler random-packets" % self.interface - flags.append(exp) - config = get_config(self.module, flags) - if not config: - sampler_tmp1["sampler_interval"] = "null" - sampler_tmp1["sampler_direction"] = "null" - else: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - sampler_tmp1 = dict() - config_mem_list = config_mem.split(' ') - config_num = len(config_mem_list) - if config_num > 1: - sampler_tmp1["sampler_direction"] = config_mem_list[ - config_num - 1] - sampler_tmp1["sampler_interval"] = config_mem_list[ - config_num - 2] - sampler_tmp1["interface"] = self.interface - self.existing["sampler"].append(sampler_tmp1) - - def get_exist_statistic_record(self): - """get exist netstream statistic record parameter""" - - if self.statistics_record and self.statistics_direction: - self.module.fail_json( - msg='Error: The statistic direction and record can not exist at the same time.') - statistic_tmp = dict() - statistic_tmp1 = dict() - statistic_tmp["statistics_record"] = list() - statistic_tmp["interface"] = self.interface - statistic_tmp1["statistics_record"] = list() - statistic_tmp1["interface"] = self.interface - flags = list() - exp = r" | ignore-case section include ^#\s+interface %s" \ - r" | include netstream record"\ - % (self.interface) - flags.append(exp) - config = get_config(self.module, flags) - if not config: - statistic_tmp["type"] = "ip" - self.existing["flexible_statistic"].append(statistic_tmp) - statistic_tmp1["type"] = "vxlan" - self.existing["flexible_statistic"].append(statistic_tmp1) - else: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem = config_mem.lstrip() - statistic_tmp["statistics_record"] = list() - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 3 and str(config_mem_list[3]) == "ip": - statistic_tmp["statistics_record"].append( - str(config_mem_list[2])) - statistic_tmp["type"] = "ip" - self.existing["flexible_statistic"].append(statistic_tmp) - for config_mem in config_list: - statistic_tmp1["statistics_record"] = list() - config_mem = config_mem.lstrip() - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 3 and str(config_mem_list[3]) == "vxlan": - statistic_tmp1["statistics_record"].append( - str(config_mem_list[2])) - statistic_tmp1["type"] = "vxlan" - self.existing["flexible_statistic"].append(statistic_tmp1) - - def get_exist_interface_statistic(self): - """get exist netstream interface statistic parameter""" - - statistic_tmp1 = dict() - statistic_tmp1["statistics_direction"] = list() - flags = list() - exp = r" | ignore-case section include ^#\s+interface %s" \ - r" | include netstream inbound|outbound"\ - % self.interface - flags.append(exp) - config = get_config(self.module, flags) - if not config: - statistic_tmp1["type"] = "null" - else: - statistic_tmp1["type"] = "ip" - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem = config_mem.lstrip() - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 1: - statistic_tmp1["statistics_direction"].append( - str(config_mem_list[1])) - statistic_tmp1["interface"] = self.interface - self.existing["statistic"].append(statistic_tmp1) - - def get_exist_index_switch(self): - """get exist netstream index-switch""" - - index_switch_tmp = dict() - index_switch_tmp1 = dict() - index_switch_tmp["index-switch"] = "16" - index_switch_tmp["type"] = "ip" - index_switch_tmp1["index-switch"] = "16" - index_switch_tmp1["type"] = "vxlan" - flags = list() - exp = " | ignore-case include index-switch" - flags.append(exp) - config = get_config(self.module, flags) - if not config: - self.existing["index-switch"].append(index_switch_tmp) - self.existing["index-switch"].append(index_switch_tmp1) - else: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 2 and str(config_mem_list[2]) == "ip": - index_switch_tmp["index-switch"] = "32" - index_switch_tmp["type"] = "ip" - if len(config_mem_list) > 2 and str(config_mem_list[2]) == "vxlan": - index_switch_tmp1["index-switch"] = "32" - index_switch_tmp1["type"] = "vxlan" - self.existing["index-switch"].append(index_switch_tmp) - self.existing["index-switch"].append(index_switch_tmp1) - - def get_exist_record(self): - """get exist netstream record""" - - flags = list() - exp = " | ignore-case include netstream record" - flags.append(exp) - config = get_config(self.module, flags) - if config: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 3 and config_mem_list[3] == "ip": - self.existing["ip_record"].append(config_mem_list[2]) - if len(config_mem_list) > 3 and config_mem_list[3] == "vxlan": - self.existing["vxlan_record"].append(config_mem_list[2]) - - def get_end_sampler_interval(self): - """get end netstream sampler interval""" - - sampler_tmp = dict() - sampler_tmp1 = dict() - flags = list() - exp = " | ignore-case include ^netstream sampler random-packets" - flags.append(exp) - config = get_config(self.module, flags) - if not config: - sampler_tmp["sampler_interval"] = "null" - sampler_tmp["sampler_direction"] = "null" - else: - config_list = config.split(' ') - config_num = len(config_list) - if config_num > 1: - sampler_tmp["sampler_direction"] = config_list[config_num - 1] - sampler_tmp["sampler_interval"] = config_list[config_num - 2] - sampler_tmp["interface"] = "all" - self.end_state["sampler"].append(sampler_tmp) - if self.interface != "all": - flags = list() - exp = r" | ignore-case section include ^#\s+interface %s" \ - r" | include netstream sampler random-packets" % self.interface - flags.append(exp) - config = get_config(self.module, flags) - if not config: - sampler_tmp1["sampler_interval"] = "null" - sampler_tmp1["sampler_direction"] = "null" - else: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - sampler_tmp1 = dict() - config_mem_list = config_mem.split(' ') - config_num = len(config_mem_list) - if config_num > 1: - sampler_tmp1["sampler_direction"] = config_mem_list[ - config_num - 1] - sampler_tmp1["sampler_interval"] = config_mem_list[ - config_num - 2] - sampler_tmp1["interface"] = self.interface - self.end_state["sampler"].append(sampler_tmp1) - - def get_end_statistic_record(self): - """get end netstream statistic record parameter""" - - if self.statistics_record and self.statistics_direction: - self.module.fail_json( - msg='Error: The statistic direction and record can not exist at the same time.') - statistic_tmp = dict() - statistic_tmp1 = dict() - statistic_tmp["statistics_record"] = list() - statistic_tmp["interface"] = self.interface - statistic_tmp1["statistics_record"] = list() - statistic_tmp1["interface"] = self.interface - flags = list() - exp = r" | ignore-case section include ^#\s+interface %s" \ - r" | include netstream record"\ - % (self.interface) - flags.append(exp) - config = get_config(self.module, flags) - if not config: - statistic_tmp["type"] = "ip" - self.end_state["flexible_statistic"].append(statistic_tmp) - statistic_tmp1["type"] = "vxlan" - self.end_state["flexible_statistic"].append(statistic_tmp1) - else: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem = config_mem.lstrip() - statistic_tmp["statistics_record"] = list() - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 3 and str(config_mem_list[3]) == "ip": - statistic_tmp["statistics_record"].append( - str(config_mem_list[2])) - statistic_tmp["type"] = "ip" - self.end_state["flexible_statistic"].append(statistic_tmp) - for config_mem in config_list: - statistic_tmp1["statistics_record"] = list() - config_mem = config_mem.lstrip() - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 3 and str(config_mem_list[3]) == "vxlan": - statistic_tmp1["statistics_record"].append( - str(config_mem_list[2])) - statistic_tmp1["type"] = "vxlan" - self.end_state["flexible_statistic"].append(statistic_tmp1) - - def get_end_interface_statistic(self): - """get end netstream interface statistic parameters""" - - statistic_tmp1 = dict() - statistic_tmp1["statistics_direction"] = list() - flags = list() - exp = r" | ignore-case section include ^#\s+interface %s" \ - r" | include netstream inbound|outbound"\ - % self.interface - flags.append(exp) - config = get_config(self.module, flags) - if not config: - statistic_tmp1["type"] = "null" - else: - statistic_tmp1["type"] = "ip" - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem = config_mem.lstrip() - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 1: - statistic_tmp1["statistics_direction"].append( - str(config_mem_list[1])) - statistic_tmp1["interface"] = self.interface - self.end_state["statistic"].append(statistic_tmp1) - - def get_end_index_switch(self): - """get end netstream index switch""" - - index_switch_tmp = dict() - index_switch_tmp1 = dict() - index_switch_tmp["index-switch"] = "16" - index_switch_tmp["type"] = "ip" - index_switch_tmp1["index-switch"] = "16" - index_switch_tmp1["type"] = "vxlan" - flags = list() - exp = " | ignore-case include index-switch" - flags.append(exp) - config = get_config(self.module, flags) - if not config: - self.end_state["index-switch"].append(index_switch_tmp) - self.end_state["index-switch"].append(index_switch_tmp1) - else: - config = config.lstrip() - config_list = config.split('\n') - for config_mem in config_list: - config_mem_list = config_mem.split(' ') - if len(config_mem_list) > 2 and str(config_mem_list[2]) == "ip": - index_switch_tmp["index-switch"] = "32" - index_switch_tmp["type"] = "ip" - if len(config_mem_list) > 2 and str(config_mem_list[2]) == "vxlan": - index_switch_tmp1["index-switch"] = "32" - index_switch_tmp1["type"] = "vxlan" - self.end_state["index-switch"].append(index_switch_tmp) - self.end_state["index-switch"].append(index_switch_tmp1) - - def check_params(self): - """check all input params""" - - # netstream parameters check - if not get_interface_type(self.interface): - self.module.fail_json( - msg='Error: Interface name of %s is error.' % self.interface) - if self.sampler_interval: - if not str(self.sampler_interval).isdigit(): - self.module.fail_json( - msg='Error: Active interval should be numerical.') - if int(self.sampler_interval) < 1 or int(self.sampler_interval) > 65535: - self.module.fail_json( - msg="Error: Sampler interval should between 1 - 65535.") - if self.statistics_record: - if len(self.statistics_record) < 1 or len(self.statistics_record) > 32: - self.module.fail_json( - msg="Error: Statistic record length should between 1 - 32.") - if self.interface == "all": - if self.statistics_record or self.statistics_direction: - self.module.fail_json( - msg="Error: Statistic function should be used at interface.") - if self.statistics_direction: - if self.type == "vxlan": - self.module.fail_json( - msg="Error: Vxlan do not support inbound or outbound statistic.") - if (self.sampler_interval and not self.sampler_direction) \ - or (self.sampler_direction and not self.sampler_interval): - self.module.fail_json( - msg="Error: Sampler interval and direction must be set at the same time.") - - if self.statistics_record and not self.type: - self.module.fail_json( - msg="Error: Statistic type and record must be set at the same time.") - - self.get_exist_record() - if self.statistics_record: - if self.type == "ip": - if self.statistics_record not in self.existing["ip_record"]: - self.module.fail_json( - msg="Error: The statistic record is not exist.") - if self.type == "vxlan": - if self.statistics_record not in self.existing["vxlan_record"]: - self.module.fail_json( - msg="Error: The statistic record is not exist.") - - def get_proposed(self): - """get proposed info""" - - if self.type: - self.proposed["type"] = self.type - if self.interface: - self.proposed["interface"] = self.interface - if self.sampler_interval: - self.proposed["sampler_interval"] = self.sampler_interval - if self.sampler_direction: - self.proposed["sampler_direction"] = self.sampler_direction - if self.statistics_direction: - self.proposed["statistics_direction"] = self.statistics_direction - if self.statistics_record: - self.proposed["statistics_record"] = self.statistics_record - if self.index_switch: - self.proposed["index_switch"] = self.index_switch - if self.state: - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - sampler_tmp = dict() - statistic_tmp = dict() - statistic_tmp1 = dict() - index_tmp = dict() - temp = False - - self.get_exist_sampler_interval() - self.get_exist_interface_statistic() - self.get_exist_statistic_record() - self.get_exist_index_switch() - - if self.state == "present": - for sampler_tmp in self.existing["sampler"]: - if self.interface == str(sampler_tmp["interface"]): - temp = True - if (self.sampler_interval and str(sampler_tmp["sampler_interval"]) != self.sampler_interval) \ - or (self.sampler_direction and - str(sampler_tmp["sampler_direction"]) != self.sampler_direction): - self.sampler_changed = True - if not temp: - if self.sampler_direction or self.sampler_interval: - self.sampler_changed = True - for statistic_tmp in self.existing["statistic"]: - if str(statistic_tmp["interface"]) == self.interface and self.interface != "all": - if self.type == "vxlan": - if statistic_tmp["statistics_direction"] \ - and 'outbound' in statistic_tmp["statistics_direction"]: - self.module.fail_json( - msg='Error: The NetStream record vxlan ' - 'cannot be configured because the port has been configured NetStream outbound ip.') - if statistic_tmp["statistics_direction"] and self.statistics_direction: - if self.statistics_direction not in statistic_tmp["statistics_direction"]: - self.statistic_changed = True - else: - if self.statistics_direction: - self.statistic_changed = True - for statistic_tmp1 in self.existing["flexible_statistic"]: - if self.interface != "all" \ - and self.type == str(statistic_tmp1["type"]) \ - and self.interface == str(statistic_tmp1["interface"]): - if statistic_tmp1["statistics_record"] and self.statistics_record: - if self.statistics_record not in statistic_tmp1["statistics_record"]: - self.flexible_changed = True - else: - if self.statistics_record: - self.flexible_changed = True - for index_tmp in self.existing["index-switch"]: - if self.type == str(index_tmp["type"]): - if self.index_switch != str(index_tmp["index-switch"]): - self.index_switch_changed = True - else: - for sampler_tmp in self.existing["sampler"]: - if self.interface == str(sampler_tmp["interface"]): - if (self.sampler_interval and str(sampler_tmp["sampler_interval"]) == self.sampler_interval) \ - and (self.sampler_direction and str(sampler_tmp["sampler_direction"]) == self.sampler_direction): - self.sampler_changed = True - for statistic_tmp in self.existing["statistic"]: - if str(statistic_tmp["interface"]) == self.interface and self.interface != "all": - if len(statistic_tmp["statistics_direction"]) and self.statistics_direction: - if self.statistics_direction in statistic_tmp["statistics_direction"]: - self.statistic_changed = True - for statistic_tmp1 in self.existing["flexible_statistic"]: - if self.interface != "all" \ - and self.type == str(statistic_tmp1["type"]) \ - and self.interface == str(statistic_tmp1["interface"]): - if len(statistic_tmp1["statistics_record"]) and self.statistics_record: - if self.statistics_record in statistic_tmp1["statistics_record"]: - self.flexible_changed = True - for index_tmp in self.existing["index-switch"]: - if self.type == str(index_tmp["type"]): - if self.index_switch == str(index_tmp["index-switch"]): - if self.index_switch != "16": - self.index_switch_changed = True - - def operate_ns_gloabl(self): - """configure netstream global parameters""" - - cmd = "" - if not self.sampler_changed and not self.statistic_changed \ - and not self.flexible_changed and not self.index_switch_changed: - self.changed = False - return - - if self.sampler_changed is True: - if self.type == "vxlan": - self.module.fail_json( - msg="Error: Netstream do not support vxlan sampler.") - if self.interface != "all": - cmd = "interface %s" % self.interface - self.cli_add_command(cmd) - cmd = "netstream sampler random-packets %s %s" % ( - self.sampler_interval, self.sampler_direction) - if self.state == "present": - self.cli_add_command(cmd) - else: - self.cli_add_command(cmd, undo=True) - if self.interface != "all": - cmd = "quit" - self.cli_add_command(cmd) - if self.statistic_changed is True: - if self.interface != "all": - cmd = "interface %s" % self.interface - self.cli_add_command(cmd) - cmd = "netstream %s ip" % self.statistics_direction - if self.state == "present": - self.cli_add_command(cmd) - else: - self.cli_add_command(cmd, undo=True) - if self.interface != "all": - cmd = "quit" - self.cli_add_command(cmd) - if self.flexible_changed is True: - if self.interface != "all": - cmd = "interface %s" % self.interface - self.cli_add_command(cmd) - if self.state == "present": - for statistic_tmp in self.existing["flexible_statistic"]: - tmp_list = statistic_tmp["statistics_record"] - if self.type == statistic_tmp["type"]: - if self.type == "ip": - if len(tmp_list) > 0: - cmd = "netstream record %s ip" % tmp_list[0] - self.cli_add_command(cmd, undo=True) - cmd = "netstream record %s ip" % self.statistics_record - self.cli_add_command(cmd) - if self.type == "vxlan": - if len(tmp_list) > 0: - cmd = "netstream record %s vxlan inner-ip" % tmp_list[ - 0] - self.cli_add_command(cmd, undo=True) - cmd = "netstream record %s vxlan inner-ip" % self.statistics_record - self.cli_add_command(cmd) - else: - if self.type == "ip": - cmd = "netstream record %s ip" % self.statistics_record - self.cli_add_command(cmd, undo=True) - if self.type == "vxlan": - cmd = "netstream record %s vxlan inner-ip" % self.statistics_record - self.cli_add_command(cmd, undo=True) - if self.interface != "all": - cmd = "quit" - self.cli_add_command(cmd) - if self.index_switch_changed is True: - if self.interface != "all": - self.module.fail_json( - msg="Error: Index-switch function should be used globally.") - if self.type == "ip": - cmd = "netstream export ip index-switch %s" % self.index_switch - else: - cmd = "netstream export vxlan inner-ip index-switch %s" % self.index_switch - if self.state == "present": - self.cli_add_command(cmd) - else: - self.cli_add_command(cmd, undo=True) - - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - - def get_end_state(self): - """get end state info""" - - self.get_end_sampler_interval() - self.get_end_interface_statistic() - self.get_end_statistic_record() - self.get_end_index_switch() - - def work(self): - """worker""" - - self.check_params() - self.get_existing() - self.get_proposed() - self.operate_ns_gloabl() - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - type=dict(required=False, choices=['ip', 'vxlan'], default='ip'), - interface=dict(required=True, type='str'), - sampler_interval=dict(required=False, type='str'), - sampler_direction=dict(required=False, choices=['inbound', 'outbound']), - statistics_direction=dict(required=False, choices=['inbound', 'outbound']), - statistics_record=dict(required=False, type='str'), - index_switch=dict(required=False, choices=['16', '32'], default='16'), - state=dict(required=False, choices=['present', 'absent'], default='present'), - ) - argument_spec.update(ce_argument_spec) - module = NetStreamGlobal(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_netstream_template.py b/plugins/modules/network/cloudengine/ce_netstream_template.py deleted file mode 100644 index 5ea943fa6e..0000000000 --- a/plugins/modules/network/cloudengine/ce_netstream_template.py +++ /dev/null @@ -1,498 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_netstream_template -short_description: Manages NetStream template configuration on HUAWEI CloudEngine switches. -description: - - Manages NetStream template configuration on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present', 'absent'] - type: - description: - - Configure the type of netstream record. - required: true - choices: ['ip', 'vxlan'] - record_name: - description: - - Configure the name of netstream record. - The value is a string of 1 to 32 case-insensitive characters. - match: - description: - - Configure flexible flow statistics template keywords. - choices: ['destination-address', 'destination-port', 'tos', 'protocol', 'source-address', 'source-port'] - collect_counter: - description: - - Configure the number of packets and bytes that are included in the flexible flow statistics sent to NSC. - choices: ['bytes', 'packets'] - collect_interface: - description: - - Configure the input or output interface that are included in the flexible flow statistics sent to NSC. - choices: ['input', 'output'] - description: - description: - - Configure the description of netstream record. - The value is a string of 1 to 80 case-insensitive characters. -''' - -EXAMPLES = ''' -- name: netstream template module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Config ipv4 netstream record - ce_netstream_template: - state: present - type: ip - record_name: test - provider: "{{ cli }}" - - name: Undo ipv4 netstream record - ce_netstream_template: - state: absent - type: ip - record_name: test - provider: "{{ cli }}" - - name: Config ipv4 netstream record collect_counter - ce_netstream_template: - state: present - type: ip - record_name: test - collect_counter: bytes - provider: "{{ cli }}" - - name: Undo ipv4 netstream record collect_counter - ce_netstream_template: - state: absent - type: ip - record_name: test - collect_counter: bytes - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"record_name": "test", - "type": "ip", - "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"record_name": "test", - "type": "ip"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["netstream record test ip"] -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_connection, rm_config_prefix -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec - - -def get_config(module, flags): - - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - if isinstance(flags, str): - flags = [flags] - elif not isinstance(flags, list): - flags = [] - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - conn = get_connection(module) - rc, out, err = conn.exec_command(cmd) - if rc != 0: - module.fail_json(msg=err) - cfg = str(out).strip() - # remove default configuration prefix '~' - for flag in flags: - if "include-default" in flag: - cfg = rm_config_prefix(cfg) - break - if cfg.startswith('display'): - lines = cfg.split('\n') - if len(lines) > 1: - return '\n'.join(lines[1:]) - else: - return '' - return cfg - - -class NetstreamTemplate(object): - """ Manages netstream template configuration """ - - def __init__(self, **kwargs): - """ Netstream template module init """ - - # module - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # netstream config - self.netstream_cfg = None - - # module args - self.state = self.module.params['state'] or None - self.type = self.module.params['type'] or None - self.record_name = self.module.params['record_name'] or None - self.match = self.module.params['match'] or None - self.collect_counter = self.module.params['collect_counter'] or None - self.collect_interface = self.module.params['collect_interface'] or None - self.description = self.module.params['description'] or None - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def cli_load_config(self, commands): - """ Cli load configuration """ - - if not self.module.check_mode: - load_config(self.module, commands) - - def cli_get_netstream_config(self): - """ Cli get netstream configuration """ - - if self.type == "ip": - cmd = "netstream record %s ip" % self.record_name - else: - cmd = "netstream record %s vxlan inner-ip" % self.record_name - flags = list() - regular = "| section include %s" % cmd - flags.append(regular) - self.netstream_cfg = get_config(self.module, flags) - - def check_args(self): - """ Check module args """ - - if not self.type or not self.record_name: - self.module.fail_json( - msg='Error: Please input type and record_name.') - - if self.record_name: - if len(self.record_name) < 1 or len(self.record_name) > 32: - self.module.fail_json( - msg='Error: The len of record_name is out of [1 - 32].') - - if self.description: - if len(self.description) < 1 or len(self.description) > 80: - self.module.fail_json( - msg='Error: The len of description is out of [1 - 80].') - - def get_proposed(self): - """ Get module proposed """ - - self.proposed["state"] = self.state - - if self.type: - self.proposed["type"] = self.type - if self.record_name: - self.proposed["record_name"] = self.record_name - if self.match: - self.proposed["match"] = self.match - if self.collect_counter: - self.proposed["collect_counter"] = self.collect_counter - if self.collect_interface: - self.proposed["collect_interface"] = self.collect_interface - if self.description: - self.proposed["description"] = self.description - - def get_existing(self): - """ Get existing configuration """ - - self.cli_get_netstream_config() - - if self.netstream_cfg is not None and "netstream record" in self.netstream_cfg: - self.existing["type"] = self.type - self.existing["record_name"] = self.record_name - - if self.description: - tmp_value = re.findall(r'description (.*)', self.netstream_cfg) - if tmp_value is not None and len(tmp_value) > 0: - self.existing["description"] = tmp_value[0] - - if self.match: - if self.type == "ip": - tmp_value = re.findall(r'match ip (.*)', self.netstream_cfg) - else: - tmp_value = re.findall(r'match inner-ip (.*)', self.netstream_cfg) - - if tmp_value: - self.existing["match"] = tmp_value - - if self.collect_counter: - tmp_value = re.findall(r'collect counter (.*)', self.netstream_cfg) - if tmp_value: - self.existing["collect_counter"] = tmp_value - - if self.collect_interface: - tmp_value = re.findall(r'collect interface (.*)', self.netstream_cfg) - if tmp_value: - self.existing["collect_interface"] = tmp_value - - def get_end_state(self): - """ Get end state """ - - self.cli_get_netstream_config() - - if self.netstream_cfg is not None and "netstream record" in self.netstream_cfg: - self.end_state["type"] = self.type - self.end_state["record_name"] = self.record_name - - if self.description: - tmp_value = re.findall(r'description (.*)', self.netstream_cfg) - if tmp_value is not None and len(tmp_value) > 0: - self.end_state["description"] = tmp_value[0] - - if self.match: - if self.type == "ip": - tmp_value = re.findall(r'match ip (.*)', self.netstream_cfg) - else: - tmp_value = re.findall(r'match inner-ip (.*)', self.netstream_cfg) - - if tmp_value: - self.end_state["match"] = tmp_value - - if self.collect_counter: - tmp_value = re.findall(r'collect counter (.*)', self.netstream_cfg) - if tmp_value: - self.end_state["collect_counter"] = tmp_value - - if self.collect_interface: - tmp_value = re.findall(r'collect interface (.*)', self.netstream_cfg) - if tmp_value: - self.end_state["collect_interface"] = tmp_value - if self.end_state == self.existing: - self.changed = False - self.updates_cmd = list() - - def present_netstream(self): - """ Present netstream configuration """ - - cmds = list() - need_create_record = False - - if self.type == "ip": - cmd = "netstream record %s ip" % self.record_name - else: - cmd = "netstream record %s vxlan inner-ip" % self.record_name - cmds.append(cmd) - - if self.existing.get('record_name') != self.record_name: - self.updates_cmd.append(cmd) - need_create_record = True - - if self.description: - cmd = "description %s" % self.description.strip() - if need_create_record or not self.netstream_cfg or cmd not in self.netstream_cfg: - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.match: - if self.type == "ip": - cmd = "match ip %s" % self.match - cfg = "match ip" - else: - cmd = "match inner-ip %s" % self.match - cfg = "match inner-ip" - - if need_create_record or cfg not in self.netstream_cfg or self.match != self.existing["match"][0]: - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.collect_counter: - cmd = "collect counter %s" % self.collect_counter - if need_create_record or cmd not in self.netstream_cfg: - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.collect_interface: - cmd = "collect interface %s" % self.collect_interface - if need_create_record or cmd not in self.netstream_cfg: - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if cmds: - self.cli_load_config(cmds) - self.changed = True - - def absent_netstream(self): - """ Absent netstream configuration """ - - cmds = list() - absent_netstream_attr = False - - if not self.netstream_cfg: - return - - if self.description or self.match or self.collect_counter or self.collect_interface: - absent_netstream_attr = True - - if absent_netstream_attr: - if self.type == "ip": - cmd = "netstream record %s ip" % self.record_name - else: - cmd = "netstream record %s vxlan inner-ip" % self.record_name - - cmds.append(cmd) - - if self.description: - cfg = "description %s" % self.description - if self.netstream_cfg and cfg in self.netstream_cfg: - cmd = "undo description %s" % self.description - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.match: - if self.type == "ip": - cfg = "match ip %s" % self.match - else: - cfg = "match inner-ip %s" % self.match - if self.netstream_cfg and cfg in self.netstream_cfg: - if self.type == "ip": - cmd = "undo match ip %s" % self.match - else: - cmd = "undo match inner-ip %s" % self.match - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.collect_counter: - cfg = "collect counter %s" % self.collect_counter - if self.netstream_cfg and cfg in self.netstream_cfg: - cmd = "undo collect counter %s" % self.collect_counter - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.collect_interface: - cfg = "collect interface %s" % self.collect_interface - if self.netstream_cfg and cfg in self.netstream_cfg: - cmd = "undo collect interface %s" % self.collect_interface - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if len(cmds) > 1: - self.cli_load_config(cmds) - self.changed = True - - else: - if self.type == "ip": - cmd = "undo netstream record %s ip" % self.record_name - else: - cmd = "undo netstream record %s vxlan inner-ip" % self.record_name - - cmds.append(cmd) - self.updates_cmd.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def work(self): - """ Work function """ - - self.check_args() - self.get_proposed() - self.get_existing() - - if self.state == "present": - self.present_netstream() - else: - self.absent_netstream() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - type=dict(choices=['ip', 'vxlan'], required=True), - record_name=dict(type='str'), - match=dict(choices=['destination-address', 'destination-port', - 'tos', 'protocol', 'source-address', 'source-port']), - collect_counter=dict(choices=['bytes', 'packets']), - collect_interface=dict(choices=['input', 'output']), - description=dict(type='str') - ) - argument_spec.update(ce_argument_spec) - module = NetstreamTemplate(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_ntp.py b/plugins/modules/network/cloudengine/ce_ntp.py deleted file mode 100644 index bc4a5a966c..0000000000 --- a/plugins/modules/network/cloudengine/ce_ntp.py +++ /dev/null @@ -1,619 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_ntp -short_description: Manages core NTP configuration on HUAWEI CloudEngine switches. -description: - - Manages core NTP configuration on HUAWEI CloudEngine switches. -author: - - Zhijin Zhou (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - server: - description: - - Network address of NTP server. - peer: - description: - - Network address of NTP peer. - key_id: - description: - - Authentication key identifier to use with given NTP server or peer. - is_preferred: - description: - - Makes given NTP server or peer the preferred NTP server or peer for the device. - choices: ['enable', 'disable'] - vpn_name: - description: - - Makes the device communicate with the given - NTP server or peer over a specific vpn. - default: '_public_' - source_int: - description: - - Local source interface from which NTP messages are sent. - Must be fully qualified interface name, i.e. C(40GE1/0/22), C(vlanif10). - Interface types, such as C(10GE), C(40GE), C(100GE), C(Eth-Trunk), C(LoopBack), - C(MEth), C(NULL), C(Tunnel), C(Vlanif). - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: NTP test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Set NTP Server with parameters" - ce_ntp: - server: 192.8.2.6 - vpn_name: js - source_int: vlanif4001 - is_preferred: enable - key_id: 32 - provider: "{{ cli }}" - - - name: "Set NTP Peer with parameters" - ce_ntp: - peer: 192.8.2.6 - vpn_name: js - source_int: vlanif4001 - is_preferred: enable - key_id: 32 - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"server": "2.2.2.2", "key_id": "48", - "is_preferred": "enable", "vpn_name":"js", - "source_int": "vlanif4002", "state":"present"} -existing: - description: k/v pairs of existing ntp server/peer - returned: always - type: dict - sample: {"server": "2.2.2.2", "key_id": "32", - "is_preferred": "disable", "vpn_name":"js", - "source_int": "vlanif4002"} -end_state: - description: k/v pairs of ntp info after module execution - returned: always - type: dict - sample: {"server": "2.2.2.2", "key_id": "48", - "is_preferred": "enable", "vpn_name":"js", - "source_int": "vlanif4002"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["ntp server 2.2.2.2 authentication-keyid 48 source-interface vlanif4002 vpn-instance js preferred"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, get_nc_config, set_nc_config - -CE_NC_GET_NTP_CONFIG = """ - - - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_NTP_CONFIG = """ - - - - - %s - %s - %s - %s - %s - %s - %s - %s - 0-0 - - - - -""" - -CE_NC_DELETE_NTP_CONFIG = """ - - - - - %s - %s - %s - %s - %s - 0-0 - - - - -""" - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-Port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class Ntp(object): - """Ntp class""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.mutually_exclusive = [('server', 'peer')] - self.init_module() - - # ntp configuration info - self.server = self.module.params['server'] or None - self.peer = self.module.params['peer'] or None - self.key_id = self.module.params['key_id'] - self.is_preferred = self.module.params['is_preferred'] - self.vpn_name = self.module.params['vpn_name'] - self.interface = self.module.params['source_int'] or "" - self.state = self.module.params['state'] - self.ntp_conf = dict() - self.conf_exsit = False - self.ip_ver = 'IPv4' - - if self.server: - self.peer_type = 'Server' - self.address = self.server - elif self.peer: - self.peer_type = 'Peer' - self.address = self.peer - else: - self.peer_type = None - self.address = None - - self.check_params() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = list() - self.end_state = list() - - self.init_data() - - def init_data(self): - """Init data""" - - if self.interface is not None: - self.interface = self.interface.lower() - - if not self.key_id: - self.key_id = "" - - if not self.is_preferred: - self.is_preferred = 'disable' - - def init_module(self): - """Init module""" - - required_one_of = [("server", "peer")] - self.module = AnsibleModule( - argument_spec=self.spec, - supports_check_mode=True, - required_one_of=required_one_of, - mutually_exclusive=self.mutually_exclusive - ) - - def check_ipaddr_validate(self): - """Check ipaddress validate""" - - rule1 = r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.' - rule2 = r'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' - ipv4_regex = '%s%s%s%s%s%s' % ('^', rule1, rule1, rule1, rule2, '$') - ipv6_regex = '^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$' - - flag = False - if bool(re.match(ipv4_regex, self.address)): - flag = True - self.ip_ver = "IPv4" - if not self.ntp_ucast_ipv4_validate(): - flag = False - elif bool(re.match(ipv6_regex, self.address)): - flag = True - self.ip_ver = "IPv6" - else: - flag = True - self.ip_ver = "IPv6" - - if not flag: - if self.peer_type == "Server": - self.module.fail_json(msg='Error: Illegal server ip-address.') - else: - self.module.fail_json(msg='Error: Illegal peer ip-address.') - - def ntp_ucast_ipv4_validate(self): - """Check ntp ucast ipv4 address""" - - addr_list = re.findall(r'(.*)\.(.*)\.(.*)\.(.*)', self.address) - if not addr_list: - self.module.fail_json(msg='Error: Match ip-address fail.') - - value = ((int(addr_list[0][0])) * 0x1000000) + (int(addr_list[0][1]) * 0x10000) + \ - (int(addr_list[0][2]) * 0x100) + (int(addr_list[0][3])) - if (value & (0xff000000) == 0x7f000000) or (value & (0xF0000000) == 0xF0000000) \ - or (value & (0xF0000000) == 0xE0000000) or (value == 0): - return False - return True - - def check_params(self): - """Check all input params""" - - # check interface type - if self.interface: - intf_type = get_interface_type(self.interface) - if not intf_type: - self.module.fail_json( - msg='Error: Interface name of %s ' - 'is error.' % self.interface) - - if self.vpn_name: - if (len(self.vpn_name) < 1) or (len(self.vpn_name) > 31): - self.module.fail_json( - msg='Error: VPN name length is between 1 and 31.') - - if self.address: - self.check_ipaddr_validate() - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def set_ntp(self, *args): - """Configure ntp parameters""" - - if self.state == 'present': - if self.ip_ver == 'IPv4': - xml_str = CE_NC_MERGE_NTP_CONFIG % ( - args[0], args[1], '::', args[2], args[3], args[4], args[5], args[6]) - elif self.ip_ver == 'IPv6': - xml_str = CE_NC_MERGE_NTP_CONFIG % ( - args[0], '0.0.0.0', args[1], args[2], args[3], args[4], args[5], args[6]) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "NTP_CORE_CONFIG") - else: - if self.ip_ver == 'IPv4': - xml_str = CE_NC_DELETE_NTP_CONFIG % ( - args[0], args[1], '::', args[2], args[3]) - elif self.ip_ver == 'IPv6': - xml_str = CE_NC_DELETE_NTP_CONFIG % ( - args[0], '0.0.0.0', args[1], args[2], args[3]) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "UNDO_NTP_CORE_CONFIG") - - def config_ntp(self): - """Config ntp""" - - if self.state == "present": - if self.address and not self.conf_exsit: - if self.is_preferred == 'enable': - is_preferred = 'true' - else: - is_preferred = 'false' - self.set_ntp(self.ip_ver, self.address, self.peer_type, - self.vpn_name, self.key_id, is_preferred, self.interface) - self.changed = True - else: - if self.address: - self.set_ntp(self.ip_ver, self.address, - self.peer_type, self.vpn_name, '', '', '') - self.changed = True - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def get_ntp_exist_config(self): - """Get ntp existed configure""" - - ntp_config = list() - conf_str = CE_NC_GET_NTP_CONFIG - con_obj = get_nc_config(self.module, conf_str) - if "" in con_obj: - return ntp_config - - xml_str = con_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get all ntp config info - root = ElementTree.fromstring(xml_str) - ntpsite = root.findall("ntp/ntpUCastCfgs/ntpUCastCfg") - for nexthop in ntpsite: - ntp_dict = dict() - for ele in nexthop: - if ele.tag in ["addrFamily", "vpnName", "ifName", "ipv4Addr", - "ipv6Addr", "type", "isPreferred", "keyId"]: - ntp_dict[ele.tag] = ele.text - - ip_addr = ntp_dict['ipv6Addr'] - if ntp_dict['addrFamily'] == "IPv4": - ip_addr = ntp_dict['ipv4Addr'] - if ntp_dict['ifName'] is None: - ntp_dict['ifName'] = "" - if ntp_dict['isPreferred'] == 'true': - is_preferred = 'enable' - else: - is_preferred = 'disable' - - if self.state == "present": - key_id = ntp_dict['keyId'] or "" - cur_ntp_cfg = dict(vpn_name=ntp_dict['vpnName'], source_int=ntp_dict['ifName'].lower(), address=ip_addr, - peer_type=ntp_dict['type'], prefer=is_preferred, key_id=key_id) - exp_ntp_cfg = dict(vpn_name=self.vpn_name, source_int=self.interface.lower(), address=self.address, - peer_type=self.peer_type, prefer=self.is_preferred, key_id=self.key_id) - if cur_ntp_cfg == exp_ntp_cfg: - self.conf_exsit = True - - vpn_name = ntp_dict['vpnName'] - if ntp_dict['vpnName'] == "_public_": - vpn_name = None - - if_name = ntp_dict['ifName'] - if if_name == "": - if_name = None - if self.peer_type == 'Server': - ntp_config.append(dict(vpn_name=vpn_name, - source_int=if_name, server=ip_addr, - is_preferred=is_preferred, key_id=ntp_dict['keyId'])) - else: - ntp_config.append(dict(vpn_name=vpn_name, - source_int=if_name, peer=ip_addr, - is_preferred=is_preferred, key_id=ntp_dict['keyId'])) - - return ntp_config - - def get_existing(self): - """Get existing info""" - - if self.address: - self.existing = self.get_ntp_exist_config() - - def get_proposed(self): - """Get proposed info""" - - if self.address: - vpn_name = self.vpn_name - if vpn_name == "_public_": - vpn_name = None - - if_name = self.interface - if if_name == "": - if_name = None - - key_id = self.key_id - if key_id == "": - key_id = None - if self.peer_type == 'Server': - self.proposed = dict(state=self.state, vpn_name=vpn_name, - source_int=if_name, server=self.address, - is_preferred=self.is_preferred, key_id=key_id) - else: - self.proposed = dict(state=self.state, vpn_name=vpn_name, - source_int=if_name, peer=self.address, - is_preferred=self.is_preferred, key_id=key_id) - - def get_end_state(self): - """Get end state info""" - - if self.address: - self.end_state = self.get_ntp_exist_config() - - def get_update_cmd(self): - """Get updated commands""" - - if self.conf_exsit: - return - - cli_str = "" - if self.state == "present": - if self.address: - if self.peer_type == 'Server': - if self.ip_ver == "IPv4": - cli_str = "%s %s" % ( - "ntp unicast-server", self.address) - else: - cli_str = "%s %s" % ( - "ntp unicast-server ipv6", self.address) - elif self.peer_type == 'Peer': - if self.ip_ver == "IPv4": - cli_str = "%s %s" % ("ntp unicast-peer", self.address) - else: - cli_str = "%s %s" % ( - "ntp unicast-peer ipv6", self.address) - - if self.key_id: - cli_str = "%s %s %s" % ( - cli_str, "authentication-keyid", self.key_id) - if self.interface: - cli_str = "%s %s %s" % ( - cli_str, "source-interface", self.interface) - if (self.vpn_name) and (self.vpn_name != '_public_'): - cli_str = "%s %s %s" % ( - cli_str, "vpn-instance", self.vpn_name) - if self.is_preferred == "enable": - cli_str = "%s %s" % (cli_str, "preferred") - else: - if self.address: - if self.peer_type == 'Server': - if self.ip_ver == "IPv4": - cli_str = "%s %s" % ( - "undo ntp unicast-server", self.address) - else: - cli_str = "%s %s" % ( - "undo ntp unicast-server ipv6", self.address) - elif self.peer_type == 'Peer': - if self.ip_ver == "IPv4": - cli_str = "%s %s" % ( - "undo ntp unicast-peer", self.address) - else: - cli_str = "%s %s" % ( - "undo ntp unicast-peer ipv6", self.address) - if (self.vpn_name) and (self.vpn_name != '_public_'): - cli_str = "%s %s %s" % ( - cli_str, "vpn-instance", self.vpn_name) - - self.updates_cmd.append(cli_str) - - def work(self): - """Execute task""" - - self.get_existing() - self.get_proposed() - - self.config_ntp() - - self.get_update_cmd() - self.get_end_state() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - server=dict(type='str'), - peer=dict(type='str'), - key_id=dict(type='str'), - is_preferred=dict(type='str', choices=['enable', 'disable']), - vpn_name=dict(type='str', default='_public_'), - source_int=dict(type='str'), - state=dict(choices=['absent', 'present'], default='present'), - ) - argument_spec.update(ce_argument_spec) - ntp_obj = Ntp(argument_spec) - ntp_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_ntp_auth.py b/plugins/modules/network/cloudengine/ce_ntp_auth.py deleted file mode 100644 index c7aa6ecd07..0000000000 --- a/plugins/modules/network/cloudengine/ce_ntp_auth.py +++ /dev/null @@ -1,520 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- - -module: ce_ntp_auth -short_description: Manages NTP authentication configuration on HUAWEI CloudEngine switches. -description: - - Manages NTP authentication configuration on HUAWEI CloudEngine switches. -author: - - Zhijin Zhou (@QijunPan) -notes: - - If C(state=absent), the module will attempt to remove the given key configuration. - If a matching key configuration isn't found on the device, the module will fail. - - If C(state=absent) and C(authentication=on), authentication will be turned on. - - If C(state=absent) and C(authentication=off), authentication will be turned off. - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - key_id: - description: - - Authentication key identifier (numeric). - required: true - auth_pwd: - description: - - Plain text with length of 1 to 255, encrypted text with length of 20 to 392. - auth_mode: - description: - - Specify authentication algorithm. - choices: ['hmac-sha256', 'md5'] - auth_type: - description: - - Whether the given password is in cleartext or - has been encrypted. If in cleartext, the device - will encrypt it before storing it. - default: encrypt - choices: ['text', 'encrypt'] - trusted_key: - description: - - Whether the given key is required to be supplied by a time source - for the device to synchronize to the time source. - default: 'disable' - choices: ['enable', 'disable'] - authentication: - description: - - Configure ntp authentication enable or unconfigure ntp authentication enable. - choices: ['enable', 'disable'] - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: NTP AUTH test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Configure ntp authentication key-id" - ce_ntp_auth: - key_id: 32 - auth_mode: md5 - auth_pwd: 11111111111111111111111 - provider: "{{ cli }}" - - - name: "Configure ntp authentication key-id and trusted authentication keyid" - ce_ntp_auth: - key_id: 32 - auth_mode: md5 - auth_pwd: 11111111111111111111111 - trusted_key: enable - provider: "{{ cli }}" - - - name: "Configure ntp authentication key-id and authentication enable" - ce_ntp_auth: - key_id: 32 - auth_mode: md5 - auth_pwd: 11111111111111111111111 - authentication: enable - provider: "{{ cli }}" - - - name: "Unconfigure ntp authentication key-id and trusted authentication keyid" - ce_ntp_auth: - key_id: 32 - state: absent - provider: "{{ cli }}" - - - name: "Unconfigure ntp authentication key-id and authentication enable" - ce_ntp_auth: - key_id: 32 - authentication: enable - state: absent - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "auth_type": "text", - "authentication": "enable", - "key_id": "32", - "auth_pwd": "1111", - "auth_mode": "md5", - "trusted_key": "enable", - "state": "present" - } -existing: - description: k/v pairs of existing ntp authentication - returned: always - type: dict - sample: { - "authentication": "off", - "authentication-keyid": [ - { - "auth_mode": "md5", - "key_id": "1", - "trusted_key": "disable" - } - ] - } -end_state: - description: k/v pairs of ntp authentication after module execution - returned: always - type: dict - sample: { - "authentication": "off", - "authentication-keyid": [ - { - "auth_mode": "md5", - "key_id": "1", - "trusted_key": "disable" - }, - { - "auth_mode": "md5", - "key_id": "32", - "trusted_key": "enable" - } - ] - } -state: - description: state as sent in from the playbook - returned: always - type: str - sample: "present" -updates: - description: command sent to the device - returned: always - type: list - sample: [ - "ntp authentication-key 32 md5 1111", - "ntp trusted-key 32", - "ntp authentication enable" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import copy -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, load_config -from ansible.module_utils.connection import exec_command - - -class NtpAuth(object): - """Manage ntp authentication""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # ntp_auth configuration info - self.key_id = self.module.params['key_id'] - self.password = self.module.params['auth_pwd'] or None - self.auth_mode = self.module.params['auth_mode'] or None - self.auth_type = self.module.params['auth_type'] - self.trusted_key = self.module.params['trusted_key'] - self.authentication = self.module.params['authentication'] or None - self.state = self.module.params['state'] - self.check_params() - - self.ntp_auth_conf = dict() - self.key_id_exist = False - self.cur_trusted_key = 'disable' - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = list() - self.end_state = list() - - self.get_ntp_auth_exist_config() - - def check_params(self): - """Check all input params""" - - if not self.key_id.isdigit(): - self.module.fail_json( - msg='Error: key_id is not digit.') - - if (int(self.key_id) < 1) or (int(self.key_id) > 4294967295): - self.module.fail_json( - msg='Error: The length of key_id is between 1 and 4294967295.') - if self.state == "present" and not self.password: - self.module.fail_json( - msg='Error: The password cannot be empty.') - if self.state == "present" and self.password: - if (self.auth_type == 'encrypt') and\ - ((len(self.password) < 20) or (len(self.password) > 392)): - self.module.fail_json( - msg='Error: The length of encrypted password is between 20 and 392.') - elif (self.auth_type == 'text') and\ - ((len(self.password) < 1) or (len(self.password) > 255)): - self.module.fail_json( - msg='Error: The length of text password is between 1 and 255.') - - def init_module(self): - """Init module object""" - - required_if = [("state", "present", ("auth_pwd", "auth_mode"))] - self.module = AnsibleModule( - argument_spec=self.spec, - required_if=required_if, - supports_check_mode=True - ) - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def get_ntp_auth_enable(self): - """Get ntp authentication enable state""" - - flags = list() - exp = "| exclude undo | include ntp authentication" - flags.append(exp) - config = self.get_config(flags) - auth_en = re.findall( - r'.*ntp\s*authentication\s*enable.*', config) - if auth_en: - self.ntp_auth_conf['authentication'] = 'enable' - else: - self.ntp_auth_conf['authentication'] = 'disable' - - def get_ntp_all_auth_keyid(self): - """Get all authentication keyid info""" - - ntp_auth_conf = list() - - flags = list() - exp = "| include authentication-keyid %s" % self.key_id - flags.append(exp) - config = self.get_config(flags) - ntp_config_list = config.split('\n') - if not ntp_config_list: - self.ntp_auth_conf["authentication-keyid"] = "None" - return ntp_auth_conf - - self.key_id_exist = True - cur_auth_mode = "" - cur_auth_pwd = "" - for ntp_config in ntp_config_list: - ntp_auth_mode = re.findall(r'.*authentication-mode(\s\S*)\s\S*\s(\S*)', ntp_config) - ntp_auth_trust = re.findall(r'.*trusted.*', ntp_config) - if ntp_auth_trust: - self.cur_trusted_key = 'enable' - if ntp_auth_mode: - cur_auth_mode = ntp_auth_mode[0][0].strip() - cur_auth_pwd = ntp_auth_mode[0][1].strip() - ntp_auth_conf.append(dict(key_id=self.key_id, - auth_mode=cur_auth_mode, - auth_pwd=cur_auth_pwd, - trusted_key=self.cur_trusted_key)) - self.ntp_auth_conf["authentication-keyid"] = ntp_auth_conf - - return ntp_auth_conf - - def get_ntp_auth_exist_config(self): - """Get ntp authentication existed configure""" - - self.get_ntp_auth_enable() - self.get_ntp_all_auth_keyid() - - def config_ntp_auth_keyid(self): - """Config ntp authentication keyid""" - - commands = list() - if self.auth_type == 'encrypt': - config_cli = "ntp authentication-keyid %s authentication-mode %s cipher %s" % ( - self.key_id, self.auth_mode, self.password) - else: - config_cli = "ntp authentication-keyid %s authentication-mode %s %s" % ( - self.key_id, self.auth_mode, self.password) - - commands.append(config_cli) - - if self.trusted_key != self.cur_trusted_key: - if self.trusted_key == 'enable': - config_cli_trust = "ntp trusted authentication-keyid %s" % (self.key_id) - commands.append(config_cli_trust) - else: - config_cli_trust = "undo ntp trusted authentication-keyid %s" % (self.key_id) - commands.append(config_cli_trust) - - self.cli_load_config(commands) - - def config_ntp_auth_enable(self): - """Config ntp authentication enable""" - - commands = list() - if self.ntp_auth_conf['authentication'] != self.authentication: - if self.authentication == 'enable': - config_cli = "ntp authentication enable" - else: - config_cli = "undo ntp authentication enable" - commands.append(config_cli) - - self.cli_load_config(commands) - - def undo_config_ntp_auth_keyid(self): - """Undo ntp authentication key-id""" - - commands = list() - config_cli = "undo ntp authentication-keyid %s" % self.key_id - commands.append(config_cli) - - self.cli_load_config(commands) - - def cli_load_config(self, commands): - """Load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def config_ntp_auth(self): - """Config ntp authentication""" - - if self.state == "present": - self.config_ntp_auth_keyid() - else: - if not self.key_id_exist: - self.module.fail_json( - msg='Error: The Authentication-keyid does not exist.') - self.undo_config_ntp_auth_keyid() - - if self.authentication: - self.config_ntp_auth_enable() - - self.changed = True - - def get_existing(self): - """Get existing info""" - - self.existing = copy.deepcopy(self.ntp_auth_conf) - - def get_proposed(self): - """Get proposed result""" - - auth_type = self.auth_type - trusted_key = self.trusted_key - if self.state == 'absent': - auth_type = None - trusted_key = None - self.proposed = dict(key_id=self.key_id, auth_pwd=self.password, - auth_mode=self.auth_mode, auth_type=auth_type, - trusted_key=trusted_key, authentication=self.authentication, - state=self.state) - - def get_update_cmd(self): - """Get updated commands""" - - cli_str = "" - if self.state == "present": - cli_str = "ntp authentication-keyid %s authentication-mode %s " % ( - self.key_id, self.auth_mode) - if self.auth_type == 'encrypt': - cli_str = "%s cipher %s" % (cli_str, self.password) - else: - cli_str = "%s %s" % (cli_str, self.password) - else: - cli_str = "undo ntp authentication-keyid %s" % self.key_id - - self.updates_cmd.append(cli_str) - - if self.authentication: - cli_str = "" - - if self.ntp_auth_conf['authentication'] != self.authentication: - if self.authentication == 'enable': - cli_str = "ntp authentication enable" - else: - cli_str = "undo ntp authentication enable" - - if cli_str != "": - self.updates_cmd.append(cli_str) - - cli_str = "" - if self.state == "present": - if self.trusted_key != self.cur_trusted_key: - if self.trusted_key == 'enable': - cli_str = "ntp trusted authentication-keyid %s" % self.key_id - else: - cli_str = "undo ntp trusted authentication-keyid %s" % self.key_id - else: - cli_str = "undo ntp trusted authentication-keyid %s" % self.key_id - - if cli_str != "": - self.updates_cmd.append(cli_str) - - def get_end_state(self): - """Get end state info""" - - self.ntp_auth_conf = dict() - self.get_ntp_auth_exist_config() - self.end_state = copy.deepcopy(self.ntp_auth_conf) - if self.end_state == self.existing: - self.changed = False - - def show_result(self): - """Show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def work(self): - """Execute task""" - - self.get_existing() - self.get_proposed() - self.get_update_cmd() - - self.config_ntp_auth() - - self.get_end_state() - self.show_result() - - -def main(): - """Main function entry""" - - argument_spec = dict( - key_id=dict(required=True, type='str'), - auth_pwd=dict(type='str', no_log=True), - auth_mode=dict(choices=['md5', 'hmac-sha256'], type='str'), - auth_type=dict(choices=['text', 'encrypt'], default='encrypt'), - trusted_key=dict(choices=['enable', 'disable'], default='disable'), - authentication=dict(choices=['enable', 'disable']), - state=dict(choices=['absent', 'present'], default='present'), - ) - argument_spec.update(ce_argument_spec) - ntp_auth_obj = NtpAuth(argument_spec) - ntp_auth_obj.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_ospf.py b/plugins/modules/network/cloudengine/ce_ospf.py deleted file mode 100644 index c5af33b219..0000000000 --- a/plugins/modules/network/cloudengine/ce_ospf.py +++ /dev/null @@ -1,972 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_ospf -short_description: Manages configuration of an OSPF instance on HUAWEI CloudEngine switches. -description: - - Manages configuration of an OSPF instance on HUAWEI CloudEngine switches. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - process_id: - description: - - Specifies a process ID. - The value is an integer ranging from 1 to 4294967295. - required: true - area: - description: - - Specifies the area ID. The area with the area-id being 0 is a backbone area. - Valid values are a string, formatted as an IP address - (i.e. "0.0.0.0") or as an integer between 1 and 4294967295. - addr: - description: - - Specifies the address of the network segment where the interface resides. - The value is in dotted decimal notation. - mask: - description: - - IP network wildcard bits in decimal format between 0 and 32. - auth_mode: - description: - - Specifies the authentication type. - choices: ['none', 'hmac-sha256', 'md5', 'hmac-md5', 'simple'] - auth_text_simple: - description: - - Specifies a password for simple authentication. - The value is a string of 1 to 8 characters. - auth_key_id: - description: - - Authentication key id when C(auth_mode) is 'hmac-sha256', 'md5' or 'hmac-md5. - Valid value is an integer is in the range from 1 to 255. - auth_text_md5: - description: - - Specifies a password for MD5, HMAC-MD5, or HMAC-SHA256 authentication. - The value is a string of 1 to 255 case-sensitive characters, spaces not supported. - nexthop_addr: - description: - - IPv4 address for configure next-hop address's weight. - Valid values are a string, formatted as an IP address. - nexthop_weight: - description: - - Indicates the weight of the next hop. - The smaller the value is, the higher the preference of the route is. - It is an integer that ranges from 1 to 254. - max_load_balance: - description: - - The maximum number of paths for forward packets over multiple paths. - Valid value is an integer in the range from 1 to 64. - state: - description: - - Determines whether the config should be present or not - on the device. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: ospf module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Configure ospf - ce_ospf: - process_id: 1 - area: 100 - state: present - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"process_id": "1", "area": "100"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"process_id": "1", "areas": [], "nexthops":[], "max_load_balance": "32"} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"process_id": "1", - "areas": [{"areaId": "0.0.0.100", "areaType": "Normal"}], - "nexthops":[], "max_load_balance": "32"} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["ospf 1", "area 0.0.0.100"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_OSPF = """ - - - - - - %s - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" - -CE_NC_CREATE_PROCESS = """ - - - - - - %s - - - - - -""" - -CE_NC_DELETE_PROCESS = """ - - - - - - %s - - - - - -""" - -CE_NC_XML_BUILD_MERGE_PROCESS = """ - - - - - - %s - %s - - - - - -""" - -CE_NC_XML_BUILD_PROCESS = """ - - - - - - %s - %s - - - - - -""" - -CE_NC_XML_BUILD_MERGE_AREA = """ - - - %s - %s - - -""" - -CE_NC_XML_BUILD_DELETE_AREA = """ - - - %s - %s - - -""" - -CE_NC_XML_BUILD_AREA = """ - - - %s - %s - - -""" - -CE_NC_XML_SET_AUTH_MODE = """ - %s -""" -CE_NC_XML_SET_AUTH_TEXT_SIMPLE = """ - %s -""" - -CE_NC_XML_SET_AUTH_MD5 = """ - %s - %s -""" - - -CE_NC_XML_MERGE_NETWORKS = """ - - - %s - %s - - -""" - -CE_NC_XML_DELETE_NETWORKS = """ - - - %s - %s - - -""" - -CE_NC_XML_SET_LB = """ - %s -""" - - -CE_NC_XML_BUILD_MERGE_TOPO = """ - - - base - %s - - - -""" - -CE_NC_XML_BUILD_TOPO = """ - - - base - %s - - - -""" - -CE_NC_XML_MERGE_NEXTHOP = """ - - - %s - %s - - -""" - -CE_NC_XML_DELETE_NEXTHOP = """ - - - %s - - -""" - - -class OSPF(object): - """ - Manages configuration of an ospf instance. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.process_id = self.module.params['process_id'] - self.area = self.module.params['area'] - self.addr = self.module.params['addr'] - self.mask = self.module.params['mask'] - self.auth_mode = self.module.params['auth_mode'] - self.auth_text_simple = self.module.params['auth_text_simple'] - self.auth_key_id = self.module.params['auth_key_id'] - self.auth_text_md5 = self.module.params['auth_text_md5'] - self.nexthop_addr = self.module.params['nexthop_addr'] - self.nexthop_weight = self.module.params['nexthop_weight'] - self.max_load_balance = self.module.params['max_load_balance'] - self.state = self.module.params['state'] - - # ospf info - self.ospf_info = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """ init module """ - - required_together = [ - ("addr", "mask"), - ("auth_key_id", "auth_text_md5"), - ("nexthop_addr", "nexthop_weight") - ] - self.module = AnsibleModule( - argument_spec=self.spec, required_together=required_together, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_wildcard_mask(self): - """convert mask length to ip address wildcard mask, i.e. 24 to 0.0.0.255""" - - mask_int = ["255"] * 4 - length = int(self.mask) - - if length > 32: - self.module.fail_json(msg='IPv4 ipaddress mask length is invalid') - if length < 8: - mask_int[0] = str(int(~(0xFF << (8 - length % 8)) & 0xFF)) - if length >= 8: - mask_int[0] = '0' - mask_int[1] = str(int(~(0xFF << (16 - (length % 16))) & 0xFF)) - if length >= 16: - mask_int[1] = '0' - mask_int[2] = str(int(~(0xFF << (24 - (length % 24))) & 0xFF)) - if length >= 24: - mask_int[2] = '0' - mask_int[3] = str(int(~(0xFF << (32 - (length % 32))) & 0xFF)) - if length == 32: - mask_int[3] = '0' - - return '.'.join(mask_int) - - def get_area_ip(self): - """convert integer to ip address""" - - if not self.area.isdigit(): - return self.area - - addr_int = ['0'] * 4 - addr_int[0] = str(((int(self.area) & 0xFF000000) >> 24) & 0xFF) - addr_int[1] = str(((int(self.area) & 0x00FF0000) >> 16) & 0xFF) - addr_int[2] = str(((int(self.area) & 0x0000FF00) >> 8) & 0XFF) - addr_int[3] = str(int(self.area) & 0xFF) - - return '.'.join(addr_int) - - def get_ospf_dict(self, process_id): - """ get one ospf attributes dict.""" - - ospf_info = dict() - conf_str = CE_NC_GET_OSPF % process_id - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return ospf_info - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get process base info - root = ElementTree.fromstring(xml_str) - ospfsite = root.find("ospfv2/ospfv2comm/ospfSites/ospfSite") - if ospfsite: - for site in ospfsite: - if site.tag in ["processId", "routerId", "vrfName"]: - ospf_info[site.tag] = site.text - - # get Topology info - topo = root.find( - "ospfv2/ospfv2comm/ospfSites/ospfSite/ProcessTopologys/ProcessTopology") - if topo: - for eles in topo: - if eles.tag in ["maxLoadBalancing"]: - ospf_info[eles.tag] = eles.text - - # get nexthop info - ospf_info["nexthops"] = list() - nexthops = root.findall( - "ospfv2/ospfv2comm/ospfSites/ospfSite/ProcessTopologys/ProcessTopology/nexthopMTs/nexthopMT") - if nexthops: - for nexthop in nexthops: - nh_dict = dict() - for ele in nexthop: - if ele.tag in ["ipAddress", "weight"]: - nh_dict[ele.tag] = ele.text - ospf_info["nexthops"].append(nh_dict) - - # get areas info - ospf_info["areas"] = list() - areas = root.findall( - "ospfv2/ospfv2comm/ospfSites/ospfSite/areas/area") - if areas: - for area in areas: - area_dict = dict() - for ele in area: - if ele.tag in ["areaId", "authTextSimple", "areaType", - "authenticationMode", "keyId", "authTextMd5"]: - area_dict[ele.tag] = ele.text - if ele.tag == "networks": - # get networks info - area_dict["networks"] = list() - for net in ele: - net_dict = dict() - for net_ele in net: - if net_ele.tag in ["ipAddress", "wildcardMask"]: - net_dict[net_ele.tag] = net_ele.text - area_dict["networks"].append(net_dict) - - ospf_info["areas"].append(area_dict) - return ospf_info - - def is_area_exist(self): - """is ospf area exist""" - if not self.ospf_info: - return False - for area in self.ospf_info["areas"]: - if area["areaId"] == self.get_area_ip(): - return True - - return False - - def is_network_exist(self): - """is ospf area network exist""" - if not self.ospf_info: - return False - - for area in self.ospf_info["areas"]: - if area["areaId"] == self.get_area_ip(): - if not area.get("networks"): - return False - for network in area.get("networks"): - if network["ipAddress"] == self.addr and network["wildcardMask"] == self.get_wildcard_mask(): - return True - return False - - def is_nexthop_exist(self): - """is ospf nexthop exist""" - - if not self.ospf_info: - return False - for nexthop in self.ospf_info["nexthops"]: - if nexthop["ipAddress"] == self.nexthop_addr: - return True - - return False - - def is_nexthop_change(self): - """is ospf nexthop change""" - if not self.ospf_info: - return True - - for nexthop in self.ospf_info["nexthops"]: - if nexthop["ipAddress"] == self.nexthop_addr: - if nexthop["weight"] == self.nexthop_weight: - return False - else: - return True - - return True - - def create_process(self): - """Create ospf process""" - - xml_area = "" - self.updates_cmd.append("ospf %s" % self.process_id) - xml_create = CE_NC_CREATE_PROCESS % self.process_id - set_nc_config(self.module, xml_create) - - # nexthop weight - xml_nh = "" - if self.nexthop_addr: - xml_nh = CE_NC_XML_MERGE_NEXTHOP % ( - self.nexthop_addr, self.nexthop_weight) - self.updates_cmd.append("nexthop %s weight %s" % ( - self.nexthop_addr, self.nexthop_weight)) - - # max load balance - xml_lb = "" - if self.max_load_balance: - xml_lb = CE_NC_XML_SET_LB % self.max_load_balance - self.updates_cmd.append( - "maximum load-balancing %s" % self.max_load_balance) - - xml_topo = "" - if xml_lb or xml_nh: - xml_topo = CE_NC_XML_BUILD_TOPO % (xml_nh + xml_lb) - - if self.area: - self.updates_cmd.append("area %s" % self.get_area_ip()) - xml_auth = "" - xml_network = "" - - # networks - if self.addr and self.mask: - xml_network = CE_NC_XML_MERGE_NETWORKS % ( - self.addr, self.get_wildcard_mask()) - self.updates_cmd.append("network %s %s" % ( - self.addr, self.get_wildcard_mask())) - - # authentication mode - if self.auth_mode: - xml_auth += CE_NC_XML_SET_AUTH_MODE % self.auth_mode - if self.auth_mode == "none": - self.updates_cmd.append("undo authentication-mode") - else: - self.updates_cmd.append( - "authentication-mode %s" % self.auth_mode) - if self.auth_mode == "simple" and self.auth_text_simple: - xml_auth += CE_NC_XML_SET_AUTH_TEXT_SIMPLE % self.auth_text_simple - self.updates_cmd.pop() - self.updates_cmd.append( - "authentication-mode %s %s" % (self.auth_mode, self.auth_text_simple)) - if self.auth_mode in ["hmac-sha256", "hmac-sha256", "md5"]: - if self.auth_key_id and self.auth_text_md5: - xml_auth += CE_NC_XML_SET_AUTH_MD5 % ( - self.auth_key_id, self.auth_text_md5) - self.updates_cmd.pop() - self.updates_cmd.append( - "authentication-mode %s %s %s" % (self.auth_mode, self.auth_key_id, self.auth_text_md5)) - if xml_network or xml_auth or not self.is_area_exist(): - xml_area += CE_NC_XML_BUILD_MERGE_AREA % ( - self.get_area_ip(), xml_network + xml_auth) - - xml_str = CE_NC_XML_BUILD_MERGE_PROCESS % ( - self.process_id, xml_topo + xml_area) - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "CREATE_PROCESS") - self.changed = True - - def delete_process(self): - """Delete ospf process""" - - xml_str = CE_NC_DELETE_PROCESS % self.process_id - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "DELETE_PROCESS") - self.updates_cmd.append("undo ospf %s" % self.process_id) - self.changed = True - - def merge_process(self): - """merge ospf process""" - - xml_area = "" - xml_str = "" - self.updates_cmd.append("ospf %s" % self.process_id) - - # nexthop weight - xml_nh = "" - if self.nexthop_addr and self.is_nexthop_change(): - xml_nh = CE_NC_XML_MERGE_NEXTHOP % ( - self.nexthop_addr, self.nexthop_weight) - self.updates_cmd.append("nexthop %s weight %s" % ( - self.nexthop_addr, self.nexthop_weight)) - - # max load balance - xml_lb = "" - if self.max_load_balance and self.ospf_info.get("maxLoadBalancing") != self.max_load_balance: - xml_lb = CE_NC_XML_SET_LB % self.max_load_balance - self.updates_cmd.append( - "maximum load-balancing %s" % self.max_load_balance) - - xml_topo = "" - if xml_lb or xml_nh: - xml_topo = CE_NC_XML_BUILD_MERGE_TOPO % (xml_nh + xml_lb) - - if self.area: - self.updates_cmd.append("area %s" % self.get_area_ip()) - xml_network = "" - xml_auth = "" - if self.addr and self.mask: - if not self.is_network_exist(): - xml_network += CE_NC_XML_MERGE_NETWORKS % ( - self.addr, self.get_wildcard_mask()) - self.updates_cmd.append("network %s %s" % ( - self.addr, self.get_wildcard_mask())) - - # NOTE: for security, authentication config will always be update - if self.auth_mode: - xml_auth += CE_NC_XML_SET_AUTH_MODE % self.auth_mode - if self.auth_mode == "none": - self.updates_cmd.append("undo authentication-mode") - else: - self.updates_cmd.append( - "authentication-mode %s" % self.auth_mode) - if self.auth_mode == "simple" and self.auth_text_simple: - xml_auth += CE_NC_XML_SET_AUTH_TEXT_SIMPLE % self.auth_text_simple - self.updates_cmd.pop() - self.updates_cmd.append( - "authentication-mode %s %s" % (self.auth_mode, self.auth_text_simple)) - if self.auth_mode in ["hmac-sha256", "hmac-sha256", "md5"]: - if self.auth_key_id and self.auth_text_md5: - xml_auth += CE_NC_XML_SET_AUTH_MD5 % ( - self.auth_key_id, self.auth_text_md5) - self.updates_cmd.pop() - self.updates_cmd.append( - "authentication-mode %s %s %s" % (self.auth_mode, self.auth_key_id, self.auth_text_md5)) - if xml_network or xml_auth or not self.is_area_exist(): - xml_area += CE_NC_XML_BUILD_MERGE_AREA % ( - self.get_area_ip(), xml_network + xml_auth) - elif self.is_area_exist(): - self.updates_cmd.pop() # remove command: area - else: - pass - - if xml_area or xml_topo: - xml_str = CE_NC_XML_BUILD_MERGE_PROCESS % ( - self.process_id, xml_topo + xml_area) - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "MERGE_PROCESS") - self.changed = True - - def remove_area_network(self): - """remvoe ospf area network""" - - if not self.is_network_exist(): - return - - xml_network = CE_NC_XML_DELETE_NETWORKS % ( - self.addr, self.get_wildcard_mask()) - xml_area = CE_NC_XML_BUILD_AREA % (self.get_area_ip(), xml_network) - xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id, xml_area) - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "DELETE_AREA_NETWORK") - self.updates_cmd.append("ospf %s" % self.process_id) - self.updates_cmd.append("area %s" % self.get_area_ip()) - self.updates_cmd.append("undo network %s %s" % - (self.addr, self.get_wildcard_mask())) - self.changed = True - - def remove_area(self): - """remove ospf area""" - - if not self.is_area_exist(): - return - - xml_area = CE_NC_XML_BUILD_DELETE_AREA % (self.get_area_ip(), "") - xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id, xml_area) - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "DELETE_AREA") - self.updates_cmd.append("ospf %s" % self.process_id) - self.updates_cmd.append("undo area %s" % self.get_area_ip()) - self.changed = True - - def remove_nexthop(self): - """remove ospf nexthop weight""" - - if not self.is_nexthop_exist(): - return - - xml_nh = CE_NC_XML_DELETE_NEXTHOP % self.nexthop_addr - xml_topo = CE_NC_XML_BUILD_TOPO % xml_nh - xml_str = CE_NC_XML_BUILD_PROCESS % (self.process_id, xml_topo) - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "DELETE_NEXTHOP_WEIGHT") - self.updates_cmd.append("ospf %s" % self.process_id) - self.updates_cmd.append("undo nexthop %s" % self.nexthop_addr) - self.changed = True - - def is_valid_v4addr(self, addr): - """check is ipv4 addr is valid""" - - if addr.find('.') != -1: - addr_list = addr.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - def convert_ip_to_network(self): - """convert ip to subnet address""" - - ip_list = self.addr.split('.') - mask_list = self.get_wildcard_mask().split('.') - - for i in range(len(ip_list)): - ip_list[i] = str((int(ip_list[i]) & (~int(mask_list[i]))) & 0xff) - - self.addr = '.'.join(ip_list) - - def check_params(self): - """Check all input params""" - - # process_id check - if not self.process_id.isdigit(): - self.module.fail_json(msg="Error: process_id is not digit.") - if int(self.process_id) < 1 or int(self.process_id) > 4294967295: - self.module.fail_json( - msg="Error: process_id must be an integer between 1 and 4294967295.") - - if self.area: - # area check - if self.area.isdigit(): - if int(self.area) < 0 or int(self.area) > 4294967295: - self.module.fail_json( - msg="Error: area id (Integer) must be between 0 and 4294967295.") - - else: - if not self.is_valid_v4addr(self.area): - self.module.fail_json(msg="Error: area id is invalid.") - - # area network check - if self.addr: - if not self.is_valid_v4addr(self.addr): - self.module.fail_json( - msg="Error: network addr is invalid.") - if not self.mask.isdigit(): - self.module.fail_json( - msg="Error: network mask is not digit.") - if int(self.mask) < 0 or int(self.mask) > 32: - self.module.fail_json( - msg="Error: network mask is invalid.") - - # area authentication check - if self.state == "present" and self.auth_mode: - if self.auth_mode == "simple": - if self.auth_text_simple and len(self.auth_text_simple) > 8: - self.module.fail_json( - msg="Error: auth_text_simple is not in the range from 1 to 8.") - if self.auth_mode in ["hmac-sha256", "hmac-sha256", "md5"]: - if self.auth_key_id: - if not self.auth_key_id.isdigit(): - self.module.fail_json( - msg="Error: auth_key_id is not digit.") - if int(self.auth_key_id) < 1 or int(self.auth_key_id) > 255: - self.module.fail_json( - msg="Error: auth_key_id is not in the range from 1 to 255.") - if self.auth_text_md5 and len(self.auth_text_md5) > 255: - self.module.fail_json( - msg="Error: auth_text_md5 is not in the range from 1 to 255.") - - # process max load balance check - if self.state == "present" and self.max_load_balance: - if not self.max_load_balance.isdigit(): - self.module.fail_json( - msg="Error: max_load_balance is not digit.") - if int(self.max_load_balance) < 1 or int(self.max_load_balance) > 64: - self.module.fail_json( - msg="Error: max_load_balance is not in the range from 1 to 64.") - - # process nexthop weight check - if self.nexthop_addr: - if not self.is_valid_v4addr(self.nexthop_addr): - self.module.fail_json(msg="Error: nexthop_addr is invalid.") - if not self.nexthop_weight.isdigit(): - self.module.fail_json( - msg="Error: nexthop_weight is not digit.") - if int(self.nexthop_weight) < 1 or int(self.nexthop_weight) > 254: - self.module.fail_json( - msg="Error: nexthop_weight is not in the range from 1 to 254.") - - if self.addr: - self.convert_ip_to_network() - - def get_proposed(self): - """get proposed info""" - - self.proposed["process_id"] = self.process_id - self.proposed["area"] = self.area - if self.area: - self.proposed["addr"] = self.addr - self.proposed["mask"] = self.mask - if self.auth_mode: - self.proposed["auth_mode"] = self.auth_mode - if self.auth_mode == "simple": - self.proposed["auth_text_simple"] = self.auth_text_simple - if self.auth_mode in ["hmac-sha256", "hmac-sha256", "md5"]: - self.proposed["auth_key_id"] = self.auth_key_id - self.proposed["auth_text_md5"] = self.auth_text_md5 - - if self.nexthop_addr: - self.proposed["nexthop_addr"] = self.nexthop_addr - self.proposed["nexthop_weight"] = self.nexthop_weight - self.proposed["max_load_balance"] = self.max_load_balance - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.ospf_info: - return - - self.existing["process_id"] = self.process_id - self.existing["areas"] = self.ospf_info["areas"] - self.existing["nexthops"] = self.ospf_info["nexthops"] - self.existing["max_load_balance"] = self.ospf_info.get( - "maxLoadBalancing") - - def get_end_state(self): - """get end state info""" - - ospf_info = self.get_ospf_dict(self.process_id) - - if not ospf_info: - return - - self.end_state["process_id"] = self.process_id - self.end_state["areas"] = ospf_info["areas"] - self.end_state["nexthops"] = ospf_info["nexthops"] - self.end_state["max_load_balance"] = ospf_info.get("maxLoadBalancing") - - if self.end_state == self.existing: - if not self.auth_text_simple and not self.auth_text_md5: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.ospf_info = self.get_ospf_dict(self.process_id) - self.get_existing() - self.get_proposed() - - # deal present or absent - if self.state == "present": - if not self.ospf_info: - # create ospf process - self.create_process() - else: - # merge ospf - self.merge_process() - else: - if self.ospf_info: - if self.area: - if self.addr: - # remove ospf area network - self.remove_area_network() - else: - # remove ospf area - self.remove_area() - if self.nexthop_addr: - # remove ospf nexthop weight - self.remove_nexthop() - - if not self.area and not self.nexthop_addr: - # remove ospf process - self.delete_process() - else: - self.module.fail_json(msg='Error: ospf process does not exist') - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - process_id=dict(required=True, type='str'), - area=dict(required=False, type='str'), - addr=dict(required=False, type='str'), - mask=dict(required=False, type='str'), - auth_mode=dict(required=False, - choices=['none', 'hmac-sha256', 'md5', 'hmac-md5', 'simple'], type='str'), - auth_text_simple=dict(required=False, type='str', no_log=True), - auth_key_id=dict(required=False, type='str'), - auth_text_md5=dict(required=False, type='str', no_log=True), - nexthop_addr=dict(required=False, type='str'), - nexthop_weight=dict(required=False, type='str'), - max_load_balance=dict(required=False, type='str'), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = OSPF(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_ospf_vrf.py b/plugins/modules/network/cloudengine/ce_ospf_vrf.py deleted file mode 100644 index e1b1b721a8..0000000000 --- a/plugins/modules/network/cloudengine/ce_ospf_vrf.py +++ /dev/null @@ -1,1623 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_ospf_vrf -short_description: Manages configuration of an OSPF VPN instance on HUAWEI CloudEngine switches. -description: - - Manages configuration of an OSPF VPN instance on HUAWEI CloudEngine switches. -author: Yang yang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - ospf: - description: - - The ID of the ospf process. - Valid values are an integer, 1 - 4294967295, the default value is 1. - required: true - route_id: - description: - - Specifies the ospf private route id,. - Valid values are a string, formatted as an IP address - (i.e. "10.1.1.1") the length is 0 - 20. - vrf: - description: - - Specifies the vpn instance which use ospf,length is 1 - 31. - Valid values are a string. - default: _public_ - description: - description: - - Specifies the description information of ospf process. - bandwidth: - description: - - Specifies the reference bandwidth used to assign ospf cost. - Valid values are an integer, in Mbps, 1 - 2147483648, the default value is 100. - lsaalflag: - description: - - Specifies the mode of timer to calculate interval of arrive LSA. - If set the parameter but not specifies value, the default will be used. - If true use general timer. - If false use intelligent timer. - type: bool - default: 'no' - lsaainterval: - description: - - Specifies the interval of arrive LSA when use the general timer. - Valid value is an integer, in millisecond, from 0 to 10000. - lsaamaxinterval: - description: - - Specifies the max interval of arrive LSA when use the intelligent timer. - Valid value is an integer, in millisecond, from 0 to 10000, the default value is 1000. - lsaastartinterval: - description: - - Specifies the start interval of arrive LSA when use the intelligent timer. - Valid value is an integer, in millisecond, from 0 to 10000, the default value is 500. - lsaaholdinterval: - description: - - Specifies the hold interval of arrive LSA when use the intelligent timer. - Valid value is an integer, in millisecond, from 0 to 10000, the default value is 500. - lsaointervalflag: - description: - - Specifies whether cancel the interval of LSA originate or not. - If set the parameter but noe specifies value, the default will be used. - true:cancel the interval of LSA originate, the interval is 0. - false:do not cancel the interval of LSA originate. - type: bool - default: 'no' - lsaointerval: - description: - - Specifies the interval of originate LSA . - Valid value is an integer, in second, from 0 to 10, the default value is 5. - lsaomaxinterval: - description: - - Specifies the max interval of originate LSA . - Valid value is an integer, in millisecond, from 1 to 10000, the default value is 5000. - lsaostartinterval: - description: - - Specifies the start interval of originate LSA . - Valid value is an integer, in millisecond, from 0 to 1000, the default value is 500. - lsaoholdinterval: - description: - - Specifies the hold interval of originate LSA . - Valid value is an integer, in millisecond, from 0 to 5000, the default value is 1000. - spfintervaltype: - description: - - Specifies the mode of timer which used to calculate SPF. - If set the parameter but noe specifies value, the default will be used. - If is intelligent-timer, then use intelligent timer. - If is timer, then use second level timer. - If is millisecond, then use millisecond level timer. - choices: ['intelligent-timer','timer','millisecond'] - default: intelligent-timer - spfinterval: - description: - - Specifies the interval to calculate SPF when use second level timer. - Valid value is an integer, in second, from 1 to 10. - spfintervalmi: - description: - - Specifies the interval to calculate SPF when use millisecond level timer. - Valid value is an integer, in millisecond, from 1 to 10000. - spfmaxinterval: - description: - - Specifies the max interval to calculate SPF when use intelligent timer. - Valid value is an integer, in millisecond, from 1 to 20000, the default value is 5000. - spfstartinterval: - description: - - Specifies the start interval to calculate SPF when use intelligent timer. - Valid value is an integer, in millisecond, from 1 to 1000, the default value is 50. - spfholdinterval: - description: - - Specifies the hold interval to calculate SPF when use intelligent timer. - Valid value is an integer, in millisecond, from 1 to 5000, the default value is 200. - state: - description: - - Specify desired state of the resource. - choices: ['present', 'absent'] - default: present -''' - -EXAMPLES = ''' -- name: ospf vrf module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Configure ospf route id - ce_ospf_vrf: - ospf: 2 - route_id: 2.2.2.2 - lsaointervalflag: False - lsaointerval: 2 - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: { - "bandwidth": "100", - "description": null, - "lsaaholdinterval": "500", - "lsaainterval": null, - "lsaamaxinterval": "1000", - "lsaastartinterval": "500", - "lsaalflag": "False", - "lsaoholdinterval": "1000", - "lsaointerval": "2", - "lsaointervalflag": "False", - "lsaomaxinterval": "5000", - "lsaostartinterval": "500", - "process_id": "2", - "route_id": "2.2.2.2", - "spfholdinterval": "1000", - "spfinterval": null, - "spfintervalmi": null, - "spfintervaltype": "intelligent-timer", - "spfmaxinterval": "10000", - "spfstartinterval": "500", - "vrf": "_public_" - } -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: { - "bandwidthReference": "100", - "description": null, - "lsaArrivalFlag": "false", - "lsaArrivalHoldInterval": "500", - "lsaArrivalInterval": null, - "lsaArrivalMaxInterval": "1000", - "lsaArrivalStartInterval": "500", - "lsaOriginateHoldInterval": "1000", - "lsaOriginateInterval": "2", - "lsaOriginateIntervalFlag": "false", - "lsaOriginateMaxInterval": "5000", - "lsaOriginateStartInterval": "500", - "processId": "2", - "routerId": "2.2.2.2", - "spfScheduleHoldInterval": "1000", - "spfScheduleInterval": null, - "spfScheduleIntervalMillisecond": null, - "spfScheduleIntervalType": "intelligent-timer", - "spfScheduleMaxInterval": "10000", - "spfScheduleStartInterval": "500", - "vrfName": "_public_" - } -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: { - "bandwidthReference": "100", - "description": null, - "lsaArrivalFlag": "false", - "lsaArrivalHoldInterval": "500", - "lsaArrivalInterval": null, - "lsaArrivalMaxInterval": "1000", - "lsaArrivalStartInterval": "500", - "lsaOriginateHoldInterval": "1000", - "lsaOriginateInterval": "2", - "lsaOriginateIntervalFlag": "false", - "lsaOriginateMaxInterval": "5000", - "lsaOriginateStartInterval": "500", - "processId": "2", - "routerId": "2.2.2.2", - "spfScheduleHoldInterval": "1000", - "spfScheduleInterval": null, - "spfScheduleIntervalMillisecond": null, - "spfScheduleIntervalType": "intelligent-timer", - "spfScheduleMaxInterval": "10000", - "spfScheduleStartInterval": "500", - "vrfName": "_public_" - } -updates: - description: commands sent to the device - returned: always - type: list - sample: ["ospf 2"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: False -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_OSPF_VRF = """ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" - -CE_NC_CREATE_OSPF_VRF = """ - - - - - %s -%s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - %s - - - - -""" -CE_NC_CREATE_ROUTE_ID = """ - %s -""" - -CE_NC_DELETE_OSPF = """ - - - - - %s - %s - %s - - - - -""" - - -def build_config_xml(xmlstr): - """build_config_xml""" - - return ' ' + xmlstr + ' ' - - -class OspfVrf(object): - """ - Manages configuration of an ospf instance. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.ospf = self.module.params['ospf'] - self.route_id = self.module.params['route_id'] - self.vrf = self.module.params['vrf'] - self.description = self.module.params['description'] - self.bandwidth = self.module.params['bandwidth'] - self.lsaalflag = self.module.params['lsaalflag'] - self.lsaainterval = self.module.params['lsaainterval'] - self.lsaamaxinterval = self.module.params['lsaamaxinterval'] - self.lsaastartinterval = self.module.params['lsaastartinterval'] - self.lsaaholdinterval = self.module.params['lsaaholdinterval'] - self.lsaointervalflag = self.module.params['lsaointervalflag'] - self.lsaointerval = self.module.params['lsaointerval'] - self.lsaomaxinterval = self.module.params['lsaomaxinterval'] - self.lsaostartinterval = self.module.params['lsaostartinterval'] - self.lsaoholdinterval = self.module.params['lsaoholdinterval'] - self.spfintervaltype = self.module.params['spfintervaltype'] - self.spfinterval = self.module.params['spfinterval'] - self.spfintervalmi = self.module.params['spfintervalmi'] - self.spfmaxinterval = self.module.params['spfmaxinterval'] - self.spfstartinterval = self.module.params['spfstartinterval'] - self.spfholdinterval = self.module.params['spfholdinterval'] - self.state = self.module.params['state'] - - # ospf info - self.ospf_info = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - self.lsa_arrival_changed = False - self.lsa_originate_changed = False - self.spf_changed = False - self.route_id_changed = False - self.bandwidth_changed = False - self.description_changed = False - self.vrf_changed = False - - def init_module(self): - """" init module """ - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def is_valid_ospf_process_id(self): - """check whether the input ospf process id is valid""" - - if not self.ospf.isdigit(): - return False - if int(self.ospf) > 4294967295 or int(self.ospf) < 1: - return False - return True - - def is_valid_ospf_route_id(self): - """check is ipv4 addr is valid""" - - if self.route_id.find('.') != -1: - addr_list = self.route_id.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - return False - - def is_valid_vrf_name(self): - """check whether the input ospf vrf name is valid""" - - if len(self.vrf) > 31 or len(self.vrf) < 1: - return False - if self.vrf.find('?') != -1: - return False - if self.vrf.find(' ') != -1: - return False - return True - - def is_valid_description(self): - """check whether the input ospf description is valid""" - - if len(self.description) > 80 or len(self.description) < 1: - return False - if self.description.find('?') != -1: - return False - return True - - def is_valid_bandwidth(self): - """check whether the input ospf bandwidth reference is valid""" - - if not self.bandwidth.isdigit(): - return False - if int(self.bandwidth) > 2147483648 or int(self.bandwidth) < 1: - return False - return True - - def is_valid_lsa_arrival_interval(self): - """check whether the input ospf lsa arrival interval is valid""" - - if self.lsaainterval is None: - return False - if not self.lsaainterval.isdigit(): - return False - if int(self.lsaainterval) > 10000 or int(self.lsaainterval) < 0: - return False - return True - - def isvalidlsamaxarrivalinterval(self): - """check whether the input ospf lsa max arrival interval is valid""" - - if not self.lsaamaxinterval.isdigit(): - return False - if int(self.lsaamaxinterval) > 10000 or int(self.lsaamaxinterval) < 1: - return False - return True - - def isvalidlsastartarrivalinterval(self): - """check whether the input ospf lsa start arrival interval is valid""" - - if not self.lsaastartinterval.isdigit(): - return False - if int(self.lsaastartinterval) > 1000 or int(self.lsaastartinterval) < 0: - return False - return True - - def isvalidlsaholdarrivalinterval(self): - """check whether the input ospf lsa hold arrival interval is valid""" - - if not self.lsaaholdinterval.isdigit(): - return False - if int(self.lsaaholdinterval) > 5000 or int(self.lsaaholdinterval) < 0: - return False - return True - - def is_valid_lsa_originate_interval(self): - """check whether the input ospf lsa originate interval is valid""" - - if not self.lsaointerval.isdigit(): - return False - if int(self.lsaointerval) > 10 or int(self.lsaointerval) < 0: - return False - return True - - def isvalidlsaoriginatemaxinterval(self): - """check whether the input ospf lsa originate max interval is valid""" - - if not self.lsaomaxinterval.isdigit(): - return False - if int(self.lsaomaxinterval) > 10000 or int(self.lsaomaxinterval) < 1: - return False - return True - - def isvalidlsaostartinterval(self): - """check whether the input ospf lsa originate start interval is valid""" - - if not self.lsaostartinterval.isdigit(): - return False - if int(self.lsaostartinterval) > 1000 or int(self.lsaostartinterval) < 0: - return False - return True - - def isvalidlsaoholdinterval(self): - """check whether the input ospf lsa originate hold interval is valid""" - - if not self.lsaoholdinterval.isdigit(): - return False - if int(self.lsaoholdinterval) > 5000 or int(self.lsaoholdinterval) < 1: - return False - return True - - def is_valid_spf_interval(self): - """check whether the input ospf spf interval is valid""" - - if not self.spfinterval.isdigit(): - return False - if int(self.spfinterval) > 10 or int(self.spfinterval) < 1: - return False - return True - - def is_valid_spf_milli_interval(self): - """check whether the input ospf spf millisecond level interval is valid""" - - if not self.spfintervalmi.isdigit(): - return False - if int(self.spfintervalmi) > 10000 or int(self.spfintervalmi) < 1: - return False - return True - - def is_valid_spf_max_interval(self): - """check whether the input ospf spf intelligent timer max interval is valid""" - - if not self.spfmaxinterval.isdigit(): - return False - if int(self.spfmaxinterval) > 20000 or int(self.spfmaxinterval) < 1: - return False - return True - - def is_valid_spf_start_interval(self): - """check whether the input ospf spf intelligent timer start interval is valid""" - - if not self.spfstartinterval.isdigit(): - return False - if int(self.spfstartinterval) > 1000 or int(self.spfstartinterval) < 1: - return False - return True - - def is_valid_spf_hold_interval(self): - """check whether the input ospf spf intelligent timer hold interval is valid""" - - if not self.spfholdinterval.isdigit(): - return False - if int(self.spfholdinterval) > 5000 or int(self.spfholdinterval) < 1: - return False - return True - - def is_route_id_exist(self): - """is route id exist""" - - if not self.ospf_info: - return False - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] != self.ospf: - continue - if ospf_site["routerId"] == self.route_id: - return True - else: - continue - return False - - def get_exist_ospf_id(self): - """get exist ospf process id""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["processId"] - else: - continue - return None - - def get_exist_route(self): - """get exist route id""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["routerId"] - else: - continue - return None - - def get_exist_vrf(self): - """get exist vrf""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["vrfName"] - else: - continue - return None - - def get_exist_bandwidth(self): - """get exist bandwidth""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["bandwidthReference"] - else: - continue - return None - - def get_exist_lsa_a_interval(self): - """get exist lsa arrival interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaArrivalInterval"] - else: - continue - return None - - def get_exist_lsa_a_interval_flag(self): - """get exist lsa arrival interval flag""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaArrivalFlag"] - else: - continue - return None - - def get_exist_lsa_a_max_interval(self): - """get exist lsa arrival max interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaArrivalMaxInterval"] - else: - continue - return None - - def get_exist_lsa_a_start_interval(self): - """get exist lsa arrival start interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaArrivalStartInterval"] - else: - continue - return None - - def get_exist_lsa_a_hold_interval(self): - """get exist lsa arrival hold interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaArrivalHoldInterval"] - else: - continue - return None - - def getexistlsaointerval(self): - """get exist lsa originate interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaOriginateInterval"] - else: - continue - return None - - def getexistlsaointerval_flag(self): - """get exist lsa originate interval flag""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaOriginateIntervalFlag"] - else: - continue - return None - - def getexistlsaomaxinterval(self): - """get exist lsa originate max interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaOriginateMaxInterval"] - else: - continue - return None - - def getexistlsaostartinterval(self): - """get exist lsa originate start interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaOriginateStartInterval"] - else: - continue - return None - - def getexistlsaoholdinterval(self): - """get exist lsa originate hold interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["lsaOriginateHoldInterval"] - else: - continue - return None - - def get_exist_spf_interval(self): - """get exist spf second level timer interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["spfScheduleInterval"] - else: - continue - return None - - def get_exist_spf_milli_interval(self): - """get exist spf millisecond level timer interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["spfScheduleIntervalMillisecond"] - else: - continue - return None - - def get_exist_spf_max_interval(self): - """get exist spf max interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["spfScheduleMaxInterval"] - else: - continue - return None - - def get_exist_spf_start_interval(self): - """get exist spf start interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["spfScheduleStartInterval"] - else: - continue - return None - - def get_exist_spf_hold_interval(self): - """get exist spf hold interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["spfScheduleHoldInterval"] - else: - continue - return None - - def get_exist_spf_interval_type(self): - """get exist spf hold interval""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["spfScheduleIntervalType"] - else: - continue - return None - - def is_ospf_exist(self): - """is ospf exist""" - - if not self.ospf_info: - return False - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return True - else: - continue - return False - - def get_exist_description(self): - """is description exist""" - - if not self.ospf_info: - return None - - for ospf_site in self.ospf_info["ospfsite"]: - if ospf_site["processId"] == self.ospf: - return ospf_site["description"] - else: - continue - return None - - def check_params(self): - """Check all input params""" - - if self.ospf == '': - self.module.fail_json( - msg='Error: The ospf process id should not be null.') - if self.ospf: - if not self.is_valid_ospf_process_id(): - self.module.fail_json( - msg='Error: The ospf process id should between 1 - 4294967295.') - if self.route_id == '': - self.module.fail_json( - msg='Error: The ospf route id length should not be null.') - if self.route_id: - if not self.is_valid_ospf_route_id(): - self.module.fail_json( - msg='Error: The ospf route id length should between 0 - 20,i.e.10.1.1.1.') - if self.vrf == '': - self.module.fail_json( - msg='Error: The ospf vpn instance length should not be null.') - if self.vrf: - if not self.is_valid_vrf_name(): - self.module.fail_json( - msg='Error: The ospf vpn instance length should between 0 - 31,but can not contain " " or "?".') - if self.description == '': - self.module.fail_json( - msg='Error: The ospf description should not be null.') - if self.description: - if not self.is_valid_description(): - self.module.fail_json( - msg='Error: The ospf description length should between 1 - 80,but can not contain "?".') - if self.bandwidth == '': - self.module.fail_json( - msg='Error: The ospf bandwidth reference should not be null.') - if self.bandwidth: - if not self.is_valid_bandwidth(): - self.module.fail_json( - msg='Error: The ospf bandwidth reference should between 1 - 2147483648.') - if self.lsaalflag is True: - if not self.is_valid_lsa_arrival_interval(): - self.module.fail_json( - msg='Error: The ospf lsa arrival interval should between 0 - 10000.') - if self.lsaamaxinterval or self.lsaastartinterval or self.lsaaholdinterval: - self.module.fail_json( - msg='Error: Non-Intelligent Timer and Intelligent Timer Interval of ' - 'lsa-arrival-interval can not configured at the same time.') - if self.lsaalflag is False: - if self.lsaainterval: - self.module.fail_json( - msg='Error: The parameter of lsa arrival interval command is invalid, ' - 'because LSA arrival interval can not be config when the LSA arrival flag is not set.') - if self.lsaamaxinterval == '' or self.lsaastartinterval == '' or self.lsaaholdinterval == '': - self.module.fail_json( - msg='Error: The ospf lsa arrival intervals should not be null.') - if self.lsaamaxinterval: - if not self.isvalidlsamaxarrivalinterval(): - self.module.fail_json( - msg='Error: The ospf lsa arrival max interval should between 1 - 10000.') - if self.lsaastartinterval: - if not self.isvalidlsastartarrivalinterval(): - self.module.fail_json( - msg='Error: The ospf lsa arrival start interval should between 1 - 1000.') - if self.lsaaholdinterval: - if not self.isvalidlsaholdarrivalinterval(): - self.module.fail_json( - msg='Error: The ospf lsa arrival hold interval should between 1 - 5000.') - if self.lsaointervalflag is True: - if self.lsaointerval or self.lsaomaxinterval \ - or self.lsaostartinterval or self.lsaoholdinterval: - self.module.fail_json( - msg='Error: Interval for other-type and Instantly Flag ' - 'of lsa-originate-interval can not configured at the same time.') - if self.lsaointerval == '': - self.module.fail_json( - msg='Error: The ospf lsa originate interval should should not be null.') - if self.lsaointerval: - if not self.is_valid_lsa_originate_interval(): - self.module.fail_json( - msg='Error: The ospf lsa originate interval should between 0 - 10 s.') - if self.lsaomaxinterval == '' or self.lsaostartinterval == '' or self.lsaoholdinterval == '': - self.module.fail_json( - msg='Error: The ospf lsa originate intelligent intervals should should not be null.') - if self.lsaomaxinterval: - if not self.isvalidlsaoriginatemaxinterval(): - self.module.fail_json( - msg='Error: The ospf lsa originate max interval should between 1 - 10000 ms.') - if self.lsaostartinterval: - if not self.isvalidlsaostartinterval(): - self.module.fail_json( - msg='Error: The ospf lsa originate start interval should between 0 - 1000 ms.') - if self.lsaoholdinterval: - if not self.isvalidlsaoholdinterval(): - self.module.fail_json( - msg='Error: The ospf lsa originate hold interval should between 1 - 5000 ms.') - if self.spfintervaltype == '': - self.module.fail_json( - msg='Error: The ospf spf interval type should should not be null.') - if self.spfintervaltype == 'intelligent-timer': - if self.spfinterval is not None or self.spfintervalmi is not None: - self.module.fail_json( - msg='Error: Interval second and interval millisecond ' - 'of spf-schedule-interval can not configured if use intelligent timer.') - if self.spfmaxinterval == '' or self.spfstartinterval == '' or self.spfholdinterval == '': - self.module.fail_json( - msg='Error: The ospf spf intelligent timer intervals should should not be null.') - if self.spfmaxinterval and not self.is_valid_spf_max_interval(): - self.module.fail_json( - msg='Error: The ospf spf max interval of intelligent timer should between 1 - 20000 ms.') - if self.spfstartinterval and not self.is_valid_spf_start_interval(): - self.module.fail_json( - msg='Error: The ospf spf start interval of intelligent timer should between 1 - 1000 ms.') - if self.spfholdinterval and not self.is_valid_spf_hold_interval(): - self.module.fail_json( - msg='Error: The ospf spf hold interval of intelligent timer should between 1 - 5000 ms.') - if self.spfintervaltype == 'timer': - if self.spfintervalmi is not None: - self.module.fail_json( - msg='Error: Interval second and interval millisecond ' - 'of spf-schedule-interval can not configured at the same time.') - if self.spfmaxinterval or self.spfstartinterval or self.spfholdinterval: - self.module.fail_json( - msg='Error: Interval second and interval intelligent ' - 'of spf-schedule-interval can not configured at the same time.') - if self.spfinterval == '' or self.spfinterval is None: - self.module.fail_json( - msg='Error: The ospf spf timer intervals should should not be null.') - if not self.is_valid_spf_interval(): - self.module.fail_json( - msg='Error: Interval second should between 1 - 10 s.') - if self.spfintervaltype == 'millisecond': - if self.spfinterval is not None: - self.module.fail_json( - msg='Error: Interval millisecond and interval second ' - 'of spf-schedule-interval can not configured at the same time.') - if self.spfmaxinterval or self.spfstartinterval or self.spfholdinterval: - self.module.fail_json( - msg='Error: Interval millisecond and interval intelligent ' - 'of spf-schedule-interval can not configured at the same time.') - if self.spfintervalmi == '' or self.spfintervalmi is None: - self.module.fail_json( - msg='Error: The ospf spf millisecond intervals should should not be null.') - if not self.is_valid_spf_milli_interval(): - self.module.fail_json( - msg='Error: Interval millisecond should between 1 - 10000 ms.') - - def get_ospf_info(self): - """ get the detail information of ospf """ - - self.ospf_info["ospfsite"] = list() - - getxmlstr = CE_NC_GET_OSPF_VRF - xml_str = get_nc_config(self.module, getxmlstr) - if 'data/' in xml_str: - return - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - - # get the vpn address family and RD text - ospf_sites = root.findall( - "ospfv2/ospfv2comm/ospfSites/ospfSite") - if ospf_sites: - for ospf_site in ospf_sites: - ospf_ele_info = dict() - for ospf_site_ele in ospf_site: - if ospf_site_ele.tag in ["processId", "routerId", "vrfName", "bandwidthReference", - "description", "lsaArrivalInterval", "lsaArrivalMaxInterval", - "lsaArrivalStartInterval", "lsaArrivalHoldInterval", "lsaArrivalFlag", - "lsaOriginateInterval", "lsaOriginateMaxInterval", - "lsaOriginateStartInterval", "lsaOriginateHoldInterval", - "lsaOriginateIntervalFlag", "spfScheduleInterval", - "spfScheduleIntervalMillisecond", "spfScheduleMaxInterval", - "spfScheduleStartInterval", "spfScheduleHoldInterval", - "spfScheduleIntervalType"]: - ospf_ele_info[ - ospf_site_ele.tag] = ospf_site_ele.text - if ospf_ele_info["processId"] == self.ospf: - self.ospf_info["ospfsite"].append(ospf_ele_info) - - def get_proposed(self): - """get proposed info""" - - self.proposed["process_id"] = self.ospf - self.proposed["route_id"] = self.route_id - self.proposed["vrf"] = self.vrf - self.proposed["description"] = self.description - self.proposed["bandwidth"] = self.bandwidth - self.proposed["lsaalflag"] = self.lsaalflag - self.proposed["lsaainterval"] = self.lsaainterval - self.proposed["lsaamaxinterval"] = self.lsaamaxinterval - self.proposed["lsaastartinterval"] = self.lsaastartinterval - self.proposed["lsaaholdinterval"] = self.lsaaholdinterval - self.proposed["lsaointervalflag"] = self.lsaointervalflag - self.proposed["lsaointerval"] = self.lsaointerval - self.proposed["lsaomaxinterval"] = self.lsaomaxinterval - self.proposed["lsaostartinterval"] = self.lsaostartinterval - self.proposed["lsaoholdinterval"] = self.lsaoholdinterval - self.proposed["spfintervaltype"] = self.spfintervaltype - self.proposed["spfinterval"] = self.spfinterval - self.proposed["spfintervalmi"] = self.spfintervalmi - self.proposed["spfmaxinterval"] = self.spfmaxinterval - self.proposed["spfstartinterval"] = self.spfstartinterval - self.proposed["spfholdinterval"] = self.spfholdinterval - - def operate_ospf_info(self): - """operate ospf info""" - - config_route_id_xml = '' - vrf = self.get_exist_vrf() - if vrf is None: - vrf = '_public_' - description = self.get_exist_description() - if description is None: - description = '' - bandwidth_reference = self.get_exist_bandwidth() - if bandwidth_reference is None: - bandwidth_reference = '100' - lsa_in_interval = self.get_exist_lsa_a_interval() - if lsa_in_interval is None: - lsa_in_interval = '' - lsa_arrival_max_interval = self.get_exist_lsa_a_max_interval() - if lsa_arrival_max_interval is None: - lsa_arrival_max_interval = '1000' - lsa_arrival_start_interval = self.get_exist_lsa_a_start_interval() - if lsa_arrival_start_interval is None: - lsa_arrival_start_interval = '500' - lsa_arrival_hold_interval = self.get_exist_lsa_a_hold_interval() - if lsa_arrival_hold_interval is None: - lsa_arrival_hold_interval = '500' - lsa_originate_interval = self.getexistlsaointerval() - if lsa_originate_interval is None: - lsa_originate_interval = '5' - lsa_originate_max_interval = self.getexistlsaomaxinterval() - if lsa_originate_max_interval is None: - lsa_originate_max_interval = '5000' - lsa_originate_start_interval = self.getexistlsaostartinterval() - if lsa_originate_start_interval is None: - lsa_originate_start_interval = '500' - lsa_originate_hold_interval = self.getexistlsaoholdinterval() - if lsa_originate_hold_interval is None: - lsa_originate_hold_interval = '1000' - spf_interval = self.get_exist_spf_interval() - if spf_interval is None: - spf_interval = '' - spf_interval_milli = self.get_exist_spf_milli_interval() - if spf_interval_milli is None: - spf_interval_milli = '' - spf_max_interval = self.get_exist_spf_max_interval() - if spf_max_interval is None: - spf_max_interval = '5000' - spf_start_interval = self.get_exist_spf_start_interval() - if spf_start_interval is None: - spf_start_interval = '50' - spf_hold_interval = self.get_exist_spf_hold_interval() - if spf_hold_interval is None: - spf_hold_interval = '200' - - if self.route_id: - if self.state == 'present': - if self.route_id != self.get_exist_route(): - self.route_id_changed = True - config_route_id_xml = CE_NC_CREATE_ROUTE_ID % self.route_id - else: - if self.route_id != self.get_exist_route(): - self.module.fail_json( - msg='Error: The route id %s is not exist.' % self.route_id) - self.route_id_changed = True - configxmlstr = CE_NC_DELETE_OSPF % ( - self.ospf, self.get_exist_route(), self.get_exist_vrf()) - conf_str = build_config_xml(configxmlstr) - - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "OPERATE_VRF_AF") - self.changed = True - return - if self.vrf != '_public_': - if self.state == 'present': - if self.vrf != self.get_exist_vrf(): - self.vrf_changed = True - vrf = self.vrf - else: - if self.vrf != self.get_exist_vrf(): - self.module.fail_json( - msg='Error: The vrf %s is not exist.' % self.vrf) - self.vrf_changed = True - configxmlstr = CE_NC_DELETE_OSPF % ( - self.ospf, self.get_exist_route(), self.get_exist_vrf()) - conf_str = build_config_xml(configxmlstr) - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "OPERATE_VRF_AF") - self.changed = True - return - if self.bandwidth: - if self.state == 'present': - if self.bandwidth != self.get_exist_bandwidth(): - self.bandwidth_changed = True - bandwidth_reference = self.bandwidth - else: - if self.bandwidth != self.get_exist_bandwidth(): - self.module.fail_json( - msg='Error: The bandwidth %s is not exist.' % self.bandwidth) - if self.get_exist_bandwidth() != '100': - self.bandwidth_changed = True - bandwidth_reference = '100' - if self.description: - if self.state == 'present': - if self.description != self.get_exist_description(): - self.description_changed = True - description = self.description - else: - if self.description != self.get_exist_description(): - self.module.fail_json( - msg='Error: The description %s is not exist.' % self.description) - self.description_changed = True - description = '' - - if self.lsaalflag is False: - lsa_in_interval = '' - if self.state == 'present': - if self.lsaamaxinterval: - if self.lsaamaxinterval != self.get_exist_lsa_a_max_interval(): - self.lsa_arrival_changed = True - lsa_arrival_max_interval = self.lsaamaxinterval - if self.lsaastartinterval: - if self.lsaastartinterval != self.get_exist_lsa_a_start_interval(): - self.lsa_arrival_changed = True - lsa_arrival_start_interval = self.lsaastartinterval - if self.lsaaholdinterval: - if self.lsaaholdinterval != self.get_exist_lsa_a_hold_interval(): - self.lsa_arrival_changed = True - lsa_arrival_hold_interval = self.lsaaholdinterval - else: - if self.lsaamaxinterval: - if self.lsaamaxinterval != self.get_exist_lsa_a_max_interval(): - self.module.fail_json( - msg='Error: The lsaamaxinterval %s is not exist.' % self.lsaamaxinterval) - if self.get_exist_lsa_a_max_interval() != '1000': - lsa_arrival_max_interval = '1000' - self.lsa_arrival_changed = True - if self.lsaastartinterval: - if self.lsaastartinterval != self.get_exist_lsa_a_start_interval(): - self.module.fail_json( - msg='Error: The lsaastartinterval %s is not exist.' % self.lsaastartinterval) - if self.get_exist_lsa_a_start_interval() != '500': - lsa_arrival_start_interval = '500' - self.lsa_arrival_changed = True - if self.lsaaholdinterval: - if self.lsaaholdinterval != self.get_exist_lsa_a_hold_interval(): - self.module.fail_json( - msg='Error: The lsaaholdinterval %s is not exist.' % self.lsaaholdinterval) - if self.get_exist_lsa_a_hold_interval() != '500': - lsa_arrival_hold_interval = '500' - self.lsa_arrival_changed = True - else: - if self.state == 'present': - lsaalflag = "false" - if self.lsaalflag is True: - lsaalflag = "true" - if lsaalflag != self.get_exist_lsa_a_interval_flag(): - self.lsa_arrival_changed = True - if self.lsaainterval is None: - self.module.fail_json( - msg='Error: The lsaainterval is not supplied.') - else: - lsa_in_interval = self.lsaainterval - else: - if self.lsaainterval: - if self.lsaainterval != self.get_exist_lsa_a_interval(): - self.lsa_arrival_changed = True - lsa_in_interval = self.lsaainterval - else: - if self.lsaainterval: - if self.lsaainterval != self.get_exist_lsa_a_interval(): - self.module.fail_json( - msg='Error: The lsaainterval %s is not exist.' % self.lsaainterval) - self.lsaalflag = False - lsa_in_interval = '' - self.lsa_arrival_changed = True - - if self.lsaointervalflag is False: - if self.state == 'present': - if self.lsaomaxinterval: - if self.lsaomaxinterval != self.getexistlsaomaxinterval(): - self.lsa_originate_changed = True - lsa_originate_max_interval = self.lsaomaxinterval - if self.lsaostartinterval: - if self.lsaostartinterval != self.getexistlsaostartinterval(): - self.lsa_originate_changed = True - lsa_originate_start_interval = self.lsaostartinterval - if self.lsaoholdinterval: - if self.lsaoholdinterval != self.getexistlsaoholdinterval(): - self.lsa_originate_changed = True - lsa_originate_hold_interval = self.lsaoholdinterval - if self.lsaointerval: - if self.lsaointerval != self.getexistlsaointerval(): - self.lsa_originate_changed = True - lsa_originate_interval = self.lsaointerval - else: - if self.lsaomaxinterval: - if self.lsaomaxinterval != self.getexistlsaomaxinterval(): - self.module.fail_json( - msg='Error: The lsaomaxinterval %s is not exist.' % self.lsaomaxinterval) - if self.getexistlsaomaxinterval() != '5000': - lsa_originate_max_interval = '5000' - self.lsa_originate_changed = True - if self.lsaostartinterval: - if self.lsaostartinterval != self.getexistlsaostartinterval(): - self.module.fail_json( - msg='Error: The lsaostartinterval %s is not exist.' % self.lsaostartinterval) - if self.getexistlsaostartinterval() != '500': - lsa_originate_start_interval = '500' - self.lsa_originate_changed = True - if self.lsaoholdinterval: - if self.lsaoholdinterval != self.getexistlsaoholdinterval(): - self.module.fail_json( - msg='Error: The lsaoholdinterval %s is not exist.' % self.lsaoholdinterval) - if self.getexistlsaoholdinterval() != '1000': - lsa_originate_hold_interval = '1000' - self.lsa_originate_changed = True - if self.lsaointerval: - if self.lsaointerval != self.getexistlsaointerval(): - self.module.fail_json( - msg='Error: The lsaointerval %s is not exist.' % self.lsaointerval) - if self.getexistlsaointerval() != '5': - lsa_originate_interval = '5' - self.lsa_originate_changed = True - else: - if self.state == 'present': - if self.getexistlsaointerval_flag() != 'true': - self.lsa_originate_changed = True - lsa_originate_interval = '5' - lsa_originate_max_interval = '5000' - lsa_originate_start_interval = '500' - lsa_originate_hold_interval = '1000' - else: - if self.getexistlsaointerval_flag() == 'true': - self.lsaointervalflag = False - self.lsa_originate_changed = True - if self.spfintervaltype != self.get_exist_spf_interval_type(): - self.spf_changed = True - if self.spfintervaltype == 'timer': - if self.spfinterval: - if self.state == 'present': - if self.spfinterval != self.get_exist_spf_interval(): - self.spf_changed = True - spf_interval = self.spfinterval - spf_interval_milli = '' - else: - if self.spfinterval != self.get_exist_spf_interval(): - self.module.fail_json( - msg='Error: The spfinterval %s is not exist.' % self.spfinterval) - self.spfintervaltype = 'intelligent-timer' - spf_interval = '' - self.spf_changed = True - if self.spfintervaltype == 'millisecond': - if self.spfintervalmi: - if self.state == 'present': - if self.spfintervalmi != self.get_exist_spf_milli_interval(): - self.spf_changed = True - spf_interval_milli = self.spfintervalmi - spf_interval = '' - else: - if self.spfintervalmi != self.get_exist_spf_milli_interval(): - self.module.fail_json( - msg='Error: The spfintervalmi %s is not exist.' % self.spfintervalmi) - self.spfintervaltype = 'intelligent-timer' - spf_interval_milli = '' - self.spf_changed = True - if self.spfintervaltype == 'intelligent-timer': - spf_interval = '' - spf_interval_milli = '' - if self.spfmaxinterval: - if self.state == 'present': - if self.spfmaxinterval != self.get_exist_spf_max_interval(): - self.spf_changed = True - spf_max_interval = self.spfmaxinterval - else: - if self.spfmaxinterval != self.get_exist_spf_max_interval(): - self.module.fail_json( - msg='Error: The spfmaxinterval %s is not exist.' % self.spfmaxinterval) - if self.get_exist_spf_max_interval() != '5000': - self.spf_changed = True - spf_max_interval = '5000' - if self.spfstartinterval: - if self.state == 'present': - if self.spfstartinterval != self.get_exist_spf_start_interval(): - self.spf_changed = True - spf_start_interval = self.spfstartinterval - else: - if self.spfstartinterval != self.get_exist_spf_start_interval(): - self.module.fail_json( - msg='Error: The spfstartinterval %s is not exist.' % self.spfstartinterval) - if self.get_exist_spf_start_interval() != '50': - self.spf_changed = True - spf_start_interval = '50' - if self.spfholdinterval: - if self.state == 'present': - if self.spfholdinterval != self.get_exist_spf_hold_interval(): - self.spf_changed = True - spf_hold_interval = self.spfholdinterval - else: - if self.spfholdinterval != self.get_exist_spf_hold_interval(): - self.module.fail_json( - msg='Error: The spfholdinterval %s is not exist.' % self.spfholdinterval) - if self.get_exist_spf_hold_interval() != '200': - self.spf_changed = True - spf_hold_interval = '200' - - if not self.description_changed and not self.vrf_changed and not self.lsa_arrival_changed \ - and not self.lsa_originate_changed and not self.spf_changed \ - and not self.route_id_changed and not self.bandwidth_changed: - self.changed = False - return - else: - self.changed = True - lsaointervalflag = "false" - lsaalflag = "false" - if self.lsaointervalflag is True: - lsaointervalflag = "true" - if self.lsaalflag is True: - lsaalflag = "true" - configxmlstr = CE_NC_CREATE_OSPF_VRF % ( - self.ospf, config_route_id_xml, vrf, - description, bandwidth_reference, lsaalflag, - lsa_in_interval, lsa_arrival_max_interval, lsa_arrival_start_interval, - lsa_arrival_hold_interval, lsaointervalflag, lsa_originate_interval, - lsa_originate_max_interval, lsa_originate_start_interval, lsa_originate_hold_interval, - self.spfintervaltype, spf_interval, spf_interval_milli, - spf_max_interval, spf_start_interval, spf_hold_interval) - - conf_str = build_config_xml(configxmlstr) - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "OPERATE_VRF_AF") - - def get_existing(self): - """get existing info""" - - self.get_ospf_info() - self.existing['ospf_info'] = self.ospf_info["ospfsite"] - - def set_update_cmd(self): - """ set update command""" - if not self.changed: - return - - if self.state == 'present': - if self.vrf_changed: - if self.vrf != '_public_': - if self.route_id_changed: - self.updates_cmd.append( - 'ospf %s router-id %s vpn-instance %s' % (self.ospf, self.route_id, self.vrf)) - else: - self.updates_cmd.append( - 'ospf %s vpn-instance %s ' % (self.ospf, self.vrf)) - else: - if self.route_id_changed: - self.updates_cmd.append( - 'ospf %s router-id %s' % (self.ospf, self.route_id)) - else: - if self.route_id_changed: - if self.vrf != '_public_': - self.updates_cmd.append( - 'ospf %s router-id %s vpn-instance %s' % (self.ospf, self.route_id, self.get_exist_vrf())) - else: - self.updates_cmd.append( - 'ospf %s router-id %s' % (self.ospf, self.route_id)) - else: - if self.route_id_changed: - self.updates_cmd.append('undo ospf %s' % self.ospf) - return - - self.updates_cmd.append('ospf %s' % self.ospf) - - if self.description: - if self.state == 'present': - if self.description_changed: - self.updates_cmd.append( - 'description %s' % self.description) - else: - if self.description_changed: - self.updates_cmd.append('undo description') - if self.bandwidth_changed: - if self.state == 'present': - if self.get_exist_bandwidth() != '100': - self.updates_cmd.append( - 'bandwidth-reference %s' % (self.get_exist_bandwidth())) - else: - self.updates_cmd.append('undo bandwidth-reference') - if self.lsaalflag is True: - if self.lsa_arrival_changed: - if self.state == 'present': - self.updates_cmd.append( - 'lsa-arrival-interval %s' % (self.get_exist_lsa_a_interval())) - else: - self.updates_cmd.append( - 'undo lsa-arrival-interval') - - if self.lsaalflag is False: - if self.lsa_arrival_changed: - if self.state == 'present': - if self.get_exist_lsa_a_max_interval() != '1000' \ - or self.get_exist_lsa_a_start_interval() != '500'\ - or self.get_exist_lsa_a_hold_interval() != '500': - self.updates_cmd.append('lsa-arrival-interval intelligent-timer %s %s %s' - % (self.get_exist_lsa_a_max_interval(), - self.get_exist_lsa_a_start_interval(), - self.get_exist_lsa_a_hold_interval())) - else: - if self.get_exist_lsa_a_max_interval() == '1000' \ - and self.get_exist_lsa_a_start_interval() == '500'\ - and self.get_exist_lsa_a_hold_interval() == '500': - self.updates_cmd.append( - 'undo lsa-arrival-interval') - if self.lsaointervalflag is False: - if self.lsa_originate_changed: - if self.state == 'present': - if self.getexistlsaointerval() != '5' \ - or self.getexistlsaomaxinterval() != '5000' \ - or self.getexistlsaostartinterval() != '500' \ - or self.getexistlsaoholdinterval() != '1000': - self.updates_cmd.append('lsa-originate-interval other-type %s intelligent-timer %s %s %s' - % (self.getexistlsaointerval(), - self.getexistlsaomaxinterval(), - self.getexistlsaostartinterval(), - self.getexistlsaoholdinterval())) - else: - self.updates_cmd.append( - 'undo lsa-originate-interval') - if self.lsaointervalflag is True: - if self.lsa_originate_changed: - if self.state == 'present': - self.updates_cmd.append('lsa-originate-interval 0 ') - else: - self.updates_cmd.append( - 'undo lsa-originate-interval') - if self.spfintervaltype == 'millisecond': - if self.spf_changed: - if self.state == 'present': - self.updates_cmd.append( - 'spf-schedule-interval millisecond %s' % self.get_exist_spf_milli_interval()) - else: - self.updates_cmd.append( - 'undo spf-schedule-interval') - if self.spfintervaltype == 'timer': - if self.spf_changed: - if self.state == 'present': - self.updates_cmd.append( - 'spf-schedule-interval %s' % self.get_exist_spf_interval()) - else: - self.updates_cmd.append( - 'undo spf-schedule-interval') - if self.spfintervaltype == 'intelligent-timer': - if self.spf_changed: - if self.state == 'present': - if self.get_exist_spf_max_interval() != '5000' \ - or self.get_exist_spf_start_interval() != '50' \ - or self.get_exist_spf_hold_interval() != '200': - self.updates_cmd.append('spf-schedule-interval intelligent-timer %s %s %s' - % (self.get_exist_spf_max_interval(), - self.get_exist_spf_start_interval(), - self.get_exist_spf_hold_interval())) - else: - self.updates_cmd.append( - 'undo spf-schedule-interval') - - def get_end_state(self): - """get end state info""" - - self.get_ospf_info() - self.end_state['ospf_info'] = self.ospf_info["ospfsite"] - - def work(self): - """worker""" - - self.check_params() - self.get_existing() - self.get_proposed() - self.operate_ospf_info() - self.get_end_state() - self.set_update_cmd() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - ospf=dict(required=True, type='str'), - route_id=dict(required=False, type='str'), - vrf=dict(required=False, type='str', default='_public_'), - description=dict(required=False, type='str'), - bandwidth=dict(required=False, type='str'), - lsaalflag=dict(type='bool', default=False), - lsaainterval=dict(required=False, type='str'), - lsaamaxinterval=dict(required=False, type='str'), - lsaastartinterval=dict(required=False, type='str'), - lsaaholdinterval=dict(required=False, type='str'), - lsaointervalflag=dict(type='bool', default=False), - lsaointerval=dict(required=False, type='str'), - lsaomaxinterval=dict(required=False, type='str'), - lsaostartinterval=dict(required=False, type='str'), - lsaoholdinterval=dict(required=False, type='str'), - spfintervaltype=dict(required=False, default='intelligent-timer', - choices=['intelligent-timer', 'timer', 'millisecond']), - spfinterval=dict(required=False, type='str'), - spfintervalmi=dict(required=False, type='str'), - spfmaxinterval=dict(required=False, type='str'), - spfstartinterval=dict(required=False, type='str'), - spfholdinterval=dict(required=False, type='str'), - state=dict(required=False, choices=['present', 'absent'], default='present'), - ) - - argument_spec.update(ce_argument_spec) - module = OspfVrf(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_reboot.py b/plugins/modules/network/cloudengine/ce_reboot.py deleted file mode 100644 index 289c67a43e..0000000000 --- a/plugins/modules/network/cloudengine/ce_reboot.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_reboot -short_description: Reboot a HUAWEI CloudEngine switches. -description: - - Reboot a HUAWEI CloudEngine switches. -author: Gong Jianjun (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -requirements: ["ncclient"] -options: - confirm: - description: - - Safeguard boolean. Set to true if you're sure you want to reboot. - type: bool - required: true - save_config: - description: - - Flag indicating whether to save the configuration. - required: false - type: bool - default: false -''' - -EXAMPLES = ''' -- name: reboot module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Reboot the device - ce_reboot: - confirm: true - save_config: true - provider: "{{ cli }}" -''' - -RETURN = ''' -rebooted: - description: Whether the device was instructed to reboot. - returned: success - type: bool - sample: true -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import execute_nc_action, ce_argument_spec - -try: - from ncclient.operations.errors import TimeoutExpiredError - HAS_NCCLIENT = True -except ImportError: - HAS_NCCLIENT = False - -CE_NC_XML_EXECUTE_REBOOT = """ - - - - %s - - - -""" - - -class Reboot(object): - """ Reboot a network device """ - - def __init__(self, **kwargs): - """ __init___ """ - - self.network_module = None - self.netconf = None - self.init_network_module(**kwargs) - - self.confirm = self.network_module.params['confirm'] - self.save_config = self.network_module.params['save_config'] - - def init_network_module(self, **kwargs): - """ init network module """ - - self.network_module = AnsibleModule(**kwargs) - - def netconf_set_action(self, xml_str): - """ netconf execute action """ - - try: - execute_nc_action(self.network_module, xml_str) - except TimeoutExpiredError: - pass - - def work(self): - """ start to work """ - - if not self.confirm: - self.network_module.fail_json( - msg='Error: Confirm must be set to true for this module to work.') - - xml_str = CE_NC_XML_EXECUTE_REBOOT % str(self.save_config).lower() - self.netconf_set_action(xml_str) - - -def main(): - """ main """ - - argument_spec = dict( - confirm=dict(required=True, type='bool'), - save_config=dict(default=False, type='bool') - ) - - argument_spec.update(ce_argument_spec) - module = Reboot(argument_spec=argument_spec, supports_check_mode=True) - - if not HAS_NCCLIENT: - module.network_module.fail_json(msg='Error: The ncclient library is required.') - - changed = False - rebooted = False - - module.work() - - changed = True - rebooted = True - - results = dict() - results['changed'] = changed - results['rebooted'] = rebooted - - module.network_module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_rollback.py b/plugins/modules/network/cloudengine/ce_rollback.py deleted file mode 100644 index 428b32ddf2..0000000000 --- a/plugins/modules/network/cloudengine/ce_rollback.py +++ /dev/null @@ -1,453 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_rollback -short_description: Set a checkpoint or rollback to a checkpoint on HUAWEI CloudEngine switches. -description: - - This module offers the ability to set a configuration checkpoint - file or rollback to a configuration checkpoint file on HUAWEI CloudEngine switches. -author: - - Li Yanfeng (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - commit_id: - description: - - Specifies the label of the configuration rollback point to which system configurations are - expected to roll back. - The value is an integer that the system generates automatically. - label: - description: - - Specifies a user label for a configuration rollback point. - The value is a string of 1 to 256 case-sensitive ASCII characters, spaces not supported. - The value must start with a letter and cannot be presented in a single hyphen (-). - filename: - description: - - Specifies a configuration file for configuration rollback. - The value is a string of 5 to 64 case-sensitive characters in the format of *.zip, *.cfg, or *.dat, - spaces not supported. - last: - description: - - Specifies the number of configuration rollback points. - The value is an integer that ranges from 1 to 80. - oldest: - description: - - Specifies the number of configuration rollback points. - The value is an integer that ranges from 1 to 80. - action: - description: - - The operation of configuration rollback. - required: true - choices: ['rollback','clear','set','display','commit'] -''' -EXAMPLES = ''' -- name: rollback module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - -- name: Ensure commit_id is exist, and specifies the label of the configuration rollback point to - which system configurations are expected to roll back. - ce_rollback: - commit_id: 1000000748 - action: rollback - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: sometimes - type: dict - sample: {"commit_id": "1000000748", "action": "rollback"} -existing: - description: k/v pairs of existing rollback - returned: sometimes - type: dict - sample: {"commitId": "1000000748", "userLabel": "abc"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["rollback configuration to file a.cfg", - "set configuration commit 1000000783 label ddd", - "clear configuration commit 1000000783 label", - "display configuration commit list"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -end_state: - description: k/v pairs of configuration after module execution - returned: always - type: dict - sample: {"commitId": "1000000748", "userLabel": "abc"} -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, exec_command, run_commands -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList - - -class RollBack(object): - """ - Manages rolls back the system from the current configuration state to a historical configuration state. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - self.commands = list() - # module input info - self.commit_id = self.module.params['commit_id'] - self.label = self.module.params['label'] - self.filename = self.module.params['filename'] - self.last = self.module.params['last'] - self.oldest = self.module.params['oldest'] - self.action = self.module.params['action'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.existing = dict() - self.proposed = dict() - self.end_state = dict() - - # configuration rollback points info - self.rollback_info = None - self.init_module() - - def init_module(self): - """ init module """ - - required_if = [('action', 'set', ['commit_id', 'label']), ('action', 'commit', ['label'])] - mutually_exclusive = None - required_one_of = None - if self.action == "rollback": - required_one_of = [['commit_id', 'label', 'filename', 'last']] - elif self.action == "clear": - required_one_of = [['commit_id', 'oldest']] - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True, required_if=required_if, mutually_exclusive=mutually_exclusive, required_one_of=required_one_of) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - self.commands.append("return") - self.commands.append("mmi-mode enable") - - if self.action == "commit": - self.commands.append("sys") - - self.commands.append(command) - self.updates_cmd.append(command) - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - run_commands(self.module, commands) - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def get_rollback_dict(self): - """ get rollback attributes dict.""" - - rollback_info = dict() - rollback_info["RollBackInfos"] = list() - - flags = list() - exp = "commit list" - flags.append(exp) - cfg_info = self.get_config(flags) - if not cfg_info: - return rollback_info - - cfg_line = cfg_info.split("\n") - for cfg in cfg_line: - if re.findall(r'^\d', cfg): - pre_rollback_info = cfg.split() - rollback_info["RollBackInfos"].append(dict(commitId=pre_rollback_info[1], userLabel=pre_rollback_info[2])) - - return rollback_info - - def get_filename_type(self, filename): - """Gets the type of filename, such as cfg, zip, dat...""" - - if filename is None: - return None - if ' ' in filename: - self.module.fail_json( - msg='Error: Configuration file name include spaces.') - - iftype = None - - if filename.endswith('.cfg'): - iftype = 'cfg' - elif filename.endswith('.zip'): - iftype = 'zip' - elif filename.endswith('.dat'): - iftype = 'dat' - else: - return None - return iftype.lower() - - def set_config(self): - - if self.action == "rollback": - if self.commit_id: - cmd = "rollback configuration to commit-id %s" % self.commit_id - self.cli_add_command(cmd) - if self.label: - cmd = "rollback configuration to label %s" % self.label - self.cli_add_command(cmd) - if self.filename: - cmd = "rollback configuration to file %s" % self.filename - self.cli_add_command(cmd) - if self.last: - cmd = "rollback configuration last %s" % self.last - self.cli_add_command(cmd) - elif self.action == "set": - if self.commit_id and self.label: - cmd = "set configuration commit %s label %s" % (self.commit_id, self.label) - self.cli_add_command(cmd) - elif self.action == "clear": - if self.commit_id: - cmd = "clear configuration commit %s label" % self.commit_id - self.cli_add_command(cmd) - if self.oldest: - cmd = "clear configuration commit oldest %s" % self.oldest - self.cli_add_command(cmd) - elif self.action == "commit": - if self.label: - cmd = "commit label %s" % self.label - self.cli_add_command(cmd) - - elif self.action == "display": - self.rollback_info = self.get_rollback_dict() - if self.commands: - self.commands.append('return') - self.commands.append('undo mmi-mode enable') - self.cli_load_config(self.commands) - self.changed = True - - def check_params(self): - """Check all input params""" - - # commit_id check - rollback_info = self.rollback_info["RollBackInfos"] - if self.commit_id: - if not self.commit_id.isdigit(): - self.module.fail_json( - msg='Error: The parameter of commit_id is invalid.') - - info_bool = False - for info in rollback_info: - if info.get("commitId") == self.commit_id: - info_bool = True - if not info_bool: - self.module.fail_json( - msg='Error: The parameter of commit_id is not exist.') - - if self.action == "clear": - info_bool = False - for info in rollback_info: - if info.get("commitId") == self.commit_id: - if info.get("userLabel") == "-": - info_bool = True - if info_bool: - self.module.fail_json( - msg='Error: This commit_id does not have a label.') - - # filename check - if self.filename: - if not self.get_filename_type(self.filename): - self.module.fail_json( - msg='Error: Invalid file name or file name extension ( *.cfg, *.zip, *.dat ).') - # last check - if self.last: - if not self.last.isdigit(): - self.module.fail_json( - msg='Error: Number of configuration checkpoints is not digit.') - if int(self.last) <= 0 or int(self.last) > 80: - self.module.fail_json( - msg='Error: Number of configuration checkpoints is not in the range from 1 to 80.') - - # oldest check - if self.oldest: - if not self.oldest.isdigit(): - self.module.fail_json( - msg='Error: Number of configuration checkpoints is not digit.') - if int(self.oldest) <= 0 or int(self.oldest) > 80: - self.module.fail_json( - msg='Error: Number of configuration checkpoints is not in the range from 1 to 80.') - - # label check - if self.label: - if self.label[0].isdigit(): - self.module.fail_json( - msg='Error: Commit label which should not start with a number.') - if len(self.label.replace(' ', '')) == 1: - if self.label == '-': - self.module.fail_json( - msg='Error: Commit label which should not be "-"') - if len(self.label.replace(' ', '')) < 1 or len(self.label) > 256: - self.module.fail_json( - msg='Error: Label of configuration checkpoints is a string of 1 to 256 characters.') - - if self.action == "rollback": - info_bool = False - for info in rollback_info: - if info.get("userLabel") == self.label: - info_bool = True - if not info_bool: - self.module.fail_json( - msg='Error: The parameter of userLabel is not exist.') - - if self.action == "commit": - info_bool = False - for info in rollback_info: - if info.get("userLabel") == self.label: - info_bool = True - if info_bool: - self.module.fail_json( - msg='Error: The parameter of userLabel is existing.') - - if self.action == "set": - info_bool = False - for info in rollback_info: - if info.get("commitId") == self.commit_id: - if info.get("userLabel") != "-": - info_bool = True - if info_bool: - self.module.fail_json( - msg='Error: The userLabel of this commitid is present and can be reset after deletion.') - - def get_proposed(self): - """get proposed info""" - - if self.commit_id: - self.proposed["commit_id"] = self.commit_id - if self.label: - self.proposed["label"] = self.label - if self.filename: - self.proposed["filename"] = self.filename - if self.last: - self.proposed["last"] = self.last - if self.oldest: - self.proposed["oldest"] = self.oldest - - def get_existing(self): - """get existing info""" - if not self.rollback_info: - self.existing["RollBackInfos"] = None - else: - self.existing["RollBackInfos"] = self.rollback_info["RollBackInfos"] - - def get_end_state(self): - """get end state info""" - - rollback_info = self.get_rollback_dict() - if not rollback_info: - self.end_state["RollBackInfos"] = None - else: - self.end_state["RollBackInfos"] = rollback_info["RollBackInfos"] - - def work(self): - """worker""" - - self.rollback_info = self.get_rollback_dict() - self.check_params() - self.get_proposed() - - self.set_config() - - self.get_existing() - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - commit_id=dict(required=False), - label=dict(required=False, type='str'), - filename=dict(required=False, type='str'), - last=dict(required=False, type='str'), - oldest=dict(required=False, type='str'), - action=dict(required=False, type='str', choices=[ - 'rollback', 'clear', 'set', 'commit', 'display']), - ) - argument_spec.update(ce_argument_spec) - module = RollBack(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_sflow.py b/plugins/modules/network/cloudengine/ce_sflow.py deleted file mode 100644 index ddd179e4bd..0000000000 --- a/plugins/modules/network/cloudengine/ce_sflow.py +++ /dev/null @@ -1,1167 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_sflow -short_description: Manages sFlow configuration on HUAWEI CloudEngine switches. -description: - - Configure Sampled Flow (sFlow) to monitor traffic on an interface in real time, - detect abnormal traffic, and locate the source of attack traffic, - ensuring stable running of the network. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - agent_ip: - description: - - Specifies the IPv4/IPv6 address of an sFlow agent. - source_ip: - description: - - Specifies the source IPv4/IPv6 address of sFlow packets. - collector_id: - description: - - Specifies the ID of an sFlow collector. This ID is used when you specify - the collector in subsequent sFlow configuration. - choices: ['1', '2'] - collector_ip: - description: - - Specifies the IPv4/IPv6 address of the sFlow collector. - collector_ip_vpn: - description: - - Specifies the name of a VPN instance. - The value is a string of 1 to 31 case-sensitive characters, spaces not supported. - When double quotation marks are used around the string, spaces are allowed in the string. - The value C(_public_) is reserved and cannot be used as the VPN instance name. - collector_datagram_size: - description: - - Specifies the maximum length of sFlow packets sent from an sFlow agent to an sFlow collector. - The value is an integer, in bytes. It ranges from 1024 to 8100. The default value is 1400. - collector_udp_port: - description: - - Specifies the UDP destination port number of sFlow packets. - The value is an integer that ranges from 1 to 65535. The default value is 6343. - collector_meth: - description: - - Configures the device to send sFlow packets through service interfaces, - enhancing the sFlow packet forwarding capability. - The enhanced parameter is optional. No matter whether you configure the enhanced mode, - the switch determines to send sFlow packets through service cards or management port - based on the routing information on the collector. - When the value is meth, the device forwards sFlow packets at the control plane. - When the value is enhanced, the device forwards sFlow packets at the forwarding plane to - enhance the sFlow packet forwarding capacity. - choices: ['meth', 'enhanced'] - collector_description: - description: - - Specifies the description of an sFlow collector. - The value is a string of 1 to 255 case-sensitive characters without spaces. - sflow_interface: - description: - - Full name of interface for Flow Sampling or Counter. - It must be a physical interface, Eth-Trunk, or Layer 2 subinterface. - sample_collector: - description: - - Indicates the ID list of the collector. - sample_rate: - description: - - Specifies the flow sampling rate in the format 1/rate. - The value is an integer and ranges from 1 to 4294967295. The default value is 8192. - sample_length: - description: - - Specifies the maximum length of sampled packets. - The value is an integer and ranges from 18 to 512, in bytes. The default value is 128. - sample_direction: - description: - - Enables flow sampling in the inbound or outbound direction. - choices: ['inbound', 'outbound', 'both'] - counter_collector: - description: - - Indicates the ID list of the counter collector. - counter_interval: - description: - - Indicates the counter sampling interval. - The value is an integer that ranges from 10 to 4294967295, in seconds. The default value is 20. - export_route: - description: - - Configures the sFlow packets sent by the switch not to carry routing information. - choices: ['enable', 'disable'] - rate_limit: - description: - - Specifies the rate of sFlow packets sent from a card to the control plane. - The value is an integer that ranges from 100 to 1500, in pps. - type: str - rate_limit_slot: - description: - - Specifies the slot where the rate of output sFlow packets is limited. - If this parameter is not specified, the rate of sFlow packets sent from - all cards to the control plane is limited. - The value is an integer or a string of characters. - type: str - forward_enp_slot: - description: - - Enable the Embedded Network Processor (ENP) chip function. - The switch uses the ENP chip to perform sFlow sampling, - and the maximum sFlow sampling interval is 65535. - If you set the sampling interval to be larger than 65535, - the switch automatically restores it to 65535. - The value is an integer or 'all'. - type: str - state: - description: - - Determines whether the config should be present or not - on the device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' ---- - -- name: sflow module test - hosts: ce128 - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Configuring sFlow Agent - ce_sflow: - agent_ip: 6.6.6.6 - provider: '{{ cli }}' - - - name: Configuring sFlow Collector - ce_sflow: - collector_id: 1 - collector_ip: 7.7.7.7 - collector_ip_vpn: vpn1 - collector_description: Collector1 - provider: '{{ cli }}' - - - name: Configure flow sampling. - ce_sflow: - sflow_interface: 10GE2/0/2 - sample_collector: 1 - sample_direction: inbound - provider: '{{ cli }}' - - - name: Configure counter sampling. - ce_sflow: - sflow_interface: 10GE2/0/2 - counter_collector: 1 - counter_interval: 1000 - provider: '{{ cli }}' -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"agent_ip": "6.6.6.6", "state": "present"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"agent": {}} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"agent": {"family": "ipv4", "ipv4Addr": "1.2.3.4", "ipv6Addr": null}} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["sflow agent ip 6.6.6.6"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec, check_ip_addr - -CE_NC_GET_SFLOW = """ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %s - - - - - - - - - %s - - - - - - - - - - - -""" - - -def is_config_exist(cmp_cfg, test_cfg): - """is configuration exist?""" - - if not cmp_cfg or not test_cfg: - return False - - return bool(test_cfg in cmp_cfg) - - -def is_valid_ip_vpn(vpname): - """check ip vpn""" - - if not vpname: - return False - - if vpname == "_public_": - return False - - if len(vpname) < 1 or len(vpname) > 31: - return False - - return True - - -def get_ip_version(address): - """get ip version fast""" - - if not address: - return None - - if address.count(':') >= 2 and address.count(":") <= 7: - return "ipv6" - elif address.count('.') == 3: - return "ipv4" - else: - return None - - -def get_interface_type(interface): - """get the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class Sflow(object): - """Manages sFlow""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.agent_ip = self.module.params['agent_ip'] - self.agent_version = None - self.source_ip = self.module.params['source_ip'] - self.source_version = None - self.export_route = self.module.params['export_route'] - self.rate_limit = self.module.params['rate_limit'] - self.rate_limit_slot = self.module.params['rate_limit_slot'] - self.forward_enp_slot = self.module.params['forward_enp_slot'] - self.collector_id = self.module.params['collector_id'] - self.collector_ip = self.module.params['collector_ip'] - self.collector_version = None - self.collector_ip_vpn = self.module.params['collector_ip_vpn'] - self.collector_datagram_size = self.module.params['collector_datagram_size'] - self.collector_udp_port = self.module.params['collector_udp_port'] - self.collector_meth = self.module.params['collector_meth'] - self.collector_description = self.module.params['collector_description'] - self.sflow_interface = self.module.params['sflow_interface'] - self.sample_collector = self.module.params['sample_collector'] or list() - self.sample_rate = self.module.params['sample_rate'] - self.sample_length = self.module.params['sample_length'] - self.sample_direction = self.module.params['sample_direction'] - self.counter_collector = self.module.params['counter_collector'] or list() - self.counter_interval = self.module.params['counter_interval'] - self.state = self.module.params['state'] - - # state - self.config = "" # current config - self.sflow_dict = dict() - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - - required_together = [("collector_id", "collector_ip")] - self.module = AnsibleModule( - argument_spec=self.spec, required_together=required_together, supports_check_mode=True) - - def check_response(self, con_obj, xml_name): - """Check if response message is already succeed""" - - xml_str = con_obj.xml - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def netconf_set_config(self, xml_str, xml_name): - """netconf set config""" - - rcv_xml = set_nc_config(self.module, xml_str) - if "" not in rcv_xml: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_sflow_dict(self): - """ sflow config dict""" - - sflow_dict = dict(source=list(), agent=dict(), collector=list(), - sampling=dict(), counter=dict(), export=dict()) - conf_str = CE_NC_GET_SFLOW % ( - self.sflow_interface, self.sflow_interface) - - if not self.collector_meth: - conf_str = conf_str.replace("", "") - - rcv_xml = get_nc_config(self.module, conf_str) - - if "" in rcv_xml: - return sflow_dict - - xml_str = rcv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - - # get source info - srcs = root.findall("sflow/sources/source") - if srcs: - for src in srcs: - attrs = dict() - for attr in src: - if attr.tag in ["family", "ipv4Addr", "ipv6Addr"]: - attrs[attr.tag] = attr.text - sflow_dict["source"].append(attrs) - - # get agent info - agent = root.find("sflow/agents/agent") - if agent: - for attr in agent: - if attr.tag in ["family", "ipv4Addr", "ipv6Addr"]: - sflow_dict["agent"][attr.tag] = attr.text - - # get collector info - collectors = root.findall("sflow/collectors/collector") - if collectors: - for collector in collectors: - attrs = dict() - for attr in collector: - if attr.tag in ["collectorID", "family", "ipv4Addr", "ipv6Addr", - "vrfName", "datagramSize", "port", "description", "meth"]: - attrs[attr.tag] = attr.text - sflow_dict["collector"].append(attrs) - - # get sampling info - sample = root.find("sflow/samplings/sampling") - if sample: - for attr in sample: - if attr.tag in ["ifName", "collectorID", "direction", "length", "rate"]: - sflow_dict["sampling"][attr.tag] = attr.text - - # get counter info - counter = root.find("sflow/counters/counter") - if counter: - for attr in counter: - if attr.tag in ["ifName", "collectorID", "interval"]: - sflow_dict["counter"][attr.tag] = attr.text - - # get export info - export = root.find("sflow/exports/export") - if export: - for attr in export: - if attr.tag == "ExportRoute": - sflow_dict["export"][attr.tag] = attr.text - - return sflow_dict - - def config_agent(self): - """configures sFlow agent""" - - xml_str = '' - if not self.agent_ip: - return xml_str - - self.agent_version = get_ip_version(self.agent_ip) - if not self.agent_version: - self.module.fail_json(msg="Error: agent_ip is invalid.") - - if self.state == "present": - if self.agent_ip != self.sflow_dict["agent"].get("ipv4Addr") \ - and self.agent_ip != self.sflow_dict["agent"].get("ipv6Addr"): - xml_str += '' - xml_str += '%s' % self.agent_version - if self.agent_version == "ipv4": - xml_str += '%s' % self.agent_ip - self.updates_cmd.append("sflow agent ip %s" % self.agent_ip) - else: - xml_str += '%s' % self.agent_ip - self.updates_cmd.append("sflow agent ipv6 %s" % self.agent_ip) - xml_str += '' - - else: - if self.agent_ip == self.sflow_dict["agent"].get("ipv4Addr") \ - or self.agent_ip == self.sflow_dict["agent"].get("ipv6Addr"): - xml_str += '' - self.updates_cmd.append("undo sflow agent") - - return xml_str - - def config_source(self): - """configures the source IP address for sFlow packets""" - - xml_str = '' - if not self.source_ip: - return xml_str - - self.source_version = get_ip_version(self.source_ip) - if not self.source_version: - self.module.fail_json(msg="Error: source_ip is invalid.") - - src_dict = dict() - for src in self.sflow_dict["source"]: - if src.get("family") == self.source_version: - src_dict = src - break - - if self.state == "present": - if self.source_ip != src_dict.get("ipv4Addr") \ - and self.source_ip != src_dict.get("ipv6Addr"): - xml_str += '' - xml_str += '%s' % self.source_version - if self.source_version == "ipv4": - xml_str += '%s' % self.source_ip - self.updates_cmd.append("sflow source ip %s" % self.source_ip) - else: - xml_str += '%s' % self.source_ip - self.updates_cmd.append( - "sflow source ipv6 %s" % self.source_ip) - xml_str += '' - else: - if self.source_ip == src_dict.get("ipv4Addr"): - xml_str += 'ipv4' - self.updates_cmd.append("undo sflow source ip %s" % self.source_ip) - elif self.source_ip == src_dict.get("ipv6Addr"): - xml_str += 'ipv6' - self.updates_cmd.append("undo sflow source ipv6 %s" % self.source_ip) - - return xml_str - - def config_collector(self): - """creates an sFlow collector and sets or modifies optional parameters for the sFlow collector""" - - xml_str = '' - if not self.collector_id: - return xml_str - - if self.state == "present" and not self.collector_ip: - return xml_str - - if self.collector_ip: - self.collector_version = get_ip_version(self.collector_ip) - if not self.collector_version: - self.module.fail_json(msg="Error: collector_ip is invalid.") - - # get collector dict - exist_dict = dict() - for collector in self.sflow_dict["collector"]: - if collector.get("collectorID") == self.collector_id: - exist_dict = collector - break - - change = False - if self.state == "present": - if not exist_dict: - change = True - elif self.collector_version != exist_dict.get("family"): - change = True - elif self.collector_version == "ipv4" and self.collector_ip != exist_dict.get("ipv4Addr"): - change = True - elif self.collector_version == "ipv6" and self.collector_ip != exist_dict.get("ipv6Addr"): - change = True - elif self.collector_ip_vpn and self.collector_ip_vpn != exist_dict.get("vrfName"): - change = True - elif not self.collector_ip_vpn and exist_dict.get("vrfName") != "_public_": - change = True - elif self.collector_udp_port and self.collector_udp_port != exist_dict.get("port"): - change = True - elif not self.collector_udp_port and exist_dict.get("port") != "6343": - change = True - elif self.collector_datagram_size and self.collector_datagram_size != exist_dict.get("datagramSize"): - change = True - elif not self.collector_datagram_size and exist_dict.get("datagramSize") != "1400": - change = True - elif self.collector_meth and self.collector_meth != exist_dict.get("meth"): - change = True - elif not self.collector_meth and exist_dict.get("meth") and exist_dict.get("meth") != "meth": - change = True - elif self.collector_description and self.collector_description != exist_dict.get("description"): - change = True - elif not self.collector_description and exist_dict.get("description"): - change = True - else: - pass - else: # absent - # collector not exist - if not exist_dict: - return xml_str - if self.collector_version and self.collector_version != exist_dict.get("family"): - return xml_str - if self.collector_version == "ipv4" and self.collector_ip != exist_dict.get("ipv4Addr"): - return xml_str - if self.collector_version == "ipv6" and self.collector_ip != exist_dict.get("ipv6Addr"): - return xml_str - if self.collector_ip_vpn and self.collector_ip_vpn != exist_dict.get("vrfName"): - return xml_str - if self.collector_udp_port and self.collector_udp_port != exist_dict.get("port"): - return xml_str - if self.collector_datagram_size and self.collector_datagram_size != exist_dict.get("datagramSize"): - return xml_str - if self.collector_meth and self.collector_meth != exist_dict.get("meth"): - return xml_str - if self.collector_description and self.collector_description != exist_dict.get("description"): - return xml_str - change = True - - if not change: - return xml_str - - # update or delete - if self.state == "absent": - xml_str += '%s' % self.collector_id - self.updates_cmd.append("undo collector %s" % self.collector_id) - else: - xml_str += '%s' % self.collector_id - cmd = "sflow collector %s" % self.collector_id - xml_str += '%s' % self.collector_version - if self.collector_version == "ipv4": - cmd += " ip %s" % self.collector_ip - xml_str += '%s' % self.collector_ip - else: - cmd += " ipv6 %s" % self.collector_ip - xml_str += '%s' % self.collector_ip - if self.collector_ip_vpn: - cmd += " vpn-instance %s" % self.collector_ip_vpn - xml_str += '%s' % self.collector_ip_vpn - if self.collector_datagram_size: - cmd += " length %s" % self.collector_datagram_size - xml_str += '%s' % self.collector_datagram_size - if self.collector_udp_port: - cmd += " udp-port %s" % self.collector_udp_port - xml_str += '%s' % self.collector_udp_port - if self.collector_description: - cmd += " description %s" % self.collector_description - xml_str += '%s' % self.collector_description - else: - xml_str += '' - if self.collector_meth: - if self.collector_meth == "enhanced": - cmd += " enhanced" - xml_str += '%s' % self.collector_meth - self.updates_cmd.append(cmd) - - xml_str += "" - - return xml_str - - def config_sampling(self): - """configure sflow sampling on an interface""" - - xml_str = '' - if not self.sflow_interface: - return xml_str - - if not self.sflow_dict["sampling"] and self.state == "absent": - return xml_str - - self.updates_cmd.append("interface %s" % self.sflow_interface) - if self.state == "present": - xml_str += '%s' % self.sflow_interface - else: - xml_str += '%s' % self.sflow_interface - - # sample_collector - if self.sample_collector: - if self.sflow_dict["sampling"].get("collectorID") \ - and self.sflow_dict["sampling"].get("collectorID") != "invalid": - existing = self.sflow_dict["sampling"].get("collectorID").split(',') - else: - existing = list() - - if self.state == "present": - diff = list(set(self.sample_collector) - set(existing)) - if diff: - self.updates_cmd.append( - "sflow sampling collector %s" % ' '.join(diff)) - new_set = list(self.sample_collector + existing) - xml_str += '%s' % ','.join(list(set(new_set))) - else: - same = list(set(self.sample_collector) & set(existing)) - if same: - self.updates_cmd.append( - "undo sflow sampling collector %s" % ' '.join(same)) - xml_str += '%s' % ','.join(list(set(same))) - - # sample_rate - if self.sample_rate: - exist = bool(self.sample_rate == self.sflow_dict["sampling"].get("rate")) - if self.state == "present" and not exist: - self.updates_cmd.append( - "sflow sampling rate %s" % self.sample_rate) - xml_str += '%s' % self.sample_rate - elif self.state == "absent" and exist: - self.updates_cmd.append( - "undo sflow sampling rate %s" % self.sample_rate) - xml_str += '%s' % self.sample_rate - - # sample_length - if self.sample_length: - exist = bool(self.sample_length == self.sflow_dict["sampling"].get("length")) - if self.state == "present" and not exist: - self.updates_cmd.append( - "sflow sampling length %s" % self.sample_length) - xml_str += '%s' % self.sample_length - elif self.state == "absent" and exist: - self.updates_cmd.append( - "undo sflow sampling length %s" % self.sample_length) - xml_str += '%s' % self.sample_length - - # sample_direction - if self.sample_direction: - direction = list() - if self.sample_direction == "both": - direction = ["inbound", "outbound"] - else: - direction.append(self.sample_direction) - existing = list() - if self.sflow_dict["sampling"].get("direction"): - if self.sflow_dict["sampling"].get("direction") == "both": - existing = ["inbound", "outbound"] - else: - existing.append( - self.sflow_dict["sampling"].get("direction")) - - if self.state == "present": - diff = list(set(direction) - set(existing)) - if diff: - new_set = list(set(direction + existing)) - self.updates_cmd.append( - "sflow sampling %s" % ' '.join(diff)) - if len(new_set) > 1: - new_dir = "both" - else: - new_dir = new_set[0] - xml_str += '%s' % new_dir - else: - same = list(set(existing) & set(direction)) - if same: - self.updates_cmd.append("undo sflow sampling %s" % ' '.join(same)) - if len(same) > 1: - del_dir = "both" - else: - del_dir = same[0] - xml_str += '%s' % del_dir - - if xml_str.endswith(""): - self.updates_cmd.pop() - return "" - - xml_str += '' - - return xml_str - - def config_counter(self): - """configures sflow counter on an interface""" - - xml_str = '' - if not self.sflow_interface: - return xml_str - - if not self.sflow_dict["counter"] and self.state == "absent": - return xml_str - - self.updates_cmd.append("interface %s" % self.sflow_interface) - if self.state == "present": - xml_str += '%s' % self.sflow_interface - else: - xml_str += '%s' % self.sflow_interface - - # counter_collector - if self.counter_collector: - if self.sflow_dict["counter"].get("collectorID") \ - and self.sflow_dict["counter"].get("collectorID") != "invalid": - existing = self.sflow_dict["counter"].get("collectorID").split(',') - else: - existing = list() - - if self.state == "present": - diff = list(set(self.counter_collector) - set(existing)) - if diff: - self.updates_cmd.append("sflow counter collector %s" % ' '.join(diff)) - new_set = list(self.counter_collector + existing) - xml_str += '%s' % ','.join(list(set(new_set))) - else: - same = list(set(self.counter_collector) & set(existing)) - if same: - self.updates_cmd.append( - "undo sflow counter collector %s" % ' '.join(same)) - xml_str += '%s' % ','.join(list(set(same))) - - # counter_interval - if self.counter_interval: - exist = bool(self.counter_interval == self.sflow_dict["counter"].get("interval")) - if self.state == "present" and not exist: - self.updates_cmd.append( - "sflow counter interval %s" % self.counter_interval) - xml_str += '%s' % self.counter_interval - elif self.state == "absent" and exist: - self.updates_cmd.append( - "undo sflow counter interval %s" % self.counter_interval) - xml_str += '%s' % self.counter_interval - - if xml_str.endswith(""): - self.updates_cmd.pop() - return "" - - xml_str += '' - - return xml_str - - def config_export(self): - """configure sflow export""" - - xml_str = '' - if not self.export_route: - return xml_str - - if self.export_route == "enable": - if self.sflow_dict["export"] and self.sflow_dict["export"].get("ExportRoute") == "disable": - xml_str = 'disable' - self.updates_cmd.append("undo sflow export extended-route-data disable") - else: # disable - if not self.sflow_dict["export"] or self.sflow_dict["export"].get("ExportRoute") != "disable": - xml_str = 'disable' - self.updates_cmd.append("sflow export extended-route-data disable") - - return xml_str - - def netconf_load_config(self, xml_str): - """load sflow config by netconf""" - - if not xml_str: - return - - xml_cfg = """ - - - %s - - """ % xml_str - - self.netconf_set_config(xml_cfg, "SET_SFLOW") - self.changed = True - - def check_params(self): - """Check all input params""" - - # check agent_ip - if self.agent_ip: - self.agent_ip = self.agent_ip.upper() - if not check_ip_addr(self.agent_ip): - self.module.fail_json(msg="Error: agent_ip is invalid.") - - # check source_ip - if self.source_ip: - self.source_ip = self.source_ip.upper() - if not check_ip_addr(self.source_ip): - self.module.fail_json(msg="Error: source_ip is invalid.") - - # check collector - if self.collector_id: - # check collector_ip and collector_ip_vpn - if self.collector_ip: - self.collector_ip = self.collector_ip.upper() - if not check_ip_addr(self.collector_ip): - self.module.fail_json( - msg="Error: collector_ip is invalid.") - if self.collector_ip_vpn and not is_valid_ip_vpn(self.collector_ip_vpn): - self.module.fail_json( - msg="Error: collector_ip_vpn is invalid.") - - # check collector_datagram_size ranges from 1024 to 8100 - if self.collector_datagram_size: - if not self.collector_datagram_size.isdigit(): - self.module.fail_json( - msg="Error: collector_datagram_size is not digit.") - if int(self.collector_datagram_size) < 1024 or int(self.collector_datagram_size) > 8100: - self.module.fail_json( - msg="Error: collector_datagram_size is not ranges from 1024 to 8100.") - - # check collector_udp_port ranges from 1 to 65535 - if self.collector_udp_port: - if not self.collector_udp_port.isdigit(): - self.module.fail_json( - msg="Error: collector_udp_port is not digit.") - if int(self.collector_udp_port) < 1 or int(self.collector_udp_port) > 65535: - self.module.fail_json( - msg="Error: collector_udp_port is not ranges from 1 to 65535.") - - # check collector_description 1 to 255 case-sensitive characters - if self.collector_description: - if self.collector_description.count(" "): - self.module.fail_json( - msg="Error: collector_description should without spaces.") - if len(self.collector_description) < 1 or len(self.collector_description) > 255: - self.module.fail_json( - msg="Error: collector_description is not ranges from 1 to 255.") - - # check sflow_interface - if self.sflow_interface: - intf_type = get_interface_type(self.sflow_interface) - if not intf_type: - self.module.fail_json(msg="Error: intf_type is invalid.") - if intf_type not in ['ge', '10ge', '25ge', '4x10ge', '40ge', '100ge', 'eth-trunk']: - self.module.fail_json( - msg="Error: interface %s is not support sFlow." % self.sflow_interface) - - # check sample_collector - if self.sample_collector: - self.sample_collector.sort() - if self.sample_collector not in [["1"], ["2"], ["1", "2"]]: - self.module.fail_json( - msg="Error: sample_collector is invalid.") - - # check sample_rate ranges from 1 to 4294967295 - if self.sample_rate: - if not self.sample_rate.isdigit(): - self.module.fail_json( - msg="Error: sample_rate is not digit.") - if int(self.sample_rate) < 1 or int(self.sample_rate) > 4294967295: - self.module.fail_json( - msg="Error: sample_rate is not ranges from 1 to 4294967295.") - - # check sample_length ranges from 18 to 512 - if self.sample_length: - if not self.sample_length.isdigit(): - self.module.fail_json( - msg="Error: sample_rate is not digit.") - if int(self.sample_length) < 18 or int(self.sample_length) > 512: - self.module.fail_json( - msg="Error: sample_length is not ranges from 18 to 512.") - - # check counter_collector - if self.counter_collector: - self.counter_collector.sort() - if self.counter_collector not in [["1"], ["2"], ["1", "2"]]: - self.module.fail_json( - msg="Error: counter_collector is invalid.") - - # counter_interval ranges from 10 to 4294967295 - if self.counter_interval: - if not self.counter_interval.isdigit(): - self.module.fail_json( - msg="Error: counter_interval is not digit.") - if int(self.counter_interval) < 10 or int(self.counter_interval) > 4294967295: - self.module.fail_json( - msg="Error: sample_length is not ranges from 10 to 4294967295.") - - if self.rate_limit or self.rate_limit_slot or self.forward_enp_slot: - self.module.fail_json(msg="Error: The following parameters cannot be configured" - "because XML mode is not supported:rate_limit,rate_limit_slot,forward_enp_slot.") - - def get_proposed(self): - """get proposed info""" - - # base config - if self.agent_ip: - self.proposed["agent_ip"] = self.agent_ip - if self.source_ip: - self.proposed["source_ip"] = self.source_ip - if self.export_route: - self.proposed["export_route"] = self.export_route - if self.rate_limit: - self.proposed["rate_limit"] = self.rate_limit - self.proposed["rate_limit_slot"] = self.rate_limit_slot - if self.forward_enp_slot: - self.proposed["forward_enp_slot"] = self.forward_enp_slot - if self.collector_id: - self.proposed["collector_id"] = self.collector_id - if self.collector_ip: - self.proposed["collector_ip"] = self.collector_ip - self.proposed["collector_ip_vpn"] = self.collector_ip_vpn - if self.collector_datagram_size: - self.proposed[ - "collector_datagram_size"] = self.collector_datagram_size - if self.collector_udp_port: - self.proposed["collector_udp_port"] = self.collector_udp_port - if self.collector_meth: - self.proposed["collector_meth"] = self.collector_meth - if self.collector_description: - self.proposed[ - "collector_description"] = self.collector_description - - # sample and counter config - if self.sflow_interface: - self.proposed["sflow_interface"] = self.sflow_interface - if self.sample_collector: - self.proposed["sample_collector"] = self.sample_collector - if self.sample_rate: - self.proposed["sample_rate"] = self.sample_rate - if self.sample_length: - self.proposed["sample_length"] = self.sample_length - if self.sample_direction: - self.proposed["sample_direction"] = self.sample_direction - if self.counter_collector: - self.proposed["counter_collector"] = self.counter_collector - if self.counter_interval: - self.proposed["counter_interval"] = self.counter_interval - - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.sflow_dict: - return - - if self.agent_ip: - self.existing["agent"] = self.sflow_dict["agent"] - if self.source_ip: - self.existing["source"] = self.sflow_dict["source"] - if self.collector_id: - self.existing["collector"] = self.sflow_dict["collector"] - if self.export_route: - self.existing["export"] = self.sflow_dict["export"] - - if self.sflow_interface: - self.existing["sampling"] = self.sflow_dict["sampling"] - self.existing["counter"] = self.sflow_dict["counter"] - - def get_end_state(self): - """get end state info""" - - sflow_dict = self.get_sflow_dict() - if not sflow_dict: - return - - if self.agent_ip: - self.end_state["agent"] = sflow_dict["agent"] - if self.source_ip: - self.end_state["source"] = sflow_dict["source"] - if self.collector_id: - self.end_state["collector"] = sflow_dict["collector"] - if self.export_route: - self.end_state["export"] = sflow_dict["export"] - - if self.sflow_interface: - self.end_state["sampling"] = sflow_dict["sampling"] - self.end_state["counter"] = sflow_dict["counter"] - if self.existing == self.end_state: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.sflow_dict = self.get_sflow_dict() - self.get_existing() - self.get_proposed() - - # deal present or absent - xml_str = '' - if self.export_route: - xml_str += self.config_export() - if self.agent_ip: - xml_str += self.config_agent() - if self.source_ip: - xml_str += self.config_source() - - if self.state == "present": - if self.collector_id and self.collector_ip: - xml_str += self.config_collector() - if self.sflow_interface: - xml_str += self.config_sampling() - xml_str += self.config_counter() - else: - if self.sflow_interface: - xml_str += self.config_sampling() - xml_str += self.config_counter() - if self.collector_id: - xml_str += self.config_collector() - - if xml_str: - self.netconf_load_config(xml_str) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - agent_ip=dict(required=False, type='str'), - source_ip=dict(required=False, type='str'), - export_route=dict(required=False, type='str', - choices=['enable', 'disable']), - rate_limit=dict(required=False, removed_in_version=2.13, type='str'), - rate_limit_slot=dict(required=False, removed_in_version=2.13, type='str'), - forward_enp_slot=dict(required=False, removed_in_version=2.13, type='str'), - collector_id=dict(required=False, type='str', choices=['1', '2']), - collector_ip=dict(required=False, type='str'), - collector_ip_vpn=dict(required=False, type='str'), - collector_datagram_size=dict(required=False, type='str'), - collector_udp_port=dict(required=False, type='str'), - collector_meth=dict(required=False, type='str', - choices=['meth', 'enhanced']), - collector_description=dict(required=False, type='str'), - sflow_interface=dict(required=False, type='str'), - sample_collector=dict(required=False, type='list'), - sample_rate=dict(required=False, type='str'), - sample_length=dict(required=False, type='str'), - sample_direction=dict(required=False, type='str', - choices=['inbound', 'outbound', 'both']), - counter_collector=dict(required=False, type='list'), - counter_interval=dict(required=False, type='str'), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - - argument_spec.update(ce_argument_spec) - module = Sflow(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_snmp_community.py b/plugins/modules/network/cloudengine/ce_snmp_community.py deleted file mode 100644 index be0557c41a..0000000000 --- a/plugins/modules/network/cloudengine/ce_snmp_community.py +++ /dev/null @@ -1,979 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_snmp_community -short_description: Manages SNMP community configuration on HUAWEI CloudEngine switches. -description: - - Manages SNMP community configuration on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - acl_number: - description: - - Access control list number. - community_name: - description: - - Unique name to identify the community. - access_right: - description: - - Access right read or write. - choices: ['read','write'] - community_mib_view: - description: - - Mib view name. - group_name: - description: - - Unique name to identify the SNMPv3 group. - security_level: - description: - - Security level indicating whether to use authentication and encryption. - choices: ['noAuthNoPriv', 'authentication', 'privacy'] - read_view: - description: - - Mib view name for read. - write_view: - description: - - Mib view name for write. - notify_view: - description: - - Mib view name for notification. - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' - -- name: CloudEngine snmp community test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config SNMP community" - ce_snmp_community: - state: present - community_name: Wdz123456789 - access_right: write - provider: "{{ cli }}" - - - name: "Undo SNMP community" - ce_snmp_community: - state: absent - community_name: Wdz123456789 - access_right: write - provider: "{{ cli }}" - - - name: "Config SNMP group" - ce_snmp_community: - state: present - group_name: wdz_group - security_level: noAuthNoPriv - acl_number: 2000 - provider: "{{ cli }}" - - - name: "Undo SNMP group" - ce_snmp_community: - state: absent - group_name: wdz_group - security_level: noAuthNoPriv - acl_number: 2000 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"acl_number": "2000", "group_name": "wdz_group", - "security_level": "noAuthNoPriv", "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"snmp v3 group": {"snmp_group": ["wdz_group", "noAuthNoPriv", "2000"]}} -updates: - description: command sent to the device - returned: always - type: list - sample: ["snmp-agent group v3 wdz_group noauthentication acl 2000"] -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -# get snmp community -CE_GET_SNMP_COMMUNITY_HEADER = """ - - - - - - -""" -CE_GET_SNMP_COMMUNITY_TAIL = """ - - - - -""" -# merge snmp community -CE_MERGE_SNMP_COMMUNITY_HEADER = """ - - - - - %s - %s -""" -CE_MERGE_SNMP_COMMUNITY_TAIL = """ - - - - -""" -# create snmp community -CE_CREATE_SNMP_COMMUNITY_HEADER = """ - - - - - %s - %s -""" -CE_CREATE_SNMP_COMMUNITY_TAIL = """ - - - - -""" -# delete snmp community -CE_DELETE_SNMP_COMMUNITY_HEADER = """ - - - - - %s - %s -""" -CE_DELETE_SNMP_COMMUNITY_TAIL = """ - - - - -""" - -# get snmp v3 group -CE_GET_SNMP_V3_GROUP_HEADER = """ - - - - - - -""" -CE_GET_SNMP_V3_GROUP_TAIL = """ - - - - -""" -# merge snmp v3 group -CE_MERGE_SNMP_V3_GROUP_HEADER = """ - - - - - %s - %s -""" -CE_MERGE_SNMP_V3_GROUP_TAIL = """ - - - - -""" -# create snmp v3 group -CE_CREATE_SNMP_V3_GROUP_HEADER = """ - - - - - %s - %s -""" -CE_CREATE_SNMP_V3_GROUP_TAIL = """ - - - - -""" -# delete snmp v3 group -CE_DELETE_SNMP_V3_GROUP_HEADER = """ - - - - - %s - %s -""" -CE_DELETE_SNMP_V3_GROUP_TAIL = """ - - - - -""" - - -class SnmpCommunity(object): - """ Manages SNMP community configuration """ - - def netconf_get_config(self, **kwargs): - """ Get configure through netconf """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = get_nc_config(module, conf_str) - - return xml_str - - def netconf_set_config(self, **kwargs): - """ Set configure through netconf """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = set_nc_config(module, conf_str) - - return xml_str - - def check_snmp_community_args(self, **kwargs): - """ Check snmp community args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - result["community_info"] = [] - state = module.params['state'] - community_name = module.params['community_name'] - access_right = module.params['access_right'] - acl_number = module.params['acl_number'] - community_mib_view = module.params['community_mib_view'] - - if community_name and access_right: - if len(community_name) > 32 or len(community_name) == 0: - module.fail_json( - msg='Error: The len of community_name %s is out of [1 - 32].' % community_name) - - if acl_number: - if acl_number.isdigit(): - if int(acl_number) > 2999 or int(acl_number) < 2000: - module.fail_json( - msg='Error: The value of acl_number %s is out of [2000 - 2999].' % acl_number) - else: - if not acl_number[0].isalpha() or len(acl_number) > 32 or len(acl_number) < 1: - module.fail_json( - msg='Error: The len of acl_number %s is out of [1 - 32] or is invalid.' % acl_number) - - if community_mib_view: - if len(community_mib_view) > 32 or len(community_mib_view) == 0: - module.fail_json( - msg='Error: The len of community_mib_view %s is out of [1 - 32].' % community_mib_view) - - conf_str = CE_GET_SNMP_COMMUNITY_HEADER - if acl_number: - conf_str += "" - if community_mib_view: - conf_str += "" - - conf_str += CE_GET_SNMP_COMMUNITY_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - community_info = root.findall("snmp/communitys/community") - if community_info: - for tmp in community_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["communityName", "accessRight", "aclNumber", "mibViewName"]: - tmp_dict[site.tag] = site.text - - result["community_info"].append(tmp_dict) - - if result["community_info"]: - community_name_list = list() - for tmp in result["community_info"]: - if "communityName" in tmp.keys(): - community_name_list.append(tmp["communityName"]) - - if community_name not in community_name_list: - need_cfg = True - else: - need_cfg_bool = True - - for tmp in result["community_info"]: - if tmp["communityName"] == community_name: - - cfg_bool_list = list() - - if access_right: - if "accessRight" in tmp.keys(): - need_cfg_access = False - if tmp["accessRight"] != access_right: - need_cfg_access = True - else: - need_cfg_access = True - - cfg_bool_list.append(need_cfg_access) - - if acl_number: - if "aclNumber" in tmp.keys(): - need_cfg_acl = False - if tmp["aclNumber"] != acl_number: - need_cfg_acl = True - else: - need_cfg_acl = True - - cfg_bool_list.append(need_cfg_acl) - - if community_mib_view: - if "mibViewName" in tmp.keys(): - need_cfg_mib = False - if tmp["mibViewName"] != community_mib_view: - need_cfg_mib = True - else: - need_cfg_mib = True - cfg_bool_list.append(need_cfg_mib) - - if True not in cfg_bool_list: - need_cfg_bool = False - - if state == "present": - if not need_cfg_bool: - need_cfg = False - else: - need_cfg = True - else: - if not need_cfg_bool: - need_cfg = True - else: - need_cfg = False - - result["need_cfg"] = need_cfg - return result - - def check_snmp_v3_group_args(self, **kwargs): - """ Check snmp v3 group args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - result["group_info"] = [] - state = module.params['state'] - group_name = module.params['group_name'] - security_level = module.params['security_level'] - acl_number = module.params['acl_number'] - read_view = module.params['read_view'] - write_view = module.params['write_view'] - notify_view = module.params['notify_view'] - - community_name = module.params['community_name'] - access_right = module.params['access_right'] - - if group_name and security_level: - - if community_name and access_right: - module.fail_json( - msg='Error: Community is used for v1/v2c, group_name is used for v3, do not ' - 'input at the same time.') - - if len(group_name) > 32 or len(group_name) == 0: - module.fail_json( - msg='Error: The len of group_name %s is out of [1 - 32].' % group_name) - - if acl_number: - if acl_number.isdigit(): - if int(acl_number) > 2999 or int(acl_number) < 2000: - module.fail_json( - msg='Error: The value of acl_number %s is out of [2000 - 2999].' % acl_number) - else: - if not acl_number[0].isalpha() or len(acl_number) > 32 or len(acl_number) < 1: - module.fail_json( - msg='Error: The len of acl_number %s is out of [1 - 32] or is invalid.' % acl_number) - - if read_view: - if len(read_view) > 32 or len(read_view) < 1: - module.fail_json( - msg='Error: The len of read_view %s is out of [1 - 32].' % read_view) - - if write_view: - if len(write_view) > 32 or len(write_view) < 1: - module.fail_json( - msg='Error: The len of write_view %s is out of [1 - 32].' % write_view) - - if notify_view: - if len(notify_view) > 32 or len(notify_view) < 1: - module.fail_json( - msg='Error: The len of notify_view %s is out of [1 - 32].' % notify_view) - - conf_str = CE_GET_SNMP_V3_GROUP_HEADER - if acl_number: - conf_str += "" - if read_view: - conf_str += "" - if write_view: - conf_str += "" - if notify_view: - conf_str += "" - - conf_str += CE_GET_SNMP_V3_GROUP_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - group_info = root.findall("snmp/snmpv3Groups/snmpv3Group") - if group_info: - for tmp in group_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["groupName", "securityLevel", "readViewName", "writeViewName", - "notifyViewName", "aclNumber"]: - tmp_dict[site.tag] = site.text - - result["group_info"].append(tmp_dict) - - if result["group_info"]: - group_name_list = list() - - for tmp in result["group_info"]: - if "groupName" in tmp.keys(): - group_name_list.append(tmp["groupName"]) - if group_name not in group_name_list: - if state == "present": - need_cfg = True - else: - need_cfg = False - else: - need_cfg_bool = True - for tmp in result["group_info"]: - if tmp["groupName"] == group_name: - - cfg_bool_list = list() - - if security_level: - if "securityLevel" in tmp.keys(): - need_cfg_group = False - if tmp["securityLevel"] != security_level: - need_cfg_group = True - else: - need_cfg_group = True - - cfg_bool_list.append(need_cfg_group) - - if acl_number: - if "aclNumber" in tmp.keys(): - need_cfg_acl = False - if tmp["aclNumber"] != acl_number: - need_cfg_acl = True - else: - need_cfg_acl = True - - cfg_bool_list.append(need_cfg_acl) - - if read_view: - if "readViewName" in tmp.keys(): - need_cfg_read = False - if tmp["readViewName"] != read_view: - need_cfg_read = True - else: - need_cfg_read = True - cfg_bool_list.append(need_cfg_read) - - if write_view: - if "writeViewName" in tmp.keys(): - need_cfg_write = False - if tmp["writeViewName"] != write_view: - need_cfg_write = True - else: - need_cfg_write = True - cfg_bool_list.append(need_cfg_write) - - if notify_view: - if "notifyViewName" in tmp.keys(): - need_cfg_notify = False - if tmp["notifyViewName"] != notify_view: - need_cfg_notify = True - else: - need_cfg_notify = True - cfg_bool_list.append(need_cfg_notify) - - if True not in cfg_bool_list: - need_cfg_bool = False - - if state == "present": - if not need_cfg_bool: - need_cfg = False - else: - need_cfg = True - else: - if not need_cfg_bool: - need_cfg = True - else: - need_cfg = False - - result["need_cfg"] = need_cfg - return result - - def merge_snmp_community(self, **kwargs): - """ Merge snmp community operation """ - - module = kwargs["module"] - community_name = module.params['community_name'] - access_right = module.params['access_right'] - acl_number = module.params['acl_number'] - community_mib_view = module.params['community_mib_view'] - - conf_str = CE_MERGE_SNMP_COMMUNITY_HEADER % ( - community_name, access_right) - if acl_number: - conf_str += "%s" % acl_number - if community_mib_view: - conf_str += "%s" % community_mib_view - - conf_str += CE_MERGE_SNMP_COMMUNITY_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge snmp community failed.') - - community_safe_name = "******" - - cmd = "snmp-agent community %s %s" % (access_right, community_safe_name) - - if acl_number: - cmd += " acl %s" % acl_number - if community_mib_view: - cmd += " mib-view %s" % community_mib_view - - return cmd - - def create_snmp_community(self, **kwargs): - """ Create snmp community operation """ - - module = kwargs["module"] - community_name = module.params['community_name'] - access_right = module.params['access_right'] - acl_number = module.params['acl_number'] - community_mib_view = module.params['community_mib_view'] - - conf_str = CE_CREATE_SNMP_COMMUNITY_HEADER % ( - community_name, access_right) - if acl_number: - conf_str += "%s" % acl_number - if community_mib_view: - conf_str += "%s" % community_mib_view - - conf_str += CE_CREATE_SNMP_COMMUNITY_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create snmp community failed.') - - community_safe_name = "******" - - cmd = "snmp-agent community %s %s" % (access_right, community_safe_name) - - if acl_number: - cmd += " acl %s" % acl_number - if community_mib_view: - cmd += " mib-view %s" % community_mib_view - - return cmd - - def delete_snmp_community(self, **kwargs): - """ Delete snmp community operation """ - - module = kwargs["module"] - community_name = module.params['community_name'] - access_right = module.params['access_right'] - acl_number = module.params['acl_number'] - community_mib_view = module.params['community_mib_view'] - - conf_str = CE_DELETE_SNMP_COMMUNITY_HEADER % ( - community_name, access_right) - if acl_number: - conf_str += "%s" % acl_number - if community_mib_view: - conf_str += "%s" % community_mib_view - - conf_str += CE_DELETE_SNMP_COMMUNITY_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create snmp community failed.') - - community_safe_name = "******" - cmd = "undo snmp-agent community %s %s" % ( - access_right, community_safe_name) - - return cmd - - def merge_snmp_v3_group(self, **kwargs): - """ Merge snmp v3 group operation """ - - module = kwargs["module"] - group_name = module.params['group_name'] - security_level = module.params['security_level'] - acl_number = module.params['acl_number'] - read_view = module.params['read_view'] - write_view = module.params['write_view'] - notify_view = module.params['notify_view'] - - conf_str = CE_MERGE_SNMP_V3_GROUP_HEADER % (group_name, security_level) - if acl_number: - conf_str += "%s" % acl_number - if read_view: - conf_str += "%s" % read_view - if write_view: - conf_str += "%s" % write_view - if notify_view: - conf_str += "%s" % notify_view - conf_str += CE_MERGE_SNMP_V3_GROUP_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge snmp v3 group failed.') - - if security_level == "noAuthNoPriv": - security_level_cli = "noauthentication" - elif security_level == "authentication": - security_level_cli = "authentication" - elif security_level == "privacy": - security_level_cli = "privacy" - - cmd = "snmp-agent group v3 %s %s" % (group_name, security_level_cli) - - if read_view: - cmd += " read-view %s" % read_view - if write_view: - cmd += " write-view %s" % write_view - if notify_view: - cmd += " notify-view %s" % notify_view - if acl_number: - cmd += " acl %s" % acl_number - - return cmd - - def create_snmp_v3_group(self, **kwargs): - """ Create snmp v3 group operation """ - - module = kwargs["module"] - group_name = module.params['group_name'] - security_level = module.params['security_level'] - acl_number = module.params['acl_number'] - read_view = module.params['read_view'] - write_view = module.params['write_view'] - notify_view = module.params['notify_view'] - - conf_str = CE_CREATE_SNMP_V3_GROUP_HEADER % ( - group_name, security_level) - if acl_number: - conf_str += "%s" % acl_number - if read_view: - conf_str += "%s" % read_view - if write_view: - conf_str += "%s" % write_view - if notify_view: - conf_str += "%s" % notify_view - conf_str += CE_CREATE_SNMP_V3_GROUP_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create snmp v3 group failed.') - - if security_level == "noAuthNoPriv": - security_level_cli = "noauthentication" - elif security_level == "authentication": - security_level_cli = "authentication" - elif security_level == "privacy": - security_level_cli = "privacy" - - cmd = "snmp-agent group v3 %s %s" % (group_name, security_level_cli) - - if read_view: - cmd += " read-view %s" % read_view - if write_view: - cmd += " write-view %s" % write_view - if notify_view: - cmd += " notify-view %s" % notify_view - if acl_number: - cmd += " acl %s" % acl_number - - return cmd - - def delete_snmp_v3_group(self, **kwargs): - """ Delete snmp v3 group operation """ - - module = kwargs["module"] - group_name = module.params['group_name'] - security_level = module.params['security_level'] - acl_number = module.params['acl_number'] - read_view = module.params['read_view'] - write_view = module.params['write_view'] - notify_view = module.params['notify_view'] - - conf_str = CE_DELETE_SNMP_V3_GROUP_HEADER % ( - group_name, security_level) - if acl_number: - conf_str += "%s" % acl_number - if read_view: - conf_str += "%s" % read_view - if write_view: - conf_str += "%s" % write_view - if notify_view: - conf_str += "%s" % notify_view - conf_str += CE_DELETE_SNMP_V3_GROUP_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete snmp v3 group failed.') - - if security_level == "noAuthNoPriv": - security_level_cli = "noauthentication" - elif security_level == "authentication": - security_level_cli = "authentication" - elif security_level == "privacy": - security_level_cli = "privacy" - - cmd = "undo snmp-agent group v3 %s %s" % ( - group_name, security_level_cli) - - return cmd - - -def main(): - """ main function """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - acl_number=dict(type='str'), - community_name=dict(type='str', no_log=True), - access_right=dict(choices=['read', 'write']), - community_mib_view=dict(type='str'), - group_name=dict(type='str'), - security_level=dict( - choices=['noAuthNoPriv', 'authentication', 'privacy']), - read_view=dict(type='str'), - write_view=dict(type='str'), - notify_view=dict(type='str') - ) - - argument_spec.update(ce_argument_spec) - required_together = [("community_name", "access_right"), ("security_level", "group_name")] - module = AnsibleModule( - argument_spec=argument_spec, - required_together=required_together, - supports_check_mode=True - ) - - changed = False - proposed = dict() - existing = dict() - end_state = dict() - updates = [] - - state = module.params['state'] - acl_number = module.params['acl_number'] - community_name = module.params['community_name'] - community_mib_view = module.params['community_mib_view'] - access_right = module.params['access_right'] - group_name = module.params['group_name'] - security_level = module.params['security_level'] - read_view = module.params['read_view'] - write_view = module.params['write_view'] - notify_view = module.params['notify_view'] - - snmp_community_obj = SnmpCommunity() - - if not snmp_community_obj: - module.fail_json(msg='Error: Init module failed.') - - snmp_community_rst = snmp_community_obj.check_snmp_community_args( - module=module) - snmp_v3_group_rst = snmp_community_obj.check_snmp_v3_group_args( - module=module) - - # get proposed - proposed["state"] = state - if acl_number: - proposed["acl_number"] = acl_number - if community_name: - proposed["community_name"] = community_name - if community_mib_view: - proposed["community_mib_view"] = community_mib_view - if access_right: - proposed["access_right"] = access_right - if group_name: - proposed["group_name"] = group_name - if security_level: - proposed["security_level"] = security_level - if read_view: - proposed["read_view"] = read_view - if write_view: - proposed["write_view"] = write_view - if notify_view: - proposed["notify_view"] = notify_view - - # state exist snmp community config - exist_tmp = dict() - for item in snmp_community_rst: - if item != "need_cfg": - exist_tmp[item] = snmp_community_rst[item] - - if exist_tmp: - existing["snmp community"] = exist_tmp - # state exist snmp v3 group config - exist_tmp = dict() - for item in snmp_v3_group_rst: - if item != "need_cfg": - exist_tmp[item] = snmp_v3_group_rst[item] - - if exist_tmp: - existing["snmp v3 group"] = exist_tmp - - if state == "present": - if snmp_community_rst["need_cfg"]: - if len(snmp_community_rst["community_info"]) != 0: - cmd = snmp_community_obj.merge_snmp_community(module=module) - changed = True - updates.append(cmd) - else: - cmd = snmp_community_obj.create_snmp_community(module=module) - changed = True - updates.append(cmd) - - if snmp_v3_group_rst["need_cfg"]: - if len(snmp_v3_group_rst["group_info"]): - cmd = snmp_community_obj.merge_snmp_v3_group(module=module) - changed = True - updates.append(cmd) - else: - cmd = snmp_community_obj.create_snmp_v3_group(module=module) - changed = True - updates.append(cmd) - - else: - if snmp_community_rst["need_cfg"]: - cmd = snmp_community_obj.delete_snmp_community(module=module) - changed = True - updates.append(cmd) - if snmp_v3_group_rst["need_cfg"]: - cmd = snmp_community_obj.delete_snmp_v3_group(module=module) - changed = True - updates.append(cmd) - - # state end snmp community config - snmp_community_rst = snmp_community_obj.check_snmp_community_args( - module=module) - end_tmp = dict() - for item in snmp_community_rst: - if item != "need_cfg": - end_tmp[item] = snmp_community_rst[item] - end_tmp[item] = snmp_community_rst[item] - if end_tmp: - end_state["snmp community"] = end_tmp - # state end snmp v3 group config - snmp_v3_group_rst = snmp_community_obj.check_snmp_v3_group_args( - module=module) - end_tmp = dict() - for item in snmp_v3_group_rst: - if item != "need_cfg": - end_tmp[item] = snmp_v3_group_rst[item] - if end_tmp: - end_state["snmp v3 group"] = end_tmp - - results = dict() - results['proposed'] = proposed - results['existing'] = existing - results['changed'] = changed - results['end_state'] = end_state - results['updates'] = updates - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_snmp_contact.py b/plugins/modules/network/cloudengine/ce_snmp_contact.py deleted file mode 100644 index 52f9616230..0000000000 --- a/plugins/modules/network/cloudengine/ce_snmp_contact.py +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_snmp_contact -short_description: Manages SNMP contact configuration on HUAWEI CloudEngine switches. -description: - - Manages SNMP contact configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - contact: - description: - - Contact information. - required: true - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' - -- name: CloudEngine snmp contact test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config SNMP contact" - ce_snmp_contact: - state: present - contact: call Operator at 010-99999999 - provider: "{{ cli }}" - - - name: "Undo SNMP contact" - ce_snmp_contact: - state: absent - contact: call Operator at 010-99999999 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"contact": "call Operator at 010-99999999", - "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"contact": "call Operator at 010-99999999"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["snmp-agent sys-info contact call Operator at 010-99999999"] -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import exec_command, load_config, ce_argument_spec - - -class SnmpContact(object): - """ Manages SNMP contact configuration """ - - def __init__(self, **kwargs): - """ Class init """ - - # module - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # config - self.cur_cfg = dict() - - # module args - self.state = self.module.params['state'] - self.contact = self.module.params['contact'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def check_args(self): - """ Check invalid args """ - - if self.contact: - if len(self.contact) > 255 or len(self.contact) < 1: - self.module.fail_json( - msg='Error: The len of contact %s is out of [1 - 255].' % self.contact) - else: - self.module.fail_json( - msg='Error: The len of contact is 0.') - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def get_proposed(self): - """ Get proposed state """ - - self.proposed["state"] = self.state - - if self.contact: - self.proposed["contact"] = self.contact - - def get_existing(self): - """ Get existing state """ - - tmp_cfg = self.cli_get_config() - if tmp_cfg: - temp_data = tmp_cfg.split(r"contact ") - if len(temp_data) > 1: - self.cur_cfg["contact"] = temp_data[1] - self.existing["contact"] = temp_data[1] - - def get_end_state(self): - """ Get end state """ - - tmp_cfg = self.cli_get_config() - if tmp_cfg: - temp_data = tmp_cfg.split(r"contact ") - if len(temp_data) > 1: - self.end_state["contact"] = temp_data[1] - - def cli_load_config(self, commands): - """ Load configure by cli """ - - if not self.module.check_mode: - load_config(self.module, commands) - - def cli_get_config(self): - """ Get configure by cli """ - - regular = "| include snmp | include contact" - flags = list() - flags.append(regular) - tmp_cfg = self.get_config(flags) - - return tmp_cfg - - def set_config(self): - """ Set configure by cli """ - - cmd = "snmp-agent sys-info contact %s" % self.contact - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def undo_config(self): - """ Undo configure by cli """ - - cmd = "undo snmp-agent sys-info contact" - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def work(self): - """ Main work function """ - - self.check_args() - self.get_proposed() - self.get_existing() - - if self.state == "present": - if "contact" in self.cur_cfg.keys() and self.contact == self.cur_cfg["contact"]: - pass - else: - self.set_config() - else: - if "contact" in self.cur_cfg.keys() and self.contact == self.cur_cfg["contact"]: - self.undo_config() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - contact=dict(type='str', required=True) - ) - - argument_spec.update(ce_argument_spec) - module = SnmpContact(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_snmp_location.py b/plugins/modules/network/cloudengine/ce_snmp_location.py deleted file mode 100644 index 3187ddeb78..0000000000 --- a/plugins/modules/network/cloudengine/ce_snmp_location.py +++ /dev/null @@ -1,273 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_snmp_location -short_description: Manages SNMP location configuration on HUAWEI CloudEngine switches. -description: - - Manages SNMP location configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - location: - description: - - Location information. - required: true - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' - -- name: CloudEngine snmp location test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config SNMP location" - ce_snmp_location: - state: present - location: nanjing China - provider: "{{ cli }}" - - - name: "Remove SNMP location" - ce_snmp_location: - state: absent - location: nanjing China - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"location": "nanjing China", - "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"location": "nanjing China"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["snmp-agent sys-info location nanjing China"] -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import exec_command, load_config, ce_argument_spec - - -class SnmpLocation(object): - """ Manages SNMP location configuration """ - - def __init__(self, **kwargs): - """ Class init """ - - # module - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # config - self.cur_cfg = dict() - - # module args - self.state = self.module.params['state'] - self.location = self.module.params['location'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def check_args(self): - """ Check invalid args """ - - if self.location: - if len(self.location) > 255 or len(self.location) < 1: - self.module.fail_json( - msg='Error: The len of location %s is out of [1 - 255].' % self.location) - else: - self.module.fail_json( - msg='Error: The len of location is 0.') - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def get_proposed(self): - """ Get proposed state """ - - self.proposed["state"] = self.state - - if self.location: - self.proposed["location"] = self.location - - def get_existing(self): - """ Get existing state """ - - tmp_cfg = self.cli_get_config() - if tmp_cfg: - temp_data = tmp_cfg.split(r"location ") - if len(temp_data) > 1: - self.cur_cfg["location"] = temp_data[1] - self.existing["location"] = temp_data[1] - - def get_end_state(self): - """ Get end state """ - - tmp_cfg = self.cli_get_config() - if tmp_cfg: - temp_data = tmp_cfg.split(r"location ") - if len(temp_data) > 1: - self.end_state["location"] = temp_data[1] - - def cli_load_config(self, commands): - """ Load config by cli """ - - if not self.module.check_mode: - load_config(self.module, commands) - - def cli_get_config(self): - """ Get config by cli """ - - regular = "| include snmp | include location" - flags = list() - flags.append(regular) - tmp_cfg = self.get_config(flags) - - return tmp_cfg - - def set_config(self): - """ Set configure by cli """ - - cmd = "snmp-agent sys-info location %s" % self.location - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def undo_config(self): - """ Undo configure by cli """ - - cmd = "undo snmp-agent sys-info location" - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def work(self): - """ Main work function """ - - self.check_args() - self.get_proposed() - self.get_existing() - - if self.state == "present": - if "location" in self.cur_cfg.keys() and self.location == self.cur_cfg["location"]: - pass - else: - self.set_config() - else: - if "location" in self.cur_cfg.keys() and self.location == self.cur_cfg["location"]: - self.undo_config() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - location=dict(type='str', required=True) - ) - - argument_spec.update(ce_argument_spec) - module = SnmpLocation(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_snmp_target_host.py b/plugins/modules/network/cloudengine/ce_snmp_target_host.py deleted file mode 100644 index 037cdd54f7..0000000000 --- a/plugins/modules/network/cloudengine/ce_snmp_target_host.py +++ /dev/null @@ -1,944 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_snmp_target_host -short_description: Manages SNMP target host configuration on HUAWEI CloudEngine switches. -description: - - Manages SNMP target host configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - version: - description: - - Version(s) Supported by SNMP Engine. - choices: ['none', 'v1', 'v2c', 'v3', 'v1v2c', 'v1v3', 'v2cv3', 'all'] - connect_port: - description: - - Udp port used by SNMP agent to connect the Network management. - host_name: - description: - - Unique name to identify target host entry. - address: - description: - - Network Address. - notify_type: - description: - - To configure notify type as trap or inform. - choices: ['trap','inform'] - vpn_name: - description: - - VPN instance Name. - recv_port: - description: - - UDP Port number used by network management to receive alarm messages. - security_model: - description: - - Security Model. - choices: ['v1','v2c', 'v3'] - security_name: - description: - - Security Name. - security_name_v3: - description: - - Security Name V3. - security_level: - description: - - Security level indicating whether to use authentication and encryption. - choices: ['noAuthNoPriv','authentication', 'privacy'] - is_public_net: - description: - - To enable or disable Public Net-manager for target Host. - default: no_use - choices: ['no_use','true','false'] - interface_name: - description: - - Name of the interface to send the trap message. -''' - -EXAMPLES = ''' - -- name: CloudEngine snmp target host test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config SNMP version" - ce_snmp_target_host: - state: present - version: v2cv3 - provider: "{{ cli }}" - - - name: "Config SNMP target host" - ce_snmp_target_host: - state: present - host_name: test1 - address: 1.1.1.1 - notify_type: trap - vpn_name: js - security_model: v2c - security_name: wdz - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"address": "10.135.182.158", "host_name": "test2", - "notify_type": "trap", "security_level": "authentication", - "security_model": "v3", "security_name_v3": "wdz", - "state": "present", "vpn_name": "js"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"target host info": [{"address": "10.135.182.158", "domain": "snmpUDPDomain", - "nmsName": "test2", "notifyType": "trap", - "securityLevel": "authentication", "securityModel": "v3", - "securityNameV3": "wdz", "vpnInstanceName": "js"}]} -updates: - description: command sent to the device - returned: always - type: list - sample: ["snmp-agent target-host host-name test2 trap address udp-domain 10.135.182.158 vpn-instance js params securityname wdz v3 authentication"] -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, \ - ce_argument_spec, load_config, check_ip_addr - -# get snmp version -CE_GET_SNMP_VERSION = """ - - - - - - - -""" -# merge snmp version -CE_MERGE_SNMP_VERSION = """ - - - - %s - - - -""" - -# get snmp target host -CE_GET_SNMP_TARGET_HOST_HEADER = """ - - - - - -""" -CE_GET_SNMP_TARGET_HOST_TAIL = """ - - - - -""" - -# merge snmp target host -CE_MERGE_SNMP_TARGET_HOST_HEADER = """ - - - - - %s -""" -CE_MERGE_SNMP_TARGET_HOST_TAIL = """ - - - - -""" - -# create snmp target host -CE_CREATE_SNMP_TARGET_HOST_HEADER = """ - - - - - %s -""" -CE_CREATE_SNMP_TARGET_HOST_TAIL = """ - - - - -""" - -# delete snmp target host -CE_DELETE_SNMP_TARGET_HOST_HEADER = """ - - - - - %s -""" -CE_DELETE_SNMP_TARGET_HOST_TAIL = """ - - - - -""" - -# get snmp listen port -CE_GET_SNMP_PORT = """ - - - - - - - -""" - -# merge snmp listen port -CE_MERGE_SNMP_PORT = """ - - - - %s - - - -""" - - -INTERFACE_TYPE = ['ethernet', 'eth-trunk', 'tunnel', 'null', 'loopback', - 'vlanif', '100ge', '40ge', 'mtunnel', '10ge', 'ge', 'meth', 'vbdif', 'nve'] - - -class SnmpTargetHost(object): - """ Manages SNMP target host configuration """ - - def __init__(self, **kwargs): - """ Class init """ - - # module - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - required_together = [("address", "notify_type"), ("address", "notify_type")] - required_if = [ - ["security_model", "v1", ["security_name"]], - ["security_model", "v2c", ["security_name"]], - ["security_model", "v3", ["security_name_v3"]] - ] - self.module = AnsibleModule( - argument_spec=argument_spec, - required_together=required_together, - required_if=required_if, - supports_check_mode=True - ) - - # module args - self.state = self.module.params['state'] - self.version = self.module.params['version'] - self.connect_port = self.module.params['connect_port'] - self.host_name = self.module.params['host_name'] - self.domain = "snmpUDPDomain" - self.address = self.module.params['address'] - self.notify_type = self.module.params['notify_type'] - self.vpn_name = self.module.params['vpn_name'] - self.recv_port = self.module.params['recv_port'] - self.security_model = self.module.params['security_model'] - self.security_name = self.module.params['security_name'] - self.security_name_v3 = self.module.params['security_name_v3'] - self.security_level = self.module.params['security_level'] - self.is_public_net = self.module.params['is_public_net'] - self.interface_name = self.module.params['interface_name'] - - # config - self.cur_cli_cfg = dict() - self.cur_netconf_cfg = dict() - self.end_netconf_cfg = dict() - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def netconf_get_config(self, conf_str): - """ Get configure by netconf """ - - xml_str = get_nc_config(self.module, conf_str) - - return xml_str - - def netconf_set_config(self, conf_str): - """ Set configure by netconf """ - - xml_str = set_nc_config(self.module, conf_str) - - return xml_str - - def check_cli_args(self): - """ Check invalid cli args """ - - if self.connect_port: - if int(self.connect_port) != 161 and (int(self.connect_port) > 65535 or int(self.connect_port) < 1025): - self.module.fail_json( - msg='Error: The value of connect_port %s is out of [161, 1025 - 65535].' % self.connect_port) - - def check_netconf_args(self, result): - """ Check invalid netconf args """ - - need_cfg = True - same_flag = True - delete_flag = False - result["target_host_info"] = [] - - if self.host_name: - - if len(self.host_name) > 32 or len(self.host_name) < 1: - self.module.fail_json( - msg='Error: The len of host_name is out of [1 - 32].') - - if self.vpn_name and self.is_public_net != 'no_use': - if self.is_public_net == "true": - self.module.fail_json( - msg='Error: Do not support vpn_name and is_public_net at the same time.') - - conf_str = CE_GET_SNMP_TARGET_HOST_HEADER - - if self.domain: - conf_str += "" - - if self.address: - if not check_ip_addr(ipaddr=self.address): - self.module.fail_json( - msg='Error: The host address [%s] is invalid.' % self.address) - conf_str += "
" - - if self.notify_type: - conf_str += "" - - if self.vpn_name: - if len(self.vpn_name) > 31 or len(self.vpn_name) < 1: - self.module.fail_json( - msg='Error: The len of vpn_name is out of [1 - 31].') - conf_str += "" - - if self.recv_port: - if int(self.recv_port) > 65535 or int(self.recv_port) < 0: - self.module.fail_json( - msg='Error: The value of recv_port is out of [0 - 65535].') - conf_str += "" - - if self.security_model: - conf_str += "" - - if self.security_name: - if len(self.security_name) > 32 or len(self.security_name) < 1: - self.module.fail_json( - msg='Error: The len of security_name is out of [1 - 32].') - conf_str += "" - - if self.security_name_v3: - if len(self.security_name_v3) > 32 or len(self.security_name_v3) < 1: - self.module.fail_json( - msg='Error: The len of security_name_v3 is out of [1 - 32].') - conf_str += "" - - if self.security_level: - conf_str += "" - - if self.is_public_net != 'no_use': - conf_str += "" - - if self.interface_name: - if len(self.interface_name) > 63 or len(self.interface_name) < 1: - self.module.fail_json( - msg='Error: The len of interface_name is out of [1 - 63].') - - find_flag = False - for item in INTERFACE_TYPE: - if item in self.interface_name.lower(): - find_flag = True - break - if not find_flag: - self.module.fail_json( - msg='Error: Please input full name of interface_name.') - - conf_str += "" - - conf_str += CE_GET_SNMP_TARGET_HOST_TAIL - recv_xml = self.netconf_get_config(conf_str=conf_str) - - if "" in recv_xml: - if self.state == "present": - same_flag = False - else: - delete_flag = False - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - target_host_info = root.findall( - "snmp/targetHosts/targetHost") - if target_host_info: - for tmp in target_host_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["nmsName", "domain", "address", "notifyType", "vpnInstanceName", - "portNumber", "securityModel", "securityName", "securityNameV3", - "securityLevel", "isPublicNet", "interface-name"]: - tmp_dict[site.tag] = site.text - - result["target_host_info"].append(tmp_dict) - - if result["target_host_info"]: - for tmp in result["target_host_info"]: - - same_flag = True - - if "nmsName" in tmp.keys(): - if tmp["nmsName"] != self.host_name: - same_flag = False - else: - delete_flag = True - - if "domain" in tmp.keys(): - if tmp["domain"] != self.domain: - same_flag = False - - if "address" in tmp.keys(): - if tmp["address"] != self.address: - same_flag = False - - if "notifyType" in tmp.keys(): - if tmp["notifyType"] != self.notify_type: - same_flag = False - - if "vpnInstanceName" in tmp.keys(): - if tmp["vpnInstanceName"] != self.vpn_name: - same_flag = False - - if "portNumber" in tmp.keys(): - if tmp["portNumber"] != self.recv_port: - same_flag = False - - if "securityModel" in tmp.keys(): - if tmp["securityModel"] != self.security_model: - same_flag = False - - if "securityName" in tmp.keys(): - if tmp["securityName"] != self.security_name: - same_flag = False - - if "securityNameV3" in tmp.keys(): - if tmp["securityNameV3"] != self.security_name_v3: - same_flag = False - - if "securityLevel" in tmp.keys(): - if tmp["securityLevel"] != self.security_level: - same_flag = False - - if "isPublicNet" in tmp.keys(): - if tmp["isPublicNet"] != self.is_public_net: - same_flag = False - - if "interface-name" in tmp.keys(): - if tmp.get("interface-name") is not None: - if tmp["interface-name"].lower() != self.interface_name.lower(): - same_flag = False - else: - same_flag = False - - if same_flag: - break - - if self.state == "present": - need_cfg = True - if same_flag: - need_cfg = False - else: - need_cfg = False - if delete_flag: - need_cfg = True - - result["need_cfg"] = need_cfg - - def cli_load_config(self, commands): - """ Load configure by cli """ - - if not self.module.check_mode: - load_config(self.module, commands) - - def get_snmp_version(self): - """ Get snmp version """ - - version = None - conf_str = CE_GET_SNMP_VERSION - recv_xml = self.netconf_get_config(conf_str=conf_str) - - if "" in recv_xml: - pass - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - version_info = root.find("snmp/engine") - if version_info: - for site in version_info: - if site.tag in ["version"]: - version = site.text - - return version - - def xml_get_connect_port(self): - """ Get connect port by xml """ - tmp_cfg = None - conf_str = CE_GET_SNMP_PORT - recv_xml = self.netconf_get_config(conf_str=conf_str) - if "" in recv_xml: - pass - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - snmp_port_info = root.findall("snmp/systemCfg/snmpListenPort") - - if snmp_port_info: - tmp_cfg = snmp_port_info[0].text - return tmp_cfg - - def get_proposed(self): - """ Get proposed state """ - - self.proposed["state"] = self.state - - if self.version: - self.proposed["version"] = self.version - if self.connect_port: - self.proposed["connect_port"] = self.connect_port - if self.host_name: - self.proposed["host_name"] = self.host_name - if self.address: - self.proposed["address"] = self.address - if self.notify_type: - self.proposed["notify_type"] = self.notify_type - if self.vpn_name: - self.proposed["vpn_name"] = self.vpn_name - if self.recv_port: - self.proposed["recv_port"] = self.recv_port - if self.security_model: - self.proposed["security_model"] = self.security_model - if self.security_name: - self.proposed["security_name"] = "******" - if self.security_name_v3: - self.proposed["security_name_v3"] = self.security_name_v3 - if self.security_level: - self.proposed["security_level"] = self.security_level - if self.is_public_net != 'no_use': - self.proposed["is_public_net"] = self.is_public_net - if self.interface_name: - self.proposed["interface_name"] = self.interface_name - - def get_existing(self): - """ Get existing state """ - - if self.version: - version = self.get_snmp_version() - if version: - self.cur_cli_cfg["version"] = version - self.existing["version"] = version - - if self.connect_port: - tmp_cfg = self.xml_get_connect_port() - if tmp_cfg: - self.cur_cli_cfg["connect port"] = tmp_cfg - self.existing["connect port"] = tmp_cfg - - if self.host_name: - self.existing["target host info"] = self.cur_netconf_cfg[ - "target_host_info"] - - def get_end_state(self): - """ Get end state """ - - if self.version: - version = self.get_snmp_version() - if version: - self.end_state["version"] = version - - if self.connect_port: - tmp_cfg = self.xml_get_connect_port() - if tmp_cfg: - self.end_state["connect port"] = tmp_cfg - - if self.host_name: - self.end_state["target host info"] = self.end_netconf_cfg[ - "target_host_info"] - if self.existing == self.end_state: - self.changed = False - self.updates_cmd = list() - - def config_version_cli(self): - """ Config version by cli """ - - if "disable" in self.cur_cli_cfg["version"]: - cmd = "snmp-agent sys-info version %s" % self.version - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - else: - if self.version != self.cur_cli_cfg["version"]: - cmd = "snmp-agent sys-info version %s disable" % self.cur_cli_cfg[ - "version"] - self.updates_cmd.append(cmd) - cmd = "snmp-agent sys-info version %s" % self.version - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def undo_config_version_cli(self): - """ Undo config version by cli """ - - if "disable" in self.cur_cli_cfg["version"]: - pass - else: - cmd = "snmp-agent sys-info version %s disable" % self.cur_cli_cfg[ - "version"] - - cmds = list() - cmds.append(cmd) - - self.updates_cmd.append(cmd) - self.cli_load_config(cmds) - self.changed = True - - def config_connect_port_xml(self): - """ Config connect port by xml """ - - if "connect port" in self.cur_cli_cfg.keys(): - if self.cur_cli_cfg["connect port"] == self.connect_port: - pass - else: - cmd = "snmp-agent udp-port %s" % self.connect_port - - cmds = list() - cmds.append(cmd) - - self.updates_cmd.append(cmd) - conf_str = CE_MERGE_SNMP_PORT % self.connect_port - self.netconf_set_config(conf_str=conf_str) - self.changed = True - else: - cmd = "snmp-agent udp-port %s" % self.connect_port - - cmds = list() - cmds.append(cmd) - - self.updates_cmd.append(cmd) - conf_str = CE_MERGE_SNMP_PORT % self.connect_port - self.netconf_set_config(conf_str=conf_str) - self.changed = True - - def undo_config_connect_port_cli(self): - """ Undo config connect port by cli """ - - if "connect port" in self.cur_cli_cfg.keys(): - if not self.cur_cli_cfg["connect port"]: - pass - else: - cmd = "undo snmp-agent udp-port" - - cmds = list() - cmds.append(cmd) - - self.updates_cmd.append(cmd) - connect_port = "161" - conf_str = CE_MERGE_SNMP_PORT % connect_port - self.netconf_set_config(conf_str=conf_str) - self.changed = True - - def merge_snmp_target_host(self): - """ Merge snmp target host operation """ - - conf_str = CE_MERGE_SNMP_TARGET_HOST_HEADER % self.host_name - - if self.domain: - conf_str += "%s" % self.domain - if self.address: - conf_str += "
%s
" % self.address - if self.notify_type: - conf_str += "%s" % self.notify_type - if self.vpn_name: - conf_str += "%s" % self.vpn_name - if self.recv_port: - conf_str += "%s" % self.recv_port - if self.security_model: - conf_str += "%s" % self.security_model - if self.security_name: - conf_str += "%s" % self.security_name - if self.security_name_v3: - conf_str += "%s" % self.security_name_v3 - if self.security_level: - conf_str += "%s" % self.security_level - if self.is_public_net != 'no_use': - conf_str += "%s" % self.is_public_net - if self.interface_name: - conf_str += "%s" % self.interface_name - - conf_str += CE_MERGE_SNMP_TARGET_HOST_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge snmp target host failed.') - - cmd = "snmp-agent target-host host-name %s " % self.host_name - cmd += "%s " % self.notify_type - cmd += "address udp-domain %s " % self.address - - if self.recv_port: - cmd += "udp-port %s " % self.recv_port - if self.interface_name: - cmd += "source %s " % self.interface_name - if self.vpn_name: - cmd += "vpn-instance %s " % self.vpn_name - if self.is_public_net == "true": - cmd += "public-net " - if self.security_model in ["v1", "v2c"] and self.security_name: - cmd += "params securityname %s %s " % ( - "******", self.security_model) - if self.security_model == "v3" and self.security_name_v3: - cmd += "params securityname %s %s " % ( - self.security_name_v3, self.security_model) - if self.security_level and self.security_level in ["authentication", "privacy"]: - cmd += "%s" % self.security_level - - self.changed = True - self.updates_cmd.append(cmd) - - def delete_snmp_target_host(self): - """ Delete snmp target host operation """ - - conf_str = CE_DELETE_SNMP_TARGET_HOST_HEADER % self.host_name - - if self.domain: - conf_str += "%s" % self.domain - if self.address: - conf_str += "
%s
" % self.address - if self.notify_type: - conf_str += "%s" % self.notify_type - if self.vpn_name: - conf_str += "%s" % self.vpn_name - if self.recv_port: - conf_str += "%s" % self.recv_port - if self.security_model: - conf_str += "%s" % self.security_model - if self.security_name: - conf_str += "%s" % self.security_name - if self.security_name_v3: - conf_str += "%s" % self.security_name_v3 - if self.security_level: - conf_str += "%s" % self.security_level - if self.is_public_net != 'no_use': - conf_str += "%s" % self.is_public_net - if self.interface_name: - conf_str += "%s" % self.interface_name - - conf_str += CE_DELETE_SNMP_TARGET_HOST_TAIL - - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Delete snmp target host failed.') - - if not self.address: - cmd = "undo snmp-agent target-host host-name %s " % self.host_name - else: - if self.notify_type == "trap": - cmd = "undo snmp-agent target-host trap address udp-domain %s " % self.address - else: - cmd = "undo snmp-agent target-host inform address udp-domain %s " % self.address - if self.recv_port: - cmd += "udp-port %s " % self.recv_port - if self.interface_name: - cmd += "source %s " % self.interface_name - if self.vpn_name: - cmd += "vpn-instance %s " % self.vpn_name - if self.is_public_net == "true": - cmd += "public-net " - if self.security_model in ["v1", "v2c"] and self.security_name: - cmd += "params securityname %s" % "******" - if self.security_model == "v3" and self.security_name_v3: - cmd += "params securityname %s" % self.security_name_v3 - - self.changed = True - self.updates_cmd.append(cmd) - - def merge_snmp_version(self): - """ Merge snmp version operation """ - - conf_str = CE_MERGE_SNMP_VERSION % self.version - recv_xml = self.netconf_set_config(conf_str=conf_str) - - if "" not in recv_xml: - self.module.fail_json(msg='Error: Merge snmp version failed.') - - if self.version == "none": - cmd = "snmp-agent sys-info version %s disable" % self.cur_cli_cfg[ - "version"] - self.updates_cmd.append(cmd) - elif self.version == "v1v2c": - cmd = "snmp-agent sys-info version v1" - self.updates_cmd.append(cmd) - cmd = "snmp-agent sys-info version v2c" - self.updates_cmd.append(cmd) - elif self.version == "v1v3": - cmd = "snmp-agent sys-info version v1" - self.updates_cmd.append(cmd) - cmd = "snmp-agent sys-info version v3" - self.updates_cmd.append(cmd) - elif self.version == "v2cv3": - cmd = "snmp-agent sys-info version v2c" - self.updates_cmd.append(cmd) - cmd = "snmp-agent sys-info version v3" - self.updates_cmd.append(cmd) - else: - cmd = "snmp-agent sys-info version %s" % self.version - self.updates_cmd.append(cmd) - - self.changed = True - - def work(self): - """ Main work function """ - - self.check_cli_args() - self.check_netconf_args(self.cur_netconf_cfg) - self.get_proposed() - self.get_existing() - - if self.state == "present": - if self.version: - if self.version != self.cur_cli_cfg["version"]: - self.merge_snmp_version() - if self.connect_port: - self.config_connect_port_xml() - if self.cur_netconf_cfg["need_cfg"]: - self.merge_snmp_target_host() - - else: - if self.connect_port: - self.undo_config_connect_port_cli() - if self.cur_netconf_cfg["need_cfg"]: - self.delete_snmp_target_host() - - self.check_netconf_args(self.end_netconf_cfg) - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - version=dict(choices=['none', 'v1', 'v2c', 'v3', - 'v1v2c', 'v1v3', 'v2cv3', 'all']), - connect_port=dict(type='str'), - host_name=dict(type='str'), - address=dict(type='str'), - notify_type=dict(choices=['trap', 'inform']), - vpn_name=dict(type='str'), - recv_port=dict(type='str'), - security_model=dict(choices=['v1', 'v2c', 'v3']), - security_name=dict(type='str', no_log=True), - security_name_v3=dict(type='str'), - security_level=dict( - choices=['noAuthNoPriv', 'authentication', 'privacy']), - is_public_net=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']), - interface_name=dict(type='str') - ) - - argument_spec.update(ce_argument_spec) - module = SnmpTargetHost(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_snmp_traps.py b/plugins/modules/network/cloudengine/ce_snmp_traps.py deleted file mode 100644 index ec40b8f00c..0000000000 --- a/plugins/modules/network/cloudengine/ce_snmp_traps.py +++ /dev/null @@ -1,563 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_snmp_traps -short_description: Manages SNMP traps configuration on HUAWEI CloudEngine switches. -description: - - Manages SNMP traps configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - feature_name: - description: - - Alarm feature name. - choices: ['aaa', 'arp', 'bfd', 'bgp', 'cfg', 'configuration', 'dad', 'devm', - 'dhcpsnp', 'dldp', 'driver', 'efm', 'erps', 'error-down', 'fcoe', - 'fei', 'fei_comm', 'fm', 'ifnet', 'info', 'ipsg', 'ipv6', 'isis', - 'l3vpn', 'lacp', 'lcs', 'ldm', 'ldp', 'ldt', 'lldp', 'mpls_lspm', - 'msdp', 'mstp', 'nd', 'netconf', 'nqa', 'nvo3', 'openflow', 'ospf', - 'ospfv3', 'pim', 'pim-std', 'qos', 'radius', 'rm', 'rmon', 'securitytrap', - 'smlktrap', 'snmp', 'ssh', 'stackmng', 'sysclock', 'sysom', 'system', - 'tcp', 'telnet', 'trill', 'trunk', 'tty', 'vbst', 'vfs', 'virtual-perception', - 'vrrp', 'vstm', 'all'] - trap_name: - description: - - Alarm trap name. - interface_type: - description: - - Interface type. - choices: ['Ethernet', 'Eth-Trunk', 'Tunnel', 'NULL', 'LoopBack', 'Vlanif', '100GE', - '40GE', 'MTunnel', '10GE', 'GE', 'MEth', 'Vbdif', 'Nve'] - interface_number: - description: - - Interface number. - port_number: - description: - - Source port number. -''' - -EXAMPLES = ''' - -- name: CloudEngine snmp traps test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config SNMP trap all enable" - ce_snmp_traps: - state: present - feature_name: all - provider: "{{ cli }}" - - - name: "Config SNMP trap interface" - ce_snmp_traps: - state: present - interface_type: 40GE - interface_number: 2/0/1 - provider: "{{ cli }}" - - - name: "Config SNMP trap port" - ce_snmp_traps: - state: present - port_number: 2222 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"feature_name": "all", - "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"snmp-agent trap": [], - "undo snmp-agent trap": []} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"snmp-agent trap": ["enable"], - "undo snmp-agent trap": []} -updates: - description: command sent to the device - returned: always - type: list - sample: ["snmp-agent trap enable"] -''' - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import load_config, ce_argument_spec, run_commands -from ansible.module_utils.connection import exec_command - - -class SnmpTraps(object): - """ Manages SNMP trap configuration """ - - def __init__(self, **kwargs): - """ Class init """ - - # module - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule( - argument_spec=self.spec, - required_together=[("interface_type", "interface_number")], - supports_check_mode=True - ) - - # config - self.cur_cfg = dict() - self.cur_cfg["snmp-agent trap"] = [] - self.cur_cfg["undo snmp-agent trap"] = [] - - # module args - self.state = self.module.params['state'] - self.feature_name = self.module.params['feature_name'] - self.trap_name = self.module.params['trap_name'] - self.interface_type = self.module.params['interface_type'] - self.interface_number = self.module.params['interface_number'] - self.port_number = self.module.params['port_number'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.existing["snmp-agent trap"] = [] - self.existing["undo snmp-agent trap"] = [] - self.end_state = dict() - self.end_state["snmp-agent trap"] = [] - self.end_state["undo snmp-agent trap"] = [] - - commands = list() - cmd1 = 'display interface brief' - commands.append(cmd1) - self.interface = run_commands(self.module, commands) - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def check_args(self): - """ Check invalid args """ - - if self.port_number: - if self.port_number.isdigit(): - if int(self.port_number) < 1025 or int(self.port_number) > 65535: - self.module.fail_json( - msg='Error: The value of port_number is out of [1025 - 65535].') - else: - self.module.fail_json( - msg='Error: The port_number is not digit.') - - if self.interface_type and self.interface_number: - tmp_interface = self.interface_type + self.interface_number - if tmp_interface not in self.interface[0]: - self.module.fail_json( - msg='Error: The interface %s is not in the device.' % tmp_interface) - - def get_proposed(self): - """ Get proposed state """ - - self.proposed["state"] = self.state - - if self.feature_name: - self.proposed["feature_name"] = self.feature_name - - if self.trap_name: - self.proposed["trap_name"] = self.trap_name - - if self.interface_type: - self.proposed["interface_type"] = self.interface_type - - if self.interface_number: - self.proposed["interface_number"] = self.interface_number - - if self.port_number: - self.proposed["port_number"] = self.port_number - - def get_existing(self): - """ Get existing state """ - - tmp_cfg = self.cli_get_config() - if tmp_cfg: - temp_cfg_lower = tmp_cfg.lower() - temp_data = tmp_cfg.split("\n") - temp_data_lower = temp_cfg_lower.split("\n") - - for item in temp_data: - if "snmp-agent trap source-port " in item: - if self.port_number: - item_tmp = item.split("snmp-agent trap source-port ") - self.cur_cfg["trap source-port"] = item_tmp[1] - self.existing["trap source-port"] = item_tmp[1] - elif "snmp-agent trap source " in item: - if self.interface_type: - item_tmp = item.split("snmp-agent trap source ") - self.cur_cfg["trap source interface"] = item_tmp[1] - self.existing["trap source interface"] = item_tmp[1] - - if self.feature_name: - for item in temp_data_lower: - if item == "snmp-agent trap enable": - self.cur_cfg["snmp-agent trap"].append("enable") - self.existing["snmp-agent trap"].append("enable") - elif item == "snmp-agent trap disable": - self.cur_cfg["snmp-agent trap"].append("disable") - self.existing["snmp-agent trap"].append("disable") - elif "undo snmp-agent trap enable " in item: - item_tmp = item.split("undo snmp-agent trap enable ") - self.cur_cfg[ - "undo snmp-agent trap"].append(item_tmp[1]) - self.existing[ - "undo snmp-agent trap"].append(item_tmp[1]) - elif "snmp-agent trap enable " in item: - item_tmp = item.split("snmp-agent trap enable ") - self.cur_cfg["snmp-agent trap"].append(item_tmp[1]) - self.existing["snmp-agent trap"].append(item_tmp[1]) - else: - del self.existing["snmp-agent trap"] - del self.existing["undo snmp-agent trap"] - - def get_end_state(self): - """ Get end_state state """ - - tmp_cfg = self.cli_get_config() - if tmp_cfg: - temp_cfg_lower = tmp_cfg.lower() - temp_data = tmp_cfg.split("\n") - temp_data_lower = temp_cfg_lower.split("\n") - - for item in temp_data: - if "snmp-agent trap source-port " in item: - if self.port_number: - item_tmp = item.split("snmp-agent trap source-port ") - self.end_state["trap source-port"] = item_tmp[1] - elif "snmp-agent trap source " in item: - if self.interface_type: - item_tmp = item.split("snmp-agent trap source ") - self.end_state["trap source interface"] = item_tmp[1] - - if self.feature_name: - for item in temp_data_lower: - if item == "snmp-agent trap enable": - self.end_state["snmp-agent trap"].append("enable") - elif item == "snmp-agent trap disable": - self.end_state["snmp-agent trap"].append("disable") - elif "undo snmp-agent trap enable " in item: - item_tmp = item.split("undo snmp-agent trap enable ") - self.end_state[ - "undo snmp-agent trap"].append(item_tmp[1]) - elif "snmp-agent trap enable " in item: - item_tmp = item.split("snmp-agent trap enable ") - self.end_state["snmp-agent trap"].append(item_tmp[1]) - else: - del self.end_state["snmp-agent trap"] - del self.end_state["undo snmp-agent trap"] - if self.end_state == self.existing: - self.changed = False - self.updates_cmd = list() - - def cli_load_config(self, commands): - """ Load configure through cli """ - - if not self.module.check_mode: - load_config(self.module, commands) - - def cli_get_config(self): - """ Get configure through cli """ - - regular = "| include snmp | include trap" - flags = list() - flags.append(regular) - tmp_cfg = self.get_config(flags) - - return tmp_cfg - - def set_trap_feature_name(self): - """ Set feature name for trap """ - - if self.feature_name == "all": - cmd = "snmp-agent trap enable" - else: - cmd = "snmp-agent trap enable feature-name %s" % self.feature_name - if self.trap_name: - cmd += " trap-name %s" % self.trap_name - - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def undo_trap_feature_name(self): - """ Undo feature name for trap """ - - if self.feature_name == "all": - cmd = "undo snmp-agent trap enable" - else: - cmd = "undo snmp-agent trap enable feature-name %s" % self.feature_name - if self.trap_name: - cmd += " trap-name %s" % self.trap_name - - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def set_trap_source_interface(self): - """ Set source interface for trap """ - - cmd = "snmp-agent trap source %s %s" % ( - self.interface_type, self.interface_number) - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def undo_trap_source_interface(self): - """ Undo source interface for trap """ - - cmd = "undo snmp-agent trap source" - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def set_trap_source_port(self): - """ Set source port for trap """ - - cmd = "snmp-agent trap source-port %s" % self.port_number - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def undo_trap_source_port(self): - """ Undo source port for trap """ - - cmd = "undo snmp-agent trap source-port" - self.updates_cmd.append(cmd) - - cmds = list() - cmds.append(cmd) - - self.cli_load_config(cmds) - self.changed = True - - def work(self): - """ The work function """ - - self.check_args() - self.get_proposed() - self.get_existing() - - find_flag = False - find_undo_flag = False - tmp_interface = None - - if self.state == "present": - if self.feature_name: - if self.trap_name: - tmp_cfg = "feature-name %s trap-name %s" % ( - self.feature_name, self.trap_name.lower()) - else: - tmp_cfg = "feature-name %s" % self.feature_name - - find_undo_flag = False - if self.cur_cfg["undo snmp-agent trap"]: - for item in self.cur_cfg["undo snmp-agent trap"]: - if item == tmp_cfg: - find_undo_flag = True - elif tmp_cfg in item: - find_undo_flag = True - elif self.feature_name == "all": - find_undo_flag = True - if find_undo_flag: - self.set_trap_feature_name() - - if not find_undo_flag: - find_flag = False - if self.cur_cfg["snmp-agent trap"]: - for item in self.cur_cfg["snmp-agent trap"]: - if item == "enable": - find_flag = True - elif item == tmp_cfg: - find_flag = True - if not find_flag: - self.set_trap_feature_name() - - if self.interface_type: - find_flag = False - tmp_interface = self.interface_type + self.interface_number - - if "trap source interface" in self.cur_cfg.keys(): - if self.cur_cfg["trap source interface"] == tmp_interface: - find_flag = True - - if not find_flag: - self.set_trap_source_interface() - - if self.port_number: - find_flag = False - - if "trap source-port" in self.cur_cfg.keys(): - if self.cur_cfg["trap source-port"] == self.port_number: - find_flag = True - - if not find_flag: - self.set_trap_source_port() - - else: - if self.feature_name: - if self.trap_name: - tmp_cfg = "feature-name %s trap-name %s" % ( - self.feature_name, self.trap_name.lower()) - else: - tmp_cfg = "feature-name %s" % self.feature_name - - find_flag = False - if self.cur_cfg["snmp-agent trap"]: - for item in self.cur_cfg["snmp-agent trap"]: - if item == tmp_cfg: - find_flag = True - elif item == "enable": - find_flag = True - elif tmp_cfg in item: - find_flag = True - else: - find_flag = True - - find_undo_flag = False - if self.cur_cfg["undo snmp-agent trap"]: - for item in self.cur_cfg["undo snmp-agent trap"]: - if item == tmp_cfg: - find_undo_flag = True - elif tmp_cfg in item: - find_undo_flag = True - - if find_undo_flag: - pass - elif find_flag: - self.undo_trap_feature_name() - - if self.interface_type: - if "trap source interface" in self.cur_cfg.keys(): - self.undo_trap_source_interface() - - if self.port_number: - if "trap source-port" in self.cur_cfg.keys(): - self.undo_trap_source_port() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - feature_name=dict(choices=['aaa', 'arp', 'bfd', 'bgp', 'cfg', 'configuration', 'dad', - 'devm', 'dhcpsnp', 'dldp', 'driver', 'efm', 'erps', 'error-down', - 'fcoe', 'fei', 'fei_comm', 'fm', 'ifnet', 'info', 'ipsg', 'ipv6', - 'isis', 'l3vpn', 'lacp', 'lcs', 'ldm', 'ldp', 'ldt', 'lldp', - 'mpls_lspm', 'msdp', 'mstp', 'nd', 'netconf', 'nqa', 'nvo3', - 'openflow', 'ospf', 'ospfv3', 'pim', 'pim-std', 'qos', 'radius', - 'rm', 'rmon', 'securitytrap', 'smlktrap', 'snmp', 'ssh', 'stackmng', - 'sysclock', 'sysom', 'system', 'tcp', 'telnet', 'trill', 'trunk', - 'tty', 'vbst', 'vfs', 'virtual-perception', 'vrrp', 'vstm', 'all']), - trap_name=dict(type='str'), - interface_type=dict(choices=['Ethernet', 'Eth-Trunk', 'Tunnel', 'NULL', 'LoopBack', 'Vlanif', - '100GE', '40GE', 'MTunnel', '10GE', 'GE', 'MEth', 'Vbdif', 'Nve']), - interface_number=dict(type='str'), - port_number=dict(type='str') - ) - - argument_spec.update(ce_argument_spec) - module = SnmpTraps(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_snmp_user.py b/plugins/modules/network/cloudengine/ce_snmp_user.py deleted file mode 100644 index 5358b8926f..0000000000 --- a/plugins/modules/network/cloudengine/ce_snmp_user.py +++ /dev/null @@ -1,1048 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_snmp_user -short_description: Manages SNMP user configuration on HUAWEI CloudEngine switches. -description: - - Manages SNMP user configurations on CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - acl_number: - description: - - Access control list number. - usm_user_name: - description: - - Unique name to identify the USM user. - aaa_local_user: - description: - - Unique name to identify the local user. - remote_engine_id: - description: - - Remote engine id of the USM user. - user_group: - description: - - Name of the group where user belongs to. - auth_protocol: - description: - - Authentication protocol. - choices: ['noAuth', 'md5', 'sha'] - auth_key: - description: - - The authentication password. Password length, 8-255 characters. - priv_protocol: - description: - - Encryption protocol. - choices: ['noPriv', 'des56', '3des168', 'aes128', 'aes192', 'aes256'] - priv_key: - description: - - The encryption password. Password length 8-255 characters. -''' - -EXAMPLES = ''' - -- name: CloudEngine snmp user test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config SNMP usm user" - ce_snmp_user: - state: present - usm_user_name: wdz_snmp - remote_engine_id: 800007DB03389222111200 - acl_number: 2000 - user_group: wdz_group - provider: "{{ cli }}" - - - name: "Undo SNMP usm user" - ce_snmp_user: - state: absent - usm_user_name: wdz_snmp - remote_engine_id: 800007DB03389222111200 - acl_number: 2000 - user_group: wdz_group - provider: "{{ cli }}" - - - name: "Config SNMP local user" - ce_snmp_user: - state: present - aaa_local_user: wdz_user - auth_protocol: md5 - auth_key: huawei123 - priv_protocol: des56 - priv_key: huawei123 - provider: "{{ cli }}" - - - name: "Config SNMP local user" - ce_snmp_user: - state: absent - aaa_local_user: wdz_user - auth_protocol: md5 - auth_key: huawei123 - priv_protocol: des56 - priv_key: huawei123 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"acl_number": "2000", "remote_engine_id": "800007DB03389222111200", - "state": "present", "user_group": "wdz_group", - "usm_user_name": "wdz_snmp"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"snmp local user": {"local_user_info": []}, - "snmp usm user": {"usm_user_info": []}} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"snmp local user": {"local_user_info": []}, - "snmp usm user": {"usm_user_info": [{"aclNumber": "2000", "engineID": "800007DB03389222111200", - "groupName": "wdz_group", "userName": "wdz_snmp"}]}} -updates: - description: command sent to the device - returned: always - type: list - sample: ["snmp-agent remote-engineid 800007DB03389222111200 usm-user v3 wdz_snmp wdz_group acl 2000"] -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec - -# get snmp v3 USM user -CE_GET_SNMP_V3_USM_USER_HEADER = """ - - - - - - - -""" -CE_GET_SNMP_V3_USM_USER_TAIL = """ - - - - -""" -# merge snmp v3 USM user -CE_MERGE_SNMP_V3_USM_USER_HEADER = """ - - - - - %s - %s - %s -""" -CE_MERGE_SNMP_V3_USM_USER_TAIL = """ - - - - -""" -# create snmp v3 USM user -CE_CREATE_SNMP_V3_USM_USER_HEADER = """ - - - - - %s - %s - %s -""" -CE_CREATE_SNMP_V3_USM_USER_TAIL = """ - - - - -""" -# delete snmp v3 USM user -CE_DELETE_SNMP_V3_USM_USER_HEADER = """ - - - - - %s - %s - %s -""" -CE_DELETE_SNMP_V3_USM_USER_TAIL = """ - - - - -""" - -# get snmp v3 aaa local user -CE_GET_SNMP_V3_LOCAL_USER = """ - - - - - - - - - - - - - -""" -# merge snmp v3 aaa local user -CE_MERGE_SNMP_V3_LOCAL_USER = """ - - - - - %s - %s - %s - %s - %s - - - - -""" -# create snmp v3 aaa local user -CE_CREATE_SNMP_V3_LOCAL_USER = """ - - - - - %s - %s - %s - %s - %s - - - - -""" -# delete snmp v3 aaa local user -CE_DELETE_SNMP_V3_LOCAL_USER = """ - - - - - %s - %s - %s - %s - %s - - - - -""" -# display info -GET_SNMP_LOCAL_ENGINE = """ - - - - - - - -""" - - -class SnmpUser(object): - """ Manages SNMP user configuration """ - - def netconf_get_config(self, **kwargs): - """ Get configure by netconf """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = get_nc_config(module, conf_str) - - return xml_str - - def netconf_set_config(self, **kwargs): - """ Set configure by netconf """ - - module = kwargs["module"] - conf_str = kwargs["conf_str"] - - xml_str = set_nc_config(module, conf_str) - - return xml_str - - def check_snmp_v3_usm_user_args(self, **kwargs): - """ Check snmp v3 usm user invalid args """ - - module = kwargs["module"] - result = dict() - need_cfg = False - state = module.params['state'] - usm_user_name = module.params['usm_user_name'] - remote_engine_id = module.params['remote_engine_id'] - - acl_number = module.params['acl_number'] - user_group = module.params['user_group'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - - local_user_name = module.params['aaa_local_user'] - - if usm_user_name: - if len(usm_user_name) > 32 or len(usm_user_name) == 0: - module.fail_json( - msg='Error: The length of usm_user_name %s is out of [1 - 32].' % usm_user_name) - if remote_engine_id: - if len(remote_engine_id) > 64 or len(remote_engine_id) < 10: - module.fail_json( - msg='Error: The length of remote_engine_id %s is out of [10 - 64].' % remote_engine_id) - - conf_str = CE_GET_SNMP_V3_USM_USER_HEADER - - if acl_number: - if acl_number.isdigit(): - if int(acl_number) > 2999 or int(acl_number) < 2000: - module.fail_json( - msg='Error: The value of acl_number %s is out of [2000 - 2999].' % acl_number) - else: - if not acl_number[0].isalpha() or len(acl_number) > 32 or len(acl_number) < 1: - module.fail_json( - msg='Error: The length of acl_number %s is out of [1 - 32].' % acl_number) - - conf_str += "" - - if user_group: - if len(user_group) > 32 or len(user_group) == 0: - module.fail_json( - msg='Error: The length of user_group %s is out of [1 - 32].' % user_group) - - conf_str += "" - - if auth_protocol: - conf_str += "" - - if auth_key: - if len(auth_key) > 255 or len(auth_key) == 0: - module.fail_json( - msg='Error: The length of auth_key %s is out of [1 - 255].' % auth_key) - - conf_str += "" - - if priv_protocol: - if not auth_protocol: - module.fail_json( - msg='Error: Please input auth_protocol at the same time.') - - conf_str += "" - - if priv_key: - if len(priv_key) > 255 or len(priv_key) == 0: - module.fail_json( - msg='Error: The length of priv_key %s is out of [1 - 255].' % priv_key) - conf_str += "" - - result["usm_user_info"] = [] - - conf_str += CE_GET_SNMP_V3_USM_USER_TAIL - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - usm_user_info = root.findall("snmp/usmUsers/usmUser") - if usm_user_info: - for tmp in usm_user_info: - tmp_dict = dict() - tmp_dict["remoteEngineID"] = None - for site in tmp: - if site.tag in ["userName", "remoteEngineID", "engineID", "groupName", "authProtocol", - "authKey", "privProtocol", "privKey", "aclNumber"]: - tmp_dict[site.tag] = site.text - - result["usm_user_info"].append(tmp_dict) - - cur_cfg = dict() - if usm_user_name: - cur_cfg["userName"] = usm_user_name - if user_group: - cur_cfg["groupName"] = user_group - if auth_protocol: - cur_cfg["authProtocol"] = auth_protocol - if auth_key: - cur_cfg["authKey"] = auth_key - if priv_protocol: - cur_cfg["privProtocol"] = priv_protocol - if priv_key: - cur_cfg["privKey"] = priv_key - if acl_number: - cur_cfg["aclNumber"] = acl_number - - if remote_engine_id: - cur_cfg["engineID"] = remote_engine_id - cur_cfg["remoteEngineID"] = "true" - else: - cur_cfg["engineID"] = self.local_engine_id - cur_cfg["remoteEngineID"] = "false" - - if result["usm_user_info"]: - num = 0 - for tmp in result["usm_user_info"]: - if cur_cfg == tmp: - num += 1 - - if num == 0: - if state == "present": - need_cfg = True - else: - need_cfg = False - else: - if state == "present": - need_cfg = False - else: - need_cfg = True - - else: - if state == "present": - need_cfg = True - else: - need_cfg = False - - result["need_cfg"] = need_cfg - return result - - def check_snmp_v3_local_user_args(self, **kwargs): - """ Check snmp v3 local user invalid args """ - - module = kwargs["module"] - result = dict() - - need_cfg = False - state = module.params['state'] - local_user_name = module.params['aaa_local_user'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - - usm_user_name = module.params['usm_user_name'] - - if local_user_name: - - if usm_user_name: - module.fail_json( - msg='Error: Please do not input usm_user_name and local_user_name at the same time.') - - if not auth_protocol or not auth_key or not priv_protocol or not priv_key: - module.fail_json( - msg='Error: Please input auth_protocol auth_key priv_protocol priv_key for local user.') - - if len(local_user_name) > 32 or len(local_user_name) == 0: - module.fail_json( - msg='Error: The length of local_user_name %s is out of [1 - 32].' % local_user_name) - - if len(auth_key) > 255 or len(auth_key) == 0: - module.fail_json( - msg='Error: The length of auth_key %s is out of [1 - 255].' % auth_key) - - if len(priv_key) > 255 or len(priv_key) == 0: - module.fail_json( - msg='Error: The length of priv_key %s is out of [1 - 255].' % priv_key) - - result["local_user_info"] = [] - - conf_str = CE_GET_SNMP_V3_LOCAL_USER - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - - if "" in recv_xml: - if state == "present": - need_cfg = True - - else: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - local_user_info = root.findall( - "snmp/localUsers/localUser") - if local_user_info: - for tmp in local_user_info: - tmp_dict = dict() - for site in tmp: - if site.tag in ["userName", "authProtocol", "authKey", "privProtocol", "privKey"]: - tmp_dict[site.tag] = site.text - - result["local_user_info"].append(tmp_dict) - - if result["local_user_info"]: - for tmp in result["local_user_info"]: - if "userName" in tmp.keys(): - if state == "present": - if tmp["userName"] != local_user_name: - need_cfg = True - else: - if tmp["userName"] == local_user_name: - need_cfg = True - if auth_protocol: - if "authProtocol" in tmp.keys(): - if state == "present": - if tmp["authProtocol"] != auth_protocol: - need_cfg = True - else: - if tmp["authProtocol"] == auth_protocol: - need_cfg = True - if auth_key: - if "authKey" in tmp.keys(): - if state == "present": - if tmp["authKey"] != auth_key: - need_cfg = True - else: - if tmp["authKey"] == auth_key: - need_cfg = True - if priv_protocol: - if "privProtocol" in tmp.keys(): - if state == "present": - if tmp["privProtocol"] != priv_protocol: - need_cfg = True - else: - if tmp["privProtocol"] == priv_protocol: - need_cfg = True - if priv_key: - if "privKey" in tmp.keys(): - if state == "present": - if tmp["privKey"] != priv_key: - need_cfg = True - else: - if tmp["privKey"] == priv_key: - need_cfg = True - - result["need_cfg"] = need_cfg - return result - - def merge_snmp_v3_usm_user(self, **kwargs): - """ Merge snmp v3 usm user operation """ - - module = kwargs["module"] - usm_user_name = module.params['usm_user_name'] - remote_engine_id = module.params['remote_engine_id'] - acl_number = module.params['acl_number'] - user_group = module.params['user_group'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - - cmds = [] - - if remote_engine_id: - conf_str = CE_MERGE_SNMP_V3_USM_USER_HEADER % ( - usm_user_name, "true", remote_engine_id) - cmd = "snmp-agent remote-engineid %s usm-user v3 %s" % ( - remote_engine_id, usm_user_name) - else: - if not self.local_engine_id: - module.fail_json( - msg='Error: The local engine id is null, please input remote_engine_id.') - - conf_str = CE_MERGE_SNMP_V3_USM_USER_HEADER % ( - usm_user_name, "false", self.local_engine_id) - cmd = "snmp-agent usm-user v3 %s" % usm_user_name - - if user_group: - conf_str += "%s" % user_group - cmd += " %s" % user_group - - if acl_number: - conf_str += "%s" % acl_number - cmd += " acl %s" % acl_number - - cmds.append(cmd) - - if remote_engine_id: - cmd = "snmp-agent remote-engineid %s usm-user v3 %s" % ( - remote_engine_id, usm_user_name) - else: - cmd = "snmp-agent usm-user v3 %s" % usm_user_name - - if auth_protocol: - conf_str += "%s" % auth_protocol - - if auth_protocol != "noAuth": - cmd += " authentication-mode %s" % auth_protocol - - if auth_key: - conf_str += "%s" % auth_key - - if auth_protocol != "noAuth": - cmd += " cipher %s" % "******" - if auth_protocol or auth_key: - cmds.append(cmd) - - if remote_engine_id: - cmd = "snmp-agent remote-engineid %s usm-user v3 %s" % ( - remote_engine_id, usm_user_name) - else: - cmd = "snmp-agent usm-user v3 %s" % usm_user_name - - if priv_protocol: - conf_str += "%s" % priv_protocol - - if auth_protocol != "noAuth" and priv_protocol != "noPriv": - cmd += " privacy-mode %s" % priv_protocol - - if priv_key: - conf_str += "%s" % priv_key - - if auth_protocol != "noAuth" and priv_protocol != "noPriv": - cmd += " cipher %s" % "******" - if priv_key or priv_protocol: - cmds.append(cmd) - - conf_str += CE_MERGE_SNMP_V3_USM_USER_TAIL - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge snmp v3 usm user failed.') - - return cmds - - def create_snmp_v3_usm_user(self, **kwargs): - """ Create snmp v3 usm user operation """ - - module = kwargs["module"] - usm_user_name = module.params['usm_user_name'] - remote_engine_id = module.params['remote_engine_id'] - acl_number = module.params['acl_number'] - user_group = module.params['user_group'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - - cmds = [] - - if remote_engine_id: - conf_str = CE_CREATE_SNMP_V3_USM_USER_HEADER % ( - usm_user_name, "true", remote_engine_id) - cmd = "snmp-agent remote-engineid %s usm-user v3 %s" % ( - remote_engine_id, usm_user_name) - else: - if not self.local_engine_id: - module.fail_json( - msg='Error: The local engine id is null, please input remote_engine_id.') - - conf_str = CE_CREATE_SNMP_V3_USM_USER_HEADER % ( - usm_user_name, "false", self.local_engine_id) - cmd = "snmp-agent usm-user v3 %s" % usm_user_name - - if user_group: - conf_str += "%s" % user_group - cmd += " %s" % user_group - - if acl_number: - conf_str += "%s" % acl_number - cmd += " acl %s" % acl_number - cmds.append(cmd) - - if remote_engine_id: - cmd = "snmp-agent remote-engineid %s usm-user v3 %s" % ( - remote_engine_id, usm_user_name) - else: - cmd = "snmp-agent usm-user v3 %s" % usm_user_name - - if auth_protocol: - conf_str += "%s" % auth_protocol - - if auth_protocol != "noAuth": - cmd += " authentication-mode %s" % auth_protocol - - if auth_key: - conf_str += "%s" % auth_key - - if auth_protocol != "noAuth": - cmd += " cipher %s" % "******" - - if auth_key or auth_protocol: - cmds.append(cmd) - - if remote_engine_id: - cmd = "snmp-agent remote-engineid %s usm-user v3 %s" % ( - remote_engine_id, usm_user_name) - else: - cmd = "snmp-agent usm-user v3 %s" % usm_user_name - - if priv_protocol: - conf_str += "%s" % priv_protocol - - if auth_protocol != "noAuth" and priv_protocol != "noPriv": - cmd += " privacy-mode %s" % priv_protocol - - if priv_key: - conf_str += "%s" % priv_key - - if auth_protocol != "noAuth" and priv_protocol != "noPriv": - cmd += " cipher %s" % "******" - - if priv_protocol or priv_key: - cmds.append(cmd) - - conf_str += CE_CREATE_SNMP_V3_USM_USER_TAIL - - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create snmp v3 usm user failed.') - - return cmds - - def delete_snmp_v3_usm_user(self, **kwargs): - """ Delete snmp v3 usm user operation """ - - module = kwargs["module"] - usm_user_name = module.params['usm_user_name'] - remote_engine_id = module.params['remote_engine_id'] - acl_number = module.params['acl_number'] - user_group = module.params['user_group'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - - if remote_engine_id: - conf_str = CE_DELETE_SNMP_V3_USM_USER_HEADER % ( - usm_user_name, "true", remote_engine_id) - cmd = "undo snmp-agent remote-engineid %s usm-user v3 %s" % ( - remote_engine_id, usm_user_name) - else: - if not self.local_engine_id: - module.fail_json( - msg='Error: The local engine id is null, please input remote_engine_id.') - - conf_str = CE_DELETE_SNMP_V3_USM_USER_HEADER % ( - usm_user_name, "false", self.local_engine_id) - cmd = "undo snmp-agent usm-user v3 %s" % usm_user_name - - if user_group: - conf_str += "%s" % user_group - - if acl_number: - conf_str += "%s" % acl_number - - if auth_protocol: - conf_str += "%s" % auth_protocol - - if auth_key: - conf_str += "%s" % auth_key - - if priv_protocol: - conf_str += "%s" % priv_protocol - - if priv_key: - conf_str += "%s" % priv_key - - conf_str += CE_DELETE_SNMP_V3_USM_USER_TAIL - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete snmp v3 usm user failed.') - - return cmd - - def merge_snmp_v3_local_user(self, **kwargs): - """ Merge snmp v3 local user operation """ - - module = kwargs["module"] - local_user_name = module.params['aaa_local_user'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - - conf_str = CE_MERGE_SNMP_V3_LOCAL_USER % ( - local_user_name, auth_protocol, auth_key, priv_protocol, priv_key) - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Merge snmp v3 local user failed.') - - cmd = "snmp-agent local-user v3 %s " % local_user_name + "authentication-mode %s " % auth_protocol + \ - "cipher ****** " + "privacy-mode %s " % priv_protocol + "cipher ******" - - return cmd - - def create_snmp_v3_local_user(self, **kwargs): - """ Create snmp v3 local user operation """ - - module = kwargs["module"] - local_user_name = module.params['aaa_local_user'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - - conf_str = CE_CREATE_SNMP_V3_LOCAL_USER % ( - local_user_name, auth_protocol, auth_key, priv_protocol, priv_key) - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Create snmp v3 local user failed.') - - cmd = "snmp-agent local-user v3 %s " % local_user_name + "authentication-mode %s " % auth_protocol + \ - "cipher ****** " + "privacy-mode %s " % priv_protocol + "cipher ******" - - return cmd - - def delete_snmp_v3_local_user(self, **kwargs): - """ Delete snmp v3 local user operation """ - - module = kwargs["module"] - local_user_name = module.params['aaa_local_user'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - - conf_str = CE_DELETE_SNMP_V3_LOCAL_USER % ( - local_user_name, auth_protocol, auth_key, priv_protocol, priv_key) - recv_xml = self.netconf_set_config(module=module, conf_str=conf_str) - - if "" not in recv_xml: - module.fail_json(msg='Error: Delete snmp v3 local user failed.') - - cmd = "undo snmp-agent local-user v3 %s" % local_user_name - - return cmd - - def get_snmp_local_engine(self, **kwargs): - """ Get snmp local engine operation """ - - module = kwargs["module"] - - conf_str = GET_SNMP_LOCAL_ENGINE - recv_xml = self.netconf_get_config(module=module, conf_str=conf_str) - if "" in recv_xml: - xml_str = recv_xml.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - local_engine_info = root.findall("snmp/engine/engineID") - if local_engine_info: - self.local_engine_id = local_engine_info[0].text - - -def main(): - """ Module main function """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - acl_number=dict(type='str'), - usm_user_name=dict(type='str'), - remote_engine_id=dict(type='str'), - user_group=dict(type='str'), - auth_protocol=dict(choices=['noAuth', 'md5', 'sha']), - auth_key=dict(type='str', no_log=True), - priv_protocol=dict( - choices=['noPriv', 'des56', '3des168', 'aes128', 'aes192', 'aes256']), - priv_key=dict(type='str', no_log=True), - aaa_local_user=dict(type='str') - ) - - mutually_exclusive = [("usm_user_name", "local_user_name")] - argument_spec.update(ce_argument_spec) - module = AnsibleModule( - argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True - ) - - changed = False - proposed = dict() - existing = dict() - end_state = dict() - updates = [] - - state = module.params['state'] - acl_number = module.params['acl_number'] - usm_user_name = module.params['usm_user_name'] - remote_engine_id = module.params['remote_engine_id'] - user_group = module.params['user_group'] - auth_protocol = module.params['auth_protocol'] - auth_key = module.params['auth_key'] - priv_protocol = module.params['priv_protocol'] - priv_key = module.params['priv_key'] - aaa_local_user = module.params['aaa_local_user'] - - snmp_user_obj = SnmpUser() - - if not snmp_user_obj: - module.fail_json(msg='Error: Init module failed.') - - # get proposed - proposed["state"] = state - if acl_number: - proposed["acl_number"] = acl_number - if usm_user_name: - proposed["usm_user_name"] = usm_user_name - if remote_engine_id: - proposed["remote_engine_id"] = remote_engine_id - if user_group: - proposed["user_group"] = user_group - if auth_protocol: - proposed["auth_protocol"] = auth_protocol - if auth_key: - proposed["auth_key"] = auth_key - if priv_protocol: - proposed["priv_protocol"] = priv_protocol - if priv_key: - proposed["priv_key"] = priv_key - if aaa_local_user: - proposed["aaa_local_user"] = aaa_local_user - - snmp_user_obj.get_snmp_local_engine(module=module) - snmp_v3_usm_user_rst = snmp_user_obj.check_snmp_v3_usm_user_args( - module=module) - snmp_v3_local_user_rst = snmp_user_obj.check_snmp_v3_local_user_args( - module=module) - - # state exist snmp v3 user config - exist_tmp = dict() - for item in snmp_v3_usm_user_rst: - if item != "need_cfg": - exist_tmp[item] = snmp_v3_usm_user_rst[item] - if exist_tmp: - existing["snmp usm user"] = exist_tmp - - exist_tmp = dict() - for item in snmp_v3_local_user_rst: - if item != "need_cfg": - exist_tmp[item] = snmp_v3_local_user_rst[item] - if exist_tmp: - existing["snmp local user"] = exist_tmp - - if state == "present": - if snmp_v3_usm_user_rst["need_cfg"]: - if len(snmp_v3_usm_user_rst["usm_user_info"]) != 0: - cmd = snmp_user_obj.merge_snmp_v3_usm_user(module=module) - changed = True - updates.append(cmd) - else: - cmd = snmp_user_obj.create_snmp_v3_usm_user(module=module) - changed = True - updates.append(cmd) - - if snmp_v3_local_user_rst["need_cfg"]: - if len(snmp_v3_local_user_rst["local_user_info"]) != 0: - cmd = snmp_user_obj.merge_snmp_v3_local_user( - module=module) - changed = True - updates.append(cmd) - else: - cmd = snmp_user_obj.create_snmp_v3_local_user( - module=module) - changed = True - updates.append(cmd) - - else: - if snmp_v3_usm_user_rst["need_cfg"]: - cmd = snmp_user_obj.delete_snmp_v3_usm_user(module=module) - changed = True - updates.append(cmd) - if snmp_v3_local_user_rst["need_cfg"]: - cmd = snmp_user_obj.delete_snmp_v3_local_user(module=module) - changed = True - updates.append(cmd) - - # state exist snmp v3 user config - snmp_v3_usm_user_rst = snmp_user_obj.check_snmp_v3_usm_user_args( - module=module) - end_tmp = dict() - for item in snmp_v3_usm_user_rst: - if item != "need_cfg": - end_tmp[item] = snmp_v3_usm_user_rst[item] - if end_tmp: - end_state["snmp usm user"] = end_tmp - - snmp_v3_local_user_rst = snmp_user_obj.check_snmp_v3_local_user_args( - module=module) - end_tmp = dict() - for item in snmp_v3_local_user_rst: - if item != "need_cfg": - end_tmp[item] = snmp_v3_local_user_rst[item] - if end_tmp: - end_state["snmp local user"] = end_tmp - - results = dict() - results['proposed'] = proposed - results['existing'] = existing - results['changed'] = changed - results['end_state'] = end_state - results['updates'] = updates - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_startup.py b/plugins/modules/network/cloudengine/ce_startup.py deleted file mode 100644 index 4311b68bce..0000000000 --- a/plugins/modules/network/cloudengine/ce_startup.py +++ /dev/null @@ -1,469 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_startup -short_description: Manages a system startup information on HUAWEI CloudEngine switches. -description: - - Manages a system startup information on HUAWEI CloudEngine switches. -author: - - Li Yanfeng (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - cfg_file: - description: - - Name of the configuration file that is applied for the next startup. - The value is a string of 5 to 255 characters. - default: present - software_file: - description: - - File name of the system software that is applied for the next startup. - The value is a string of 5 to 255 characters. - patch_file: - description: - - Name of the patch file that is applied for the next startup. - slot: - description: - - Position of the device.The value is a string of 1 to 32 characters. - The possible value of slot is all, slave-board, or the specific slotID. - action: - description: - - Display the startup information. - choices: ['display'] - -''' - -EXAMPLES = ''' -- name: startup module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Display startup information - ce_startup: - action: display - provider: "{{ cli }}" - - - name: Set startup patch file - ce_startup: - patch_file: 2.PAT - slot: all - provider: "{{ cli }}" - - - name: Set startup software file - ce_startup: - software_file: aa.cc - slot: 1 - provider: "{{ cli }}" - - - name: Set startup cfg file - ce_startup: - cfg_file: 2.cfg - slot: 1 - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"patch_file": "2.PAT", - "slot": "all"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: { - "configSysSoft": "flash:/CE12800-V200R002C20_issuB071.cc", - "curentPatchFile": "NULL", - "curentStartupFile": "NULL", - "curentSysSoft": "flash:/CE12800-V200R002C20_issuB071.cc", - "nextPatchFile": "flash:/1.PAT", - "nextStartupFile": "flash:/1.cfg", - "nextSysSoft": "flash:/CE12800-V200R002C20_issuB071.cc", - "position": "5" - } -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"StartupInfos": null} -updates: - description: command sent to the device - returned: always - type: list - sample: {"startup patch 2.PAT all"} -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, run_commands -from ansible.module_utils.connection import exec_command - - -class StartUp(object): - """ - Manages system startup information. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.cfg_file = self.module.params['cfg_file'] - self.software_file = self.module.params['software_file'] - self.patch_file = self.module.params['patch_file'] - self.slot = self.module.params['slot'] - self.action = self.module.params['action'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.existing = dict() - self.proposed = dict() - self.end_state = dict() - - # system startup info - self.startup_info = None - - def init_module(self): - """ init module """ - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_startup_dict(self): - """Retrieves the current config from the device or cache - """ - cmd = 'display startup' - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - startup_info = dict() - startup_info["StartupInfos"] = list() - if not cfg: - return startup_info - else: - re_find = re.findall(r'(.*)\s*' - r'\s*Configured\s*startup\s*system\s*software:\s*(.*)' - r'\s*Startup\s*system\s*software:\s*(.*)' - r'\s*Next\s*startup\s*system\s*software:\s*(.*)' - r'\s*Startup\s*saved-configuration\s*file:\s*(.*)' - r'\s*Next\s*startup\s*saved-configuration\s*file:\s*(.*)' - r'\s*Startup\s*paf\s*file:\s*(.*)' - r'\s*Next\s*startup\s*paf\s*file:\s*(.*)' - r'\s*Startup\s*patch\s*package:\s*(.*)' - r'\s*Next\s*startup\s*patch\s*package:\s*(.*)', cfg) - - if re_find: - for mem in re_find: - startup_info["StartupInfos"].append( - dict(nextStartupFile=mem[5], configSysSoft=mem[1], curentSysSoft=mem[2], - nextSysSoft=mem[3], curentStartupFile=mem[4], curentPatchFile=mem[8], - nextPatchFile=mem[9], postion=mem[0])) - return startup_info - return startup_info - - def get_cfg_filename_type(self, filename): - """Gets the type of cfg filename, such as cfg, zip, dat...""" - - if filename is None: - return None - if ' ' in filename: - self.module.fail_json( - msg='Error: Configuration file name include spaces.') - - iftype = None - - if filename.endswith('.cfg'): - iftype = 'cfg' - elif filename.endswith('.zip'): - iftype = 'zip' - elif filename.endswith('.dat'): - iftype = 'dat' - else: - return None - return iftype.lower() - - def get_pat_filename_type(self, filename): - """Gets the type of patch filename, such as cfg, zip, dat...""" - - if filename is None: - return None - if ' ' in filename: - self.module.fail_json( - msg='Error: Patch file name include spaces.') - - iftype = None - - if filename.endswith('.PAT'): - iftype = 'PAT' - else: - return None - return iftype.upper() - - def get_software_filename_type(self, filename): - """Gets the type of software filename, such as cfg, zip, dat...""" - - if filename is None: - return None - if ' ' in filename: - self.module.fail_json( - msg='Error: Software file name include spaces.') - - iftype = None - - if filename.endswith('.cc'): - iftype = 'cc' - else: - return None - return iftype.lower() - - def startup_next_cfg_file(self): - """set next cfg file""" - commands = list() - cmd = {'output': None, 'command': ''} - if self.slot: - cmd['command'] = "startup saved-configuration %s slot %s" % ( - self.cfg_file, self.slot) - commands.append(cmd) - self.updates_cmd.append( - "startup saved-configuration %s slot %s" % (self.cfg_file, self.slot)) - run_commands(self.module, commands) - self.changed = True - else: - cmd['command'] = "startup saved-configuration %s" % self.cfg_file - commands.append(cmd) - self.updates_cmd.append( - "startup saved-configuration %s" % self.cfg_file) - run_commands(self.module, commands) - self.changed = True - - def startup_next_software_file(self): - """set next software file""" - commands = list() - cmd = {'output': None, 'command': ''} - if self.slot: - if self.slot == "all" or self.slot == "slave-board": - cmd['command'] = "startup system-software %s %s" % ( - self.software_file, self.slot) - commands.append(cmd) - self.updates_cmd.append( - "startup system-software %s %s" % (self.software_file, self.slot)) - run_commands(self.module, commands) - self.changed = True - else: - cmd['command'] = "startup system-software %s slot %s" % ( - self.software_file, self.slot) - commands.append(cmd) - self.updates_cmd.append( - "startup system-software %s slot %s" % (self.software_file, self.slot)) - run_commands(self.module, commands) - self.changed = True - - if not self.slot: - cmd['command'] = "startup system-software %s" % self.software_file - commands.append(cmd) - self.updates_cmd.append( - "startup system-software %s" % self.software_file) - run_commands(self.module, commands) - self.changed = True - - def startup_next_pat_file(self): - """set next patch file""" - - commands = list() - cmd = {'output': None, 'command': ''} - if self.slot: - if self.slot == "all": - cmd['command'] = "startup patch %s %s" % ( - self.patch_file, self.slot) - commands.append(cmd) - self.updates_cmd.append( - "startup patch %s %s" % (self.patch_file, self.slot)) - run_commands(self.module, commands) - self.changed = True - else: - cmd['command'] = "startup patch %s slot %s" % ( - self.patch_file, self.slot) - commands.append(cmd) - self.updates_cmd.append( - "startup patch %s slot %s" % (self.patch_file, self.slot)) - run_commands(self.module, commands) - self.changed = True - - if not self.slot: - cmd['command'] = "startup patch %s" % self.patch_file - commands.append(cmd) - self.updates_cmd.append( - "startup patch %s" % self.patch_file) - run_commands(self.module, commands) - self.changed = True - - def check_params(self): - """Check all input params""" - - # cfg_file check - if self.cfg_file: - if not self.get_cfg_filename_type(self.cfg_file): - self.module.fail_json( - msg='Error: Invalid cfg file name or cfg file name extension ( *.cfg, *.zip, *.dat ).') - - # software_file check - if self.software_file: - if not self.get_software_filename_type(self.software_file): - self.module.fail_json( - msg='Error: Invalid software file name or software file name extension ( *.cc).') - - # patch_file check - if self.patch_file: - if not self.get_pat_filename_type(self.patch_file): - self.module.fail_json( - msg='Error: Invalid patch file name or patch file name extension ( *.PAT ).') - - # slot check - if self.slot: - if self.slot.isdigit(): - if int(self.slot) <= 0 or int(self.slot) > 16: - self.module.fail_json( - msg='Error: The number of slot is not in the range from 1 to 16.') - else: - if len(self.slot) <= 0 or len(self.slot) > 32: - self.module.fail_json( - msg='Error: The length of slot is not in the range from 1 to 32.') - - def get_proposed(self): - """get proposed info""" - - if self.cfg_file: - self.proposed["cfg_file"] = self.cfg_file - if self.software_file: - self.proposed["system_file"] = self.software_file - if self.patch_file: - self.proposed["patch_file"] = self.patch_file - if self.slot: - self.proposed["slot"] = self.slot - - def get_existing(self): - """get existing info""" - - if not self.startup_info: - self.existing["StartupInfos"] = None - else: - self.existing["StartupInfos"] = self.startup_info["StartupInfos"] - - def get_end_state(self): - """get end state info""" - if not self.startup_info: - self.end_state["StartupInfos"] = None - else: - self.end_state["StartupInfos"] = self.startup_info["StartupInfos"] - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.get_proposed() - - self.startup_info = self.get_startup_dict() - self.get_existing() - - startup_info = self.startup_info["StartupInfos"][0] - if self.cfg_file: - if self.cfg_file != startup_info["nextStartupFile"]: - self.startup_next_cfg_file() - - if self.software_file: - if self.software_file != startup_info["nextSysSoft"]: - self.startup_next_software_file() - if self.patch_file: - if self.patch_file != startup_info["nextPatchFile"]: - self.startup_next_pat_file() - if self.action == "display": - self.startup_info = self.get_startup_dict() - - self.startup_info = self.get_startup_dict() - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - cfg_file=dict(type='str'), - software_file=dict(type='str'), - patch_file=dict(type='str'), - slot=dict(type='str'), - action=dict(type='str', choices=['display']) - ) - argument_spec.update(ce_argument_spec) - module = StartUp(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_static_route.py b/plugins/modules/network/cloudengine/ce_static_route.py deleted file mode 100644 index 7a8ea88206..0000000000 --- a/plugins/modules/network/cloudengine/ce_static_route.py +++ /dev/null @@ -1,833 +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) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_static_route -short_description: Manages static route configuration on HUAWEI CloudEngine switches. -description: - - Manages the static routes on HUAWEI CloudEngine switches. -author: Yang yang (@QijunPan) -notes: - - If no vrf is supplied, vrf is set to default. - - If I(state=absent), the route will be removed, regardless of the non-required parameters. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - prefix: - description: - - Destination ip address of static route. - required: true - mask: - description: - - Destination ip mask of static route. - required: true - aftype: - description: - - Destination ip address family type of static route. - required: true - choices: ['v4','v6'] - next_hop: - description: - - Next hop address of static route. - nhp_interface: - description: - - Next hop interface full name of static route. - vrf: - description: - - VPN instance of destination ip address. - destvrf: - description: - - VPN instance of next hop ip address. - tag: - description: - - Route tag value (numeric). - description: - description: - - Name of the route. Used with the name parameter on the CLI. - pref: - description: - - Preference or administrative difference of route (range 1-255). - state: - description: - - Specify desired state of the resource. - choices: ['present','absent'] - default: present -''' - -EXAMPLES = ''' -- name: static route module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Config a ipv4 static route, next hop is an address and that it has the proper description - ce_static_route: - prefix: 2.1.1.2 - mask: 24 - next_hop: 3.1.1.2 - description: 'Configured by Ansible' - aftype: v4 - provider: "{{ cli }}" - - name: Config a ipv4 static route ,next hop is an interface and that it has the proper description - ce_static_route: - prefix: 2.1.1.2 - mask: 24 - next_hop: 10GE1/0/1 - description: 'Configured by Ansible' - aftype: v4 - provider: "{{ cli }}" - - name: Config a ipv6 static route, next hop is an address and that it has the proper description - ce_static_route: - prefix: fc00:0:0:2001::1 - mask: 64 - next_hop: fc00:0:0:2004::1 - description: 'Configured by Ansible' - aftype: v6 - provider: "{{ cli }}" - - name: Config a ipv4 static route, next hop is an interface and that it has the proper description - ce_static_route: - prefix: fc00:0:0:2001::1 - mask: 64 - next_hop: 10GE1/0/1 - description: 'Configured by Ansible' - aftype: v6 - provider: "{{ cli }}" - - name: Config a VRF and set ipv4 static route, next hop is an address and that it has the proper description - ce_static_route: - vrf: vpna - prefix: 2.1.1.2 - mask: 24 - next_hop: 3.1.1.2 - description: 'Configured by Ansible' - aftype: v4 - provider: "{{ cli }}" -''' -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"next_hop": "3.3.3.3", "pref": "100", - "prefix": "192.168.20.642", "mask": "24", "description": "testing", - "vrf": "_public_"} -existing: - description: k/v pairs of existing switchport - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of switchport after module execution - returned: always - type: dict - sample: {"next_hop": "3.3.3.3", "pref": "100", - "prefix": "192.168.20.0", "mask": "24", "description": "testing", - "tag" : "null"} -updates: - description: command list sent to the device - returned: always - type: list - sample: ["ip route-static 192.168.20.0 255.255.255.0 3.3.3.3 preference 100 description testing"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_STATIC_ROUTE = """ - - - - - - - - - - - - - - - - - - - - - -""" - -CE_NC_GET_STATIC_ROUTE_ABSENT = """ - - - - - - - - - - - - - - - - - - -""" - -CE_NC_SET_STATIC_ROUTE = """ - - - - - %s - %s - base - %s - %s - %s - %s - %s%s%s%s - - - - -""" -CE_NC_SET_DESCRIPTION = """ -%s -""" - -CE_NC_SET_PREFERENCE = """ -%s -""" - -CE_NC_SET_TAG = """ -%s -""" - -CE_NC_DELETE_STATIC_ROUTE = """ - - - - - %s - %s - base - %s - %s - %s - %s - %s - - - - -""" - - -def build_config_xml(xmlstr): - """build config xml""" - - return ' ' + xmlstr + ' ' - - -def is_valid_v4addr(addr): - """check if ipv4 addr is valid""" - if addr.find('.') != -1: - addr_list = addr.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - return False - - -def is_valid_v6addr(addr): - """check if ipv6 addr is valid""" - if addr.find(':') != -1: - addr_list = addr.split(':') - # The IPv6 binary system has a length of 128 bits and is grouped by 16 bits. - # Each group is separated by a colon ":" and can be divided into 8 groups, each group being represented by 4 hexadecimal - if len(addr_list) > 8: - return False - # You can use a double colon "::" to represent a group of 0 or more consecutive 0s, but only once. - if addr.count('::') > 1: - return False - # if do not use '::', the length of address should not be less than 8. - if addr.count('::') == 0 and len(addr_list) < 8: - return False - for group in addr_list: - if group.strip() == '': - continue - try: - # Each group is represented in 4-digit hexadecimal - int(group, base=16) - except ValueError: - return False - return True - return False - - -def is_valid_tag(tag): - """check if the tag is valid""" - - if not tag.isdigit(): - return False - - if int(tag) < 1 or int(tag) > 4294967295: - return False - - return True - - -def is_valid_preference(pref): - """check if the preference is valid""" - if pref.isdigit(): - return int(pref) > 0 and int(pref) < 256 - else: - return False - - -def is_valid_description(description): - """check if the description is valid""" - if description.find('?') != -1: - return False - if len(description) < 1 or len(description) > 255: - return False - return True - - -class StaticRoute(object): - """static route module""" - - def __init__(self, argument_spec, ): - self.spec = argument_spec - self.module = None - self.init_module() - - # static route info - self.prefix = self.module.params['prefix'] - self.mask = self.module.params['mask'] - self.aftype = self.module.params['aftype'] - self.next_hop = self.module.params['next_hop'] - self.nhp_interface = self.module.params['nhp_interface'] - if self.nhp_interface is None: - self.nhp_interface = "Invalid0" - self.tag = self.module.params['tag'] - self.description = self.module.params['description'] - self.state = self.module.params['state'] - self.pref = self.module.params['pref'] - - # vpn instance info - self.vrf = self.module.params['vrf'] - if self.vrf is None: - self.vrf = "_public_" - self.destvrf = self.module.params['destvrf'] - if self.destvrf is None: - self.destvrf = "_public_" - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - self.static_routes_info = dict() - - def init_module(self): - """init module""" - - required_one_of = [["next_hop", "nhp_interface"]] - self.module = AnsibleModule( - argument_spec=self.spec, required_one_of=required_one_of, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def convert_len_to_mask(self, masklen): - """convert mask length to ip address mask, i.e. 24 to 255.255.255.0""" - - mask_int = ["0"] * 4 - length = int(masklen) - - if length > 32: - self.module.fail_json(msg='IPv4 ipaddress mask length is invalid') - if length < 8: - mask_int[0] = str(int((0xFF << (8 - length % 8)) & 0xFF)) - if length >= 8: - mask_int[0] = '255' - mask_int[1] = str(int((0xFF << (16 - (length % 16))) & 0xFF)) - if length >= 16: - mask_int[1] = '255' - mask_int[2] = str(int((0xFF << (24 - (length % 24))) & 0xFF)) - if length >= 24: - mask_int[2] = '255' - mask_int[3] = str(int((0xFF << (32 - (length % 32))) & 0xFF)) - if length == 32: - mask_int[3] = '255' - - return '.'.join(mask_int) - - def convert_ip_prefix(self): - """convert prefix to real value i.e. 2.2.2.2/24 to 2.2.2.0/24""" - if self.aftype == "v4": - if self.prefix.find('.') == -1: - return False - if self.mask == '32': - return True - if self.mask == '0': - self.prefix = '0.0.0.0' - return True - addr_list = self.prefix.split('.') - length = len(addr_list) - if length > 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - byte_len = 8 - ip_len = int(self.mask) // byte_len - ip_bit = int(self.mask) % byte_len - else: - if self.prefix.find(':') == -1: - return False - if self.mask == '128': - return True - if self.mask == '0': - self.prefix = '::' - return True - addr_list = self.prefix.split(':') - length = len(addr_list) - if length > 6: - return False - byte_len = 16 - ip_len = int(self.mask) // byte_len - ip_bit = int(self.mask) % byte_len - - if self.aftype == "v4": - for i in range(ip_len + 1, length): - addr_list[i] = 0 - else: - for i in range(length - ip_len, length): - addr_list[i] = 0 - for j in range(0, byte_len - ip_bit): - if self.aftype == "v4": - addr_list[ip_len] = int(addr_list[ip_len]) & (0 << j) - else: - if addr_list[length - ip_len - 1] == "": - continue - addr_list[length - ip_len - - 1] = '0x%s' % addr_list[length - ip_len - 1] - addr_list[length - ip_len - - 1] = int(addr_list[length - ip_len - 1], 16) & (0 << j) - - if self.aftype == "v4": - self.prefix = '%s.%s.%s.%s' % (addr_list[0], addr_list[1], addr_list[2], addr_list[3]) - return True - else: - ipv6_addr_str = "" - for num in range(0, length - ip_len): - ipv6_addr_str += '%s:' % addr_list[num] - self.prefix = ipv6_addr_str - return True - - def set_update_cmd(self): - """set update command""" - if not self.changed: - return - if self.aftype == "v4": - aftype = "ip" - maskstr = self.convert_len_to_mask(self.mask) - else: - aftype = "ipv6" - maskstr = self.mask - if self.next_hop is None: - next_hop = '' - else: - next_hop = self.next_hop - if self.vrf == "_public_": - vrf = '' - else: - vrf = self.vrf - if self.destvrf == "_public_": - destvrf = '' - else: - destvrf = self.destvrf - if self.nhp_interface == "Invalid0": - nhp_interface = '' - else: - nhp_interface = self.nhp_interface - if self.state == "present": - if self.vrf != "_public_": - if self.destvrf != "_public_": - self.updates_cmd.append('%s route-static vpn-instance %s %s %s vpn-instance %s %s' - % (aftype, vrf, self.prefix, maskstr, destvrf, next_hop)) - else: - self.updates_cmd.append('%s route-static vpn-instance %s %s %s %s %s' - % (aftype, vrf, self.prefix, maskstr, nhp_interface, next_hop)) - elif self.destvrf != "_public_": - self.updates_cmd.append('%s route-static %s %s vpn-instance %s %s' - % (aftype, self.prefix, maskstr, self.destvrf, next_hop)) - else: - self.updates_cmd.append('%s route-static %s %s %s %s' - % (aftype, self.prefix, maskstr, nhp_interface, next_hop)) - if self.pref: - self.updates_cmd[0] += ' preference %s' % (self.pref) - if self.tag: - self.updates_cmd[0] += ' tag %s' % (self.tag) - if self.description: - self.updates_cmd[0] += ' description %s' % (self.description) - - if self.state == "absent": - if self.vrf != "_public_": - if self.destvrf != "_public_": - self.updates_cmd.append('undo %s route-static vpn-instance %s %s %s vpn-instance %s %s' - % (aftype, vrf, self.prefix, maskstr, destvrf, next_hop)) - else: - self.updates_cmd.append('undo %s route-static vpn-instance %s %s %s %s %s' - % (aftype, vrf, self.prefix, maskstr, nhp_interface, next_hop)) - elif self.destvrf != "_public_": - self.updates_cmd.append('undo %s route-static %s %s vpn-instance %s %s' - % (aftype, self.prefix, maskstr, self.destvrf, self.next_hop)) - else: - self.updates_cmd.append('undo %s route-static %s %s %s %s' - % (aftype, self.prefix, maskstr, nhp_interface, next_hop)) - - def operate_static_route(self, version, prefix, mask, nhp_interface, next_hop, vrf, destvrf, state): - """operate ipv4 static route""" - - description_xml = """\n""" - preference_xml = """\n""" - tag_xml = """\n""" - if next_hop is None: - next_hop = '0.0.0.0' - if nhp_interface is None: - nhp_interface = "Invalid0" - - if vrf is None: - vpn_instance = "_public_" - else: - vpn_instance = vrf - - if destvrf is None: - dest_vpn_instance = "_public_" - else: - dest_vpn_instance = destvrf - if self.description: - description_xml = CE_NC_SET_DESCRIPTION % self.description - if self.pref: - preference_xml = CE_NC_SET_PREFERENCE % self.pref - if self.tag: - tag_xml = CE_NC_SET_TAG % self.tag - - if state == "present": - configxmlstr = CE_NC_SET_STATIC_ROUTE % ( - vpn_instance, version, prefix, mask, nhp_interface, - dest_vpn_instance, next_hop, description_xml, preference_xml, tag_xml) - else: - configxmlstr = CE_NC_DELETE_STATIC_ROUTE % ( - vpn_instance, version, prefix, mask, nhp_interface, dest_vpn_instance, next_hop) - - conf_str = build_config_xml(configxmlstr) - - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "OPERATE_STATIC_ROUTE") - - def get_static_route(self, state): - """get ipv4 static route""" - - self.static_routes_info["sroute"] = list() - - if state == 'absent': - getxmlstr = CE_NC_GET_STATIC_ROUTE_ABSENT - else: - getxmlstr = CE_NC_GET_STATIC_ROUTE - - xml_str = get_nc_config(self.module, getxmlstr) - - if 'data/' in xml_str: - return - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - static_routes = root.findall( - "staticrt/staticrtbase/srRoutes/srRoute") - - if static_routes: - for static_route in static_routes: - static_info = dict() - for static_ele in static_route: - if static_ele.tag in ["vrfName", "afType", "topologyName", - "prefix", "maskLength", "destVrfName", - "nexthop", "ifName", "preference", "description"]: - static_info[ - static_ele.tag] = static_ele.text - if static_ele.tag == "tag": - if static_ele.text is not None: - static_info["tag"] = static_ele.text - else: - static_info["tag"] = "None" - self.static_routes_info["sroute"].append(static_info) - - def check_params(self): - """check all input params""" - - # check prefix and mask - if not self.mask.isdigit(): - self.module.fail_json(msg='Error: Mask is invalid.') - # ipv4 check - if self.aftype == "v4": - if int(self.mask) > 32 or int(self.mask) < 0: - self.module.fail_json( - msg='Error: Ipv4 mask must be an integer between 1 and 32.') - # next_hop check - if self.next_hop: - if not is_valid_v4addr(self.next_hop): - self.module.fail_json( - msg='Error: The %s is not a valid address' % self.next_hop) - # ipv6 check - if self.aftype == "v6": - if int(self.mask) > 128 or int(self.mask) < 0: - self.module.fail_json( - msg='Error: Ipv6 mask must be an integer between 1 and 128.') - if self.next_hop: - if not is_valid_v6addr(self.next_hop): - self.module.fail_json( - msg='Error: The %s is not a valid address' % self.next_hop) - - # description check - if self.description: - if not is_valid_description(self.description): - self.module.fail_json( - msg='Error: Dsecription length should be 1 - 35, and can not contain "?".') - # tag check - if self.tag: - if not is_valid_tag(self.tag): - self.module.fail_json( - msg='Error: Tag should be integer 1 - 4294967295.') - # preference check - if self.pref: - if not is_valid_preference(self.pref): - self.module.fail_json( - msg='Error: Preference should be integer 1 - 255.') - if self.nhp_interface != "Invalid0" and self.destvrf != "_public_": - self.module.fail_json( - msg='Error: Destination vrf dose no support next hop is interface.') - # convert prefix - if not self.convert_ip_prefix(): - self.module.fail_json( - msg='Error: The %s is not a valid address' % self.prefix) - - def set_ip_static_route(self): - """set ip static route""" - if not self.changed: - return - version = None - if self.aftype == "v4": - version = "ipv4unicast" - else: - version = "ipv6unicast" - self.operate_static_route(version, self.prefix, self.mask, self.nhp_interface, - self.next_hop, self.vrf, self.destvrf, self.state) - - def is_prefix_exist(self, static_route, version): - """is prefix mask nex_thop exist""" - if static_route is None: - return False - if self.next_hop and self.nhp_interface: - return static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["afType"] == version \ - and static_route["ifName"].lower() == self.nhp_interface.lower() \ - and static_route["nexthop"].lower() == self.next_hop.lower() - - if self.next_hop and not self.nhp_interface: - return static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["afType"] == version \ - and static_route["nexthop"].lower() == self.next_hop.lower() - - if not self.next_hop and self.nhp_interface: - return static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["afType"] == version \ - and static_route["ifName"].lower() == self.nhp_interface.lower() - - def get_ip_static_route(self): - """get ip static route""" - - if self.aftype == "v4": - version = "ipv4unicast" - else: - version = "ipv6unicast" - change = False - self.get_static_route(self.state) - if self.state == 'present': - for static_route in self.static_routes_info["sroute"]: - if self.is_prefix_exist(static_route, version): - if self.vrf: - if static_route["vrfName"] != self.vrf: - change = True - if self.tag: - if static_route["tag"] != self.tag: - change = True - if self.destvrf: - if static_route["destVrfName"] != self.destvrf: - change = True - if self.description: - if static_route["description"] != self.description: - change = True - if self.pref: - if static_route["preference"] != self.pref: - change = True - if self.nhp_interface: - if static_route["ifName"].lower() != self.nhp_interface.lower(): - change = True - if self.next_hop: - if static_route["nexthop"].lower() != self.next_hop.lower(): - change = True - return change - else: - continue - change = True - else: - for static_route in self.static_routes_info["sroute"]: - if static_route["nexthop"] and self.next_hop: - if static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["nexthop"].lower() == self.next_hop.lower() \ - and static_route["afType"] == version: - change = True - return change - if static_route["ifName"] and self.nhp_interface: - if static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["ifName"].lower() == self.nhp_interface.lower() \ - and static_route["afType"] == version: - change = True - return change - else: - continue - change = False - return change - - def get_proposed(self): - """get proposed information""" - - self.proposed['prefix'] = self.prefix - self.proposed['mask'] = self.mask - self.proposed['afType'] = self.aftype - self.proposed['next_hop'] = self.next_hop - self.proposed['ifName'] = self.nhp_interface - self.proposed['vrfName'] = self.vrf - self.proposed['destVrfName'] = self.destvrf - if self.tag: - self.proposed['tag'] = self.tag - if self.description: - self.proposed['description'] = self.description - if self.pref is None: - self.proposed['preference'] = 60 - else: - self.proposed['preference'] = self.pref - self.proposed['state'] = self.state - - def get_existing(self): - """get existing information""" - - change = self.get_ip_static_route() - self.existing['sroute'] = self.static_routes_info["sroute"] - self.changed = bool(change) - - def get_end_state(self): - """get end state information""" - - self.get_static_route(self.state) - self.end_state['sroute'] = self.static_routes_info["sroute"] - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - self.get_existing() - self.get_proposed() - self.set_ip_static_route() - self.set_update_cmd() - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """main""" - - argument_spec = dict( - prefix=dict(required=True, type='str'), - mask=dict(required=True, type='str'), - aftype=dict(choices=['v4', 'v6'], required=True), - next_hop=dict(required=False, type='str'), - nhp_interface=dict(required=False, type='str'), - vrf=dict(required=False, type='str'), - destvrf=dict(required=False, type='str'), - tag=dict(required=False, type='str'), - description=dict(required=False, type='str'), - pref=dict(required=False, type='str'), - state=dict(choices=['absent', 'present'], - default='present', required=False), - ) - argument_spec.update(ce_argument_spec) - interface = StaticRoute(argument_spec) - interface.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_static_route_bfd.py b/plugins/modules/network/cloudengine/ce_static_route_bfd.py deleted file mode 100644 index 7cd571a2d3..0000000000 --- a/plugins/modules/network/cloudengine/ce_static_route_bfd.py +++ /dev/null @@ -1,1596 +0,0 @@ -#!/usr/bin/python -# 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_static_route_bfd -short_description: Manages static route configuration on HUAWEI CloudEngine switches. -description: - - Manages the static routes on HUAWEI CloudEngine switches. -author: xuxiaowei0512 (@CloudEngine-Ansible) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. - - If no vrf is supplied, vrf is set to default. - - If I(state=absent), the route configuration will be removed, regardless of the non-required parameters. -options: - prefix: - description: - - Destination ip address of static route. - required: true - type: str - mask: - description: - - Destination ip mask of static route. - type: str - aftype: - description: - - Destination ip address family type of static route. - required: true - type: str - choices: ['v4','v6'] - next_hop: - description: - - Next hop address of static route. - type: str - nhp_interface: - description: - - Next hop interface full name of static route. - type: str - vrf: - description: - - VPN instance of destination ip address. - type: str - destvrf: - description: - - VPN instance of next hop ip address. - type: str - tag: - description: - - Route tag value (numeric). - type: int - description: - description: - - Name of the route. Used with the name parameter on the CLI. - type: str - pref: - description: - - Preference or administrative difference of route (range 1-255). - type: int - function_flag: - description: - - Used to distinguish between command line functions. - required: true - choices: ['globalBFD','singleBFD','dynamicBFD','staticBFD'] - type: str - min_tx_interval: - description: - - Set the minimum BFD session sending interval (range 50-1000). - type: int - min_rx_interval: - description: - - Set the minimum BFD receive interval (range 50-1000). - type: int - detect_multiplier: - description: - - Configure the BFD multiplier (range 3-50). - type: int - bfd_session_name: - description: - - bfd name (range 1-15). - type: str - commands: - description: - - Incoming command line is used to send sys,undo ip route-static default-bfd,commit. - type: list - state: - description: - - Specify desired state of the resource. - required: false - choices: ['present','absent'] - type: str - default: present -''' - -EXAMPLES = ''' - #ip route-static bfd interface-type interface-number nexthop-address [ local-address address ] - #[ min-rx-interval min-rx-interval | min-tx-interval min-tx-interval | detect-multiplier multiplier ] - - name: Config an ip route-static bfd 10GE1/0/1 3.3.3.3 min-rx-interval 50 min-tx-interval 50 detect-multiplier 5 - ce_static_route_bfd: - function_flag: 'singleBFD' - nhp_interface: 10GE1/0/1 - next_hop: 3.3.3.3 - min_tx_interval: 50 - min_rx_interval: 50 - detect_multiplier: 5 - aftype: v4 - state: present - - #undo ip route-static bfd [ interface-type interface-number | vpn-instance vpn-instance-name ] nexthop-address - - name: undo ip route-static bfd 10GE1/0/1 3.3.3.4 - ce_static_route_bfd: - function_flag: 'singleBFD' - nhp_interface: 10GE1/0/1 - next_hop: 3.3.3.4 - aftype: v4 - state: absent - - #ip route-static default-bfd { min-rx-interval {min-rx-interval} | min-tx-interval {min-tx-interval} | detect-multiplier {multiplier}} - - name: Config an ip route-static default-bfd min-rx-interval 50 min-tx-interval 50 detect-multiplier 6 - ce_static_route_bfd: - function_flag: 'globalBFD' - min_tx_interval: 50 - min_rx_interval: 50 - detect_multiplier: 6 - aftype: v4 - state: present - - - name: undo ip route-static default-bfd - ce_static_route_bfd: - function_flag: 'globalBFD' - aftype: v4 - state: absent - commands: 'sys,undo ip route-static default-bfd,commit' - - - name: Config an ipv4 static route 2.2.2.0/24 2.2.2.1 preference 1 tag 2 description test for staticBFD - ce_static_route_bfd: - function_flag: 'staticBFD' - prefix: 2.2.2.2 - mask: 24 - next_hop: 2.2.2.1 - tag: 2 - description: test - pref: 1 - aftype: v4 - bfd_session_name: btoa - state: present -''' -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"function_flag": "staticBFD", "next_hop": "3.3.3.3", "pref": "100", - "prefix": "192.168.20.642", "mask": "24", "description": "testing", - "vrf": "_public_", "bfd_session_name": "btoa"} -existing: - description: k/v pairs of existing switchport - returned: always - type: dict - sample: {"function_flag": "", "next_hop": "", "pref": "101", - "prefix": "192.168.20.0", "mask": "24", "description": "testing", - "tag" : "null", "bfd_session_name": "btoa"} -end_state: - description: k/v pairs of switchport after module execution - returned: always - type: dict - sample: {"function_flag": "staticBFD", "next_hop": "3.3.3.3", "pref": "100", - "prefix": "192.168.20.0", "mask": "24", "description": "testing", - "tag" : "null", "bfd_session_name": "btoa"} -updates: - description: command list sent to the device - returned: always - type: list - sample: ["ip route-static 192.168.20.0 255.255.255.0 3.3.3.3 preference 100 description testing"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import string_types -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config - -CE_NC_GET_STATIC_ROUTE_BFD_SESSIONNAME = """ - - - - - - - - - - - - - - - - - - - - - - -""" -# bfd enable -CE_NC_GET_STATIC_ROUTE_BFD_ENABLE = """ - - - - - - - - - - - - - - - - - - - - - - -""" - -CE_NC_GET_STATIC_ROUTE_BFD_ABSENT = """ - - - - - - %s - %s - %s - %s - - - - - -""" - -CE_NC_GET_STATIC_ROUTE_BFD = """ - - - - - - %s - %s - %s - %s - - - - - - - - - -""" -CE_NC_GET_STATIC_ROUTE_IPV4_GLOBAL_BFD = """ - - - - - - - - - - - -""" -CE_NC_GET_STATIC_ROUTE_ABSENT = """ - - - - - - - - - - - - - - - - - - -""" - -CE_NC_DELETE_STATIC_ROUTE_SINGLEBFD = """ - - - - - %s - %s - %s - %s - - - - -""" -CE_NC_SET_STATIC_ROUTE_SINGLEBFD = """ - - - - - %s - %s - %s - %s%s%s%s%s - - - - - -""" -CE_NC_SET_STATIC_ROUTE_SINGLEBFD_LOCALADRESS = """ -%s -""" -CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINTX = """ -%s -""" -CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINRX = """ -%s -""" -CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MUL = """ -%s -""" -CE_NC_SET_IPV4_STATIC_ROUTE_GLOBALBFD = """ - - - - %s%s%s - - - -""" - -CE_NC_SET_STATIC_ROUTE = """ - - - - - %s - %s - base - %s - %s - %s - %s - %s%s%s%s%s - - - - -""" -CE_NC_SET_DESCRIPTION = """ -%s -""" - -CE_NC_SET_PREFERENCE = """ -%s -""" - -CE_NC_SET_TAG = """ -%s -""" -CE_NC_SET_BFDSESSIONNAME = """ -%s -""" -CE_NC_SET_BFDENABLE = """ -true -""" -CE_NC_DELETE_STATIC_ROUTE = """ - - - - - %s - %s - base - %s - %s - %s - %s - %s - - - - -""" - - -def build_config_xml(xmlstr): - """build config xml""" - - return ' ' + xmlstr + ' ' - - -def is_valid_v4addr(addr): - """check if ipv4 addr is valid""" - if addr.find('.') != -1: - addr_list = addr.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - return False - - -def is_valid_v6addr(addr): - """check if ipv6 addr is valid""" - if addr.find(':') != -1: - addr_list = addr.split(':') - if len(addr_list) > 6: - return False - if addr_list[1] == "": - return False - return True - return False - - -def is_valid_tag(tag): - """check if the tag is valid""" - - if int(tag) < 1 or int(tag) > 4294967295: - return False - return True - - -def is_valid_bdf_interval(interval): - """check if the min_tx_interva,min-rx-interval is valid""" - - if interval < 50 or interval > 1000: - return False - return True - - -def is_valid_bdf_multiplier(multiplier): - """check if the detect_multiplier is valid""" - - if multiplier < 3 or multiplier > 50: - return False - return True - - -def is_valid_bdf_session_name(session_name): - """check if the bfd_session_name is valid""" - if session_name.find(' ') != -1: - return False - if len(session_name) < 1 or len(session_name) > 15: - return False - return True - - -def is_valid_preference(pref): - """check if the preference is valid""" - - if int(pref) > 0 and int(pref) < 256: - return True - return False - - -def is_valid_description(description): - """check if the description is valid""" - if description.find('?') != -1: - return False - if len(description) < 1 or len(description) > 255: - return False - return True - - -def compare_command(commands): - """check if the commands is valid""" - if len(commands) < 3: - return True - if commands[0] != 'sys' or commands[1] != 'undo ip route-static default-bfd' \ - or commands[2] != 'commit': - return True - - -def get_to_lines(stdout): - """data conversion""" - lines = list() - for item in stdout: - if isinstance(item, string_types): - item = str(item).split('\n') - lines.append(item) - return lines - - -def get_change_state(oldvalue, newvalue, change): - """get change state""" - if newvalue is not None: - if oldvalue != str(newvalue): - change = True - else: - if oldvalue != newvalue: - change = True - return change - - -def get_xml(xml, value): - """operate xml""" - if value is None: - value = '' - else: - value = value - tempxml = xml % value - return tempxml - - -class StaticRouteBFD(object): - """static route module""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self._initmodule_() - - # static route info - self.function_flag = self.module.params['function_flag'] - self.aftype = self.module.params['aftype'] - self.state = self.module.params['state'] - if self.aftype == "v4": - self.version = "ipv4unicast" - else: - self.version = "ipv6unicast" - if self.function_flag != 'globalBFD': - self.nhp_interface = self.module.params['nhp_interface'] - if self.nhp_interface is None: - self.nhp_interface = "Invalid0" - - self.destvrf = self.module.params['destvrf'] - if self.destvrf is None: - self.destvrf = "_public_" - - self.next_hop = self.module.params['next_hop'] - self.prefix = self.module.params['prefix'] - - if self.function_flag != 'globalBFD' and self.function_flag != 'singleBFD': - self.mask = self.module.params['mask'] - self.tag = self.module.params['tag'] - self.description = self.module.params['description'] - self.pref = self.module.params['pref'] - if self.pref is None: - self.pref = 60 - # vpn instance info - self.vrf = self.module.params['vrf'] - if self.vrf is None: - self.vrf = "_public_" - # bfd session name - self.bfd_session_name = self.module.params['bfd_session_name'] - - if self.function_flag == 'globalBFD' or self.function_flag == 'singleBFD': - self.min_tx_interval = self.module.params['min_tx_interval'] - self.min_rx_interval = self.module.params['min_rx_interval'] - self.detect_multiplier = self.module.params['detect_multiplier'] - if self.function_flag == 'globalBFD' and self.state == 'absent': - self.commands = self.module.params['commands'] - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - self.static_routes_info = dict() - - def _initmodule_(self): - """init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=False) - - def _checkresponse_(self, xml_str, xml_name): - """check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def _convertlentomask_(self, masklen): - """convert mask length to ip address mask, i.e. 24 to 255.255.255.0""" - - mask_int = ["0"] * 4 - length = int(masklen) - - if length > 32: - self.module.fail_json(msg='IPv4 ipaddress mask length is invalid') - if length < 8: - mask_int[0] = str(int((0xFF << (8 - length % 8)) & 0xFF)) - if length >= 8: - mask_int[0] = '255' - mask_int[1] = str(int((0xFF << (16 - (length % 16))) & 0xFF)) - if length >= 16: - mask_int[1] = '255' - mask_int[2] = str(int((0xFF << (24 - (length % 24))) & 0xFF)) - if length >= 24: - mask_int[2] = '255' - mask_int[3] = str(int((0xFF << (32 - (length % 32))) & 0xFF)) - if length == 32: - mask_int[3] = '255' - - return '.'.join(mask_int) - - def _convertipprefix_(self): - """convert prefix to real value i.e. 2.2.2.2/24 to 2.2.2.0/24""" - if self.function_flag == 'singleBFD': - if self.aftype == "v4": - if self.prefix.find('.') == -1: - return False - addr_list = self.prefix.split('.') - length = len(addr_list) - if length > 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - else: - if self.prefix.find(':') == -1: - return False - else: - if self.aftype == "v4": - if self.prefix.find('.') == -1: - return False - if self.mask == '32': - self.prefix = self.prefix - return True - if self.mask == '0': - self.prefix = '0.0.0.0' - return True - addr_list = self.prefix.split('.') - length = len(addr_list) - if length > 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - byte_len = 8 - ip_len = int(self.mask) // byte_len - ip_bit = int(self.mask) % byte_len - else: - if self.prefix.find(':') == -1: - return False - if self.mask == '128': - self.prefix = self.prefix - return True - if self.mask == '0': - self.prefix = '::' - return True - addr_list = self.prefix.split(':') - length = len(addr_list) - if length > 6: - return False - byte_len = 16 - ip_len = int(self.mask) // byte_len - ip_bit = int(self.mask) % byte_len - - if self.aftype == "v4": - for i in range(ip_len + 1, length): - addr_list[i] = 0 - else: - for i in range(length - ip_len, length): - addr_list[i] = 0 - for j in range(0, byte_len - ip_bit): - if self.aftype == "v4": - addr_list[ip_len] = int(addr_list[ip_len]) & (0 << j) - else: - if addr_list[length - ip_len - 1] == "": - continue - addr_list[length - ip_len - - 1] = '0x%s' % addr_list[length - ip_len - 1] - addr_list[length - ip_len - - 1] = int(addr_list[length - ip_len - 1], 16) & (0 << j) - - if self.aftype == "v4": - self.prefix = '%s.%s.%s.%s' % (addr_list[0], addr_list[1], addr_list[2], addr_list[3]) - return True - if self.aftype == "v6": - ipv6_addr_str = "" - for num in range(0, length - ip_len): - ipv6_addr_str += '%s:' % addr_list[num] - self.prefix = ipv6_addr_str - - return True - - def set_update_cmd_globalbfd(self): - """set globalBFD update command""" - if not self.changed: - return - if self.state == "present": - self.updates_cmd.append('ip route-static default-bfd') - if self.min_tx_interval: - self.updates_cmd.append(' min-rx-interval %s' % (self.min_tx_interval)) - if self.min_rx_interval: - self.updates_cmd.append(' min-tx-interval %s' % (self.min_rx_interval)) - if self.detect_multiplier: - self.updates_cmd.append(' detect-multiplier %s' % (self.detect_multiplier)) - else: - self.updates_cmd.append('undo ip route-static default-bfd') - - def set_update_cmd_singlebfd(self): - """set singleBFD update command""" - if not self.changed: - return - if self.next_hop is None: - next_hop = '' - else: - next_hop = self.next_hop - - if self.destvrf == "_public_": - destvrf = '' - else: - destvrf = self.destvrf - - if self.nhp_interface == "Invalid0": - nhp_interface = '' - else: - nhp_interface = self.nhp_interface - if self.prefix == "0.0.0.0": - prefix = '' - else: - prefix = self.prefix - if self.state == "present": - if nhp_interface: - self.updates_cmd.append('ip route-static bfd %s %s' % (nhp_interface, next_hop)) - elif destvrf: - self.updates_cmd.append('ip route-static bfd vpn-instance %s %s' % (destvrf, next_hop)) - else: - self.updates_cmd.append('ip route-static bfd %s' % (next_hop)) - if prefix: - self.updates_cmd.append(' local-address %s' % (self.prefix)) - if self.min_tx_interval: - self.updates_cmd.append(' min-rx-interval %s' % (self.min_tx_interval)) - if self.min_rx_interval: - self.updates_cmd.append(' min-tx-interval %s' % (self.min_rx_interval)) - if self.detect_multiplier: - self.updates_cmd.append(' detect-multiplier %s' % (self.detect_multiplier)) - else: - if nhp_interface: - self.updates_cmd.append('undo ip route-static bfd %s %s' % (nhp_interface, next_hop)) - elif destvrf: - self.updates_cmd.append('undo ip route-static bfd vpn-instance %s %s' % (destvrf, next_hop)) - else: - self.updates_cmd.append('undo ip route-static bfd %s' % (next_hop)) - - def set_update_cmd(self): - """set update command""" - if not self.changed: - return - - if self.aftype == "v4": - maskstr = self._convertlentomask_(self.mask) - else: - maskstr = self.mask - static_bfd_flag = True - if self.bfd_session_name: - static_bfd_flag = False - if self.next_hop is None: - next_hop = '' - else: - next_hop = self.next_hop - if self.vrf == "_public_": - vrf = '' - else: - vrf = self.vrf - if self.destvrf == "_public_": - destvrf = '' - else: - destvrf = self.destvrf - if self.nhp_interface == "Invalid0": - nhp_interface = '' - else: - nhp_interface = self.nhp_interface - if self.state == "present": - if self.vrf != "_public_": - if self.destvrf != "_public_": - self.updates_cmd.append('ip route-static vpn-instance %s %s %s vpn-instance %s %s' - % (vrf, self.prefix, maskstr, destvrf, next_hop)) - else: - self.updates_cmd.append('ip route-static vpn-instance %s %s %s %s %s' - % (vrf, self.prefix, maskstr, nhp_interface, next_hop)) - elif self.destvrf != "_public_": - self.updates_cmd.append('ip route-static %s %s vpn-instance %s %s' - % (self.prefix, maskstr, self.destvrf, next_hop)) - else: - self.updates_cmd.append('ip route-static %s %s %s %s' - % (self.prefix, maskstr, nhp_interface, next_hop)) - if self.pref != 60: - self.updates_cmd.append(' preference %s' % (self.pref)) - if self.tag: - self.updates_cmd.append(' tag %s' % (self.tag)) - if not static_bfd_flag: - self.updates_cmd.append(' track bfd-session %s' % (self.bfd_session_name)) - else: - self.updates_cmd.append(' bfd enable') - if self.description: - self.updates_cmd.append(' description %s' % (self.description)) - - if self.state == "absent": - if self.vrf != "_public_": - if self.destvrf != "_public_": - self.updates_cmd.append('undo ip route-static vpn-instance %s %s %s vpn-instance %s %s' - % (vrf, self.prefix, maskstr, destvrf, next_hop)) - else: - self.updates_cmd.append('undo ip route-static vpn-instance %s %s %s %s %s' - % (vrf, self.prefix, maskstr, nhp_interface, next_hop)) - elif self.destvrf != "_public_": - self.updates_cmd.append('undo ip route-static %s %s vpn-instance %s %s' - % (self.prefix, maskstr, self.destvrf, self.next_hop)) - else: - self.updates_cmd.append('undo ip route-static %s %s %s %s' - % (self.prefix, maskstr, nhp_interface, next_hop)) - - def operate_static_route_globalbfd(self): - """set globalbfd update command""" - min_tx_interval = self.min_tx_interval - min_rx_interval = self.min_rx_interval - multiplier = self.detect_multiplier - min_tx_interval_xml = """\n""" - min_rx_interval_xml = """\n""" - multiplier_xml = """\n""" - if self.state == "present": - if min_tx_interval is not None: - min_tx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINTX % min_tx_interval - if min_rx_interval is not None: - min_rx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINRX % min_rx_interval - if multiplier is not None: - multiplier_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MUL % multiplier - - configxmlstr = CE_NC_SET_IPV4_STATIC_ROUTE_GLOBALBFD % ( - min_tx_interval_xml, min_rx_interval_xml, multiplier_xml) - conf_str = build_config_xml(configxmlstr) - recv_xml = set_nc_config(self.module, conf_str) - self._checkresponse_(recv_xml, "OPERATE_STATIC_ROUTE_globalBFD") - - if self.state == "absent" and self.commands: - min_tx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINTX % 1000 - min_rx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINRX % 1000 - multiplier_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MUL % 3 - - configxmlstr = CE_NC_SET_IPV4_STATIC_ROUTE_GLOBALBFD % ( - min_tx_interval_xml, min_rx_interval_xml, multiplier_xml) - conf_str = build_config_xml(configxmlstr) - recv_xml = set_nc_config(self.module, conf_str) - self._checkresponse_(recv_xml, "OPERATE_STATIC_ROUTE_globalBFD") - - def operate_static_route_singlebfd(self, version, prefix, nhp_interface, next_hop, destvrf, state): - """operate ipv4 static route singleBFD""" - min_tx_interval = self.min_tx_interval - min_rx_interval = self.min_rx_interval - multiplier = self.detect_multiplier - min_tx_interval_xml = """\n""" - min_rx_interval_xml = """\n""" - multiplier_xml = """\n""" - local_address_xml = """\n""" - if next_hop is None: - next_hop = '0.0.0.0' - - if destvrf is None: - dest_vpn_instance = "_public_" - else: - dest_vpn_instance = destvrf - - if nhp_interface is None: - nhp_interface = "Invalid0" - - if min_tx_interval is not None: - min_tx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINTX % min_tx_interval - if min_rx_interval is not None: - min_rx_interval_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MINRX % min_rx_interval - if multiplier is not None: - multiplier_xml = CE_NC_SET_IPV4_STATIC_ROUTE_BFDCOMMON_MUL % multiplier - - if prefix is not None: - local_address_xml = CE_NC_SET_STATIC_ROUTE_SINGLEBFD_LOCALADRESS % prefix - - if state == "present": - configxmlstr = CE_NC_SET_STATIC_ROUTE_SINGLEBFD % ( - version, nhp_interface, dest_vpn_instance, - next_hop, local_address_xml, min_tx_interval_xml, - min_rx_interval_xml, multiplier_xml) - - else: - configxmlstr = CE_NC_DELETE_STATIC_ROUTE_SINGLEBFD % ( - version, nhp_interface, dest_vpn_instance, next_hop) - - conf_str = build_config_xml(configxmlstr) - - recv_xml = set_nc_config(self.module, conf_str) - self._checkresponse_(recv_xml, "OPERATE_STATIC_ROUTE_singleBFD") - - def operate_static_route(self, version, prefix, mask, nhp_interface, next_hop, vrf, destvrf, state): - """operate ipv4 static route""" - description_xml = """\n""" - preference_xml = """\n""" - tag_xml = """\n""" - bfd_xml = """\n""" - if next_hop is None: - next_hop = '0.0.0.0' - if nhp_interface is None: - nhp_interface = "Invalid0" - - if vrf is None: - vpn_instance = "_public_" - else: - vpn_instance = vrf - - if destvrf is None: - dest_vpn_instance = "_public_" - else: - dest_vpn_instance = destvrf - - description_xml = get_xml(CE_NC_SET_DESCRIPTION, self.description) - - preference_xml = get_xml(CE_NC_SET_PREFERENCE, self.pref) - - tag_xml = get_xml(CE_NC_SET_TAG, self.tag) - - if self.function_flag == 'staticBFD': - if self.bfd_session_name: - bfd_xml = CE_NC_SET_BFDSESSIONNAME % self.bfd_session_name - else: - bfd_xml = CE_NC_SET_BFDENABLE - if state == "present": - configxmlstr = CE_NC_SET_STATIC_ROUTE % ( - vpn_instance, version, prefix, mask, nhp_interface, - dest_vpn_instance, next_hop, description_xml, preference_xml, tag_xml, bfd_xml) - - else: - configxmlstr = CE_NC_DELETE_STATIC_ROUTE % ( - vpn_instance, version, prefix, mask, nhp_interface, dest_vpn_instance, next_hop) - - conf_str = build_config_xml(configxmlstr) - recv_xml = set_nc_config(self.module, conf_str) - self._checkresponse_(recv_xml, "OPERATE_STATIC_ROUTE") - - def get_change_state_global_bfd(self): - """get ipv4 global bfd change state""" - - self.get_global_bfd(self.state) - change = False - if self.state == "present": - if self.static_routes_info["sroute_global_bfd"]: - for static_route in self.static_routes_info["sroute_global_bfd"]: - if static_route is not None: - if self.min_tx_interval is not None: - if int(static_route["minTxInterval"]) != self.min_tx_interval: - change = True - if self.min_rx_interval is not None: - if int(static_route["minRxInterval"]) != self.min_rx_interval: - change = True - if self.detect_multiplier is not None: - if int(static_route["multiplier"]) != self.detect_multiplier: - change = True - return change - else: - continue - else: - change = True - else: - if self.commands: - if self.static_routes_info["sroute_global_bfd"]: - for static_route in self.static_routes_info["sroute_global_bfd"]: - if static_route is not None: - if int(static_route["minTxInterval"]) != 1000 or \ - int(static_route["minRxInterval"]) != 1000 or \ - int(static_route["multiplier"]) != 3: - change = True - return change - - def get_global_bfd(self, state): - """get ipv4 global bfd""" - - self.static_routes_info["sroute_global_bfd"] = list() - - getglobalbfdxmlstr = None - if self.aftype == 'v4': - getglobalbfdxmlstr = CE_NC_GET_STATIC_ROUTE_IPV4_GLOBAL_BFD - - if getglobalbfdxmlstr is not None: - xml_global_bfd_str = get_nc_config(self.module, getglobalbfdxmlstr) - - if 'data/' in xml_global_bfd_str: - return - - xml_global_bfd_str = xml_global_bfd_str.replace('\r', '').replace('\n', ''). \ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_global_bfd_str) - static_routes_global_bfd = root.findall( - "staticrt/staticrtbase/srIPv4StaticSite") - - if static_routes_global_bfd: - for static_route in static_routes_global_bfd: - static_info = dict() - for static_ele in static_route: - if static_ele.tag == "minTxInterval": - if static_ele.text is not None: - static_info["minTxInterval"] = static_ele.text - if static_ele.tag == "minRxInterval": - if static_ele.text is not None: - static_info["minRxInterval"] = static_ele.text - if static_ele.tag == "multiplier": - if static_ele.text is not None: - static_info["multiplier"] = static_ele.text - - self.static_routes_info["sroute_global_bfd"].append(static_info) - - def get_change_state_single_bfd(self): - """get ipv4 single bfd change state""" - - self.get_single_bfd(self.state) - change = False - version = self.version - if self.state == 'present': - if self.static_routes_info["sroute_single_bfd"]: - for static_route in self.static_routes_info["sroute_single_bfd"]: - if static_route is not None and static_route['afType'] == version: - if self.nhp_interface: - if static_route["ifName"].lower() != self.nhp_interface.lower(): - change = True - if self.destvrf: - if static_route["destVrfName"].lower() != self.destvrf.lower(): - change = True - if self.next_hop: - if static_route["nexthop"].lower() != self.next_hop.lower(): - change = True - if self.prefix: - if static_route["localAddress"].lower() != self.prefix.lower(): - change = True - if self.min_tx_interval: - if int(static_route["minTxInterval"]) != self.min_tx_interval: - change = True - if self.min_rx_interval: - if int(static_route["minRxInterval"]) != self.min_rx_interval: - change = True - if self.detect_multiplier: - if int(static_route["multiplier"]) != self.detect_multiplier: - change = True - return change - - else: - continue - else: - change = True - else: - for static_route in self.static_routes_info["sroute_single_bfd"]: - # undo ip route-static bfd [ interface-type interface-number | - # vpn-instance vpn-instance-name ] nexthop-address - - if static_route["ifName"] and self.nhp_interface: - if static_route["ifName"].lower() == self.nhp_interface.lower() \ - and static_route["nexthop"].lower() == self.next_hop.lower() \ - and static_route["afType"] == version: - change = True - return change - - if static_route["destVrfName"] and self.destvrf: - if static_route["destVrfName"].lower() == self.destvrf.lower() \ - and static_route["nexthop"].lower() == self.next_hop.lower() \ - and static_route["afType"] == version: - change = True - return change - - if static_route["nexthop"] and self.next_hop: - if static_route["nexthop"].lower() == self.next_hop.lower() \ - and static_route["afType"] == version: - change = True - return change - else: - continue - change = False - return change - - def get_single_bfd(self, state): - """get ipv4 sigle bfd""" - self.static_routes_info["sroute_single_bfd"] = list() - if self.aftype == "v4": - version = "ipv4unicast" - else: - version = "ipv6unicast" - if state == 'absent': - getbfdxmlstr = CE_NC_GET_STATIC_ROUTE_BFD_ABSENT % ( - version, self.nhp_interface, self.destvrf, self.next_hop) - else: - getbfdxmlstr = CE_NC_GET_STATIC_ROUTE_BFD % ( - version, self.nhp_interface, self.destvrf, self.next_hop) - xml_bfd_str = get_nc_config(self.module, getbfdxmlstr) - - if 'data/' in xml_bfd_str: - return - xml_bfd_str = xml_bfd_str.replace('\r', '').replace('\n', ''). \ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_bfd_str) - static_routes_bfd = root.findall( - "staticrt/staticrtbase/srBfdParas/srBfdPara") - if static_routes_bfd: - for static_route in static_routes_bfd: - static_info = dict() - for static_ele in static_route: - if static_ele.tag in ["afType", "destVrfName", "nexthop", "ifName"]: - static_info[static_ele.tag] = static_ele.text - if static_ele.tag == "localAddress": - if static_ele.text is not None: - static_info["localAddress"] = static_ele.text - else: - static_info["localAddress"] = "None" - if static_ele.tag == "minTxInterval": - if static_ele.text is not None: - static_info["minTxInterval"] = static_ele.text - if static_ele.tag == "minRxInterval": - if static_ele.text is not None: - static_info["minRxInterval"] = static_ele.text - if static_ele.tag == "multiplier": - if static_ele.text is not None: - static_info["multiplier"] = static_ele.text - self.static_routes_info["sroute_single_bfd"].append(static_info) - - def get_static_route(self, state): - """get ipv4 static route about BFD""" - self.static_routes_info["sroute"] = list() - # Increase the parameter used to distinguish whether the incoming bfdSessionName - static_bfd_flag = True - if self.bfd_session_name: - static_bfd_flag = False - - if state == 'absent': - getxmlstr = CE_NC_GET_STATIC_ROUTE_ABSENT - else: - # self.static_bfd_flag is true - if static_bfd_flag: - getxmlstr = CE_NC_GET_STATIC_ROUTE_BFD_ENABLE - - else: - getxmlstr = CE_NC_GET_STATIC_ROUTE_BFD_SESSIONNAME - xml_str = get_nc_config(self.module, getxmlstr) - if 'data/' in xml_str: - return - xml_str = xml_str.replace('\r', '').replace('\n', ''). \ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', ""). \ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - root = ElementTree.fromstring(xml_str) - static_routes = root.findall( - "staticrt/staticrtbase/srRoutes/srRoute") - - if static_routes: - for static_route in static_routes: - static_info = dict() - for static_ele in static_route: - if static_ele.tag in ["vrfName", "afType", "topologyName", - "prefix", "maskLength", "destVrfName", - "nexthop", "ifName", "preference", "description"]: - static_info[static_ele.tag] = static_ele.text - if static_ele.tag == "tag": - if static_ele.text is not None: - static_info["tag"] = static_ele.text - else: - static_info["tag"] = "None" - if static_bfd_flag: - if static_ele.tag == "bfdEnable": - if static_ele.text is not None: - static_info["bfdEnable"] = static_ele.text - else: - static_info["bfdEnable"] = "None" - else: - if static_ele.tag == "sessionName": - if static_ele.text is not None: - static_info["sessionName"] = static_ele.text - else: - static_info["sessionName"] = "None" - self.static_routes_info["sroute"].append(static_info) - - def _checkparams_(self): - """check all input params""" - if self.function_flag == 'singleBFD': - if not self.next_hop: - self.module.fail_json(msg='Error: missing required argument: next_hop.') - if self.state != 'absent': - if self.nhp_interface == "Invalid0" and (not self.prefix or self.prefix == '0.0.0.0'): - self.module.fail_json(msg='Error: If a nhp_interface is not configured, ' - 'the prefix must be configured.') - - if self.function_flag != 'globalBFD': - if self.function_flag == 'dynamicBFD' or self.function_flag == 'staticBFD': - if not self.mask: - self.module.fail_json(msg='Error: missing required argument: mask.') - # check prefix and mask - if not self.mask.isdigit(): - self.module.fail_json(msg='Error: Mask is invalid.') - if self.function_flag != 'singleBFD' or (self.function_flag == 'singleBFD' and self.destvrf != "_public_"): - if not self.prefix: - self.module.fail_json(msg='Error: missing required argument: prefix.') - # convert prefix - if not self._convertipprefix_(): - self.module.fail_json(msg='Error: The %s is not a valid address' % self.prefix) - - if self.nhp_interface != "Invalid0" and self.destvrf != "_public_": - self.module.fail_json(msg='Error: Destination vrf dose not support next hop is interface.') - - if not self.next_hop and self.nhp_interface == "Invalid0": - self.module.fail_json(msg='Error: one of the following is required: next_hop,nhp_interface.') - - if self.function_flag == 'dynamicBFD' or self.function_flag == 'staticBFD': - # description check - if self.description: - if not is_valid_description(self.description): - self.module.fail_json( - msg='Error: Dsecription length should be 1 - 35, and can not contain "?".') - # tag check - if self.tag is not None: - if not is_valid_tag(self.tag): - self.module.fail_json( - msg='Error: Tag should be integer 1 - 4294967295.') - # preference check - if self.pref is not None: - if not is_valid_preference(self.pref): - self.module.fail_json( - msg='Error: Preference should be integer 1 - 255.') - - if self.function_flag == 'staticBFD': - if self.bfd_session_name: - if not is_valid_bdf_session_name(self.bfd_session_name): - self.module.fail_json( - msg='Error: bfd_session_name length should be 1 - 15, and can not contain Space.') - - # ipv4 check - if self.aftype == "v4": - if self.function_flag == 'dynamicBFD' or self.function_flag == 'staticBFD': - if int(self.mask) > 32 or int(self.mask) < 0: - self.module.fail_json( - msg='Error: Ipv4 mask must be an integer between 1 and 32.') - # next_hop check - if self.function_flag != 'globalBFD': - if self.next_hop: - if not is_valid_v4addr(self.next_hop): - self.module.fail_json( - msg='Error: The %s is not a valid address.' % self.next_hop) - # ipv6 check - if self.aftype == "v6": - if self.function_flag == 'dynamicBFD' or self.function_flag == 'staticBFD': - if int(self.mask) > 128 or int(self.mask) < 0: - self.module.fail_json( - msg='Error: Ipv6 mask must be an integer between 1 and 128.') - if self.function_flag != 'globalBFD': - if self.next_hop: - if not is_valid_v6addr(self.next_hop): - self.module.fail_json( - msg='Error: The %s is not a valid address.' % self.next_hop) - - if self.function_flag == 'globalBFD' or self.function_flag == 'singleBFD': - # BFD prarams - if self.min_tx_interval: - if not is_valid_bdf_interval(self.min_tx_interval): - self.module.fail_json( - msg='Error: min_tx_interval should be integer 50 - 1000.') - if self.min_rx_interval: - if not is_valid_bdf_interval(self.min_rx_interval): - self.module.fail_json( - msg='Error: min_rx_interval should be integer 50 - 1000.') - if self.detect_multiplier: - if not is_valid_bdf_multiplier(self.detect_multiplier): - self.module.fail_json( - msg='Error: detect_multiplier should be integer 3 - 50.') - - if self.function_flag == 'globalBFD': - if self.state != 'absent': - if not self.min_tx_interval and not self.min_rx_interval and not self.detect_multiplier: - self.module.fail_json( - msg='Error: one of the following is required: min_tx_interval,' - 'detect_multiplier,min_rx_interval.') - else: - if not self.commands: - self.module.fail_json( - msg='Error: missing required argument: command.') - if compare_command(self.commands): - self.module.fail_json( - msg='Error: The command %s line is incorrect.' % (',').join(self.commands)) - - def set_ip_static_route_globalbfd(self): - """set ip static route globalBFD""" - if not self.changed: - return - if self.aftype == "v4": - self.operate_static_route_globalbfd() - - def set_ip_static_route_singlebfd(self): - """set ip static route singleBFD""" - if not self.changed: - return - version = None - if self.aftype == "v4": - version = "ipv4unicast" - else: - version = "ipv6unicast" - self.operate_static_route_singlebfd(version, self.prefix, self.nhp_interface, - self.next_hop, self.destvrf, self.state) - - def set_ip_static_route(self): - """set ip static route""" - if not self.changed: - return - version = None - if self.aftype == "v4": - version = "ipv4unicast" - else: - version = "ipv6unicast" - self.operate_static_route(version, self.prefix, self.mask, self.nhp_interface, - self.next_hop, self.vrf, self.destvrf, self.state) - - def is_prefix_exist(self, static_route, version): - """is prefix mask nex_thop exist""" - if static_route is None: - return False - if self.next_hop and self.nhp_interface: - return static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["afType"] == version \ - and static_route["ifName"].lower() == self.nhp_interface.lower() \ - and static_route["nexthop"].lower() == self.next_hop.lower() - - if self.next_hop and not self.nhp_interface: - return static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["afType"] == version \ - and static_route["nexthop"].lower() == self.next_hop.lower() - - if not self.next_hop and self.nhp_interface: - return static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["afType"] == version \ - and static_route["ifName"].lower() == self.nhp_interface.lower() - - def get_ip_static_route(self): - """get ip static route""" - change = False - version = self.version - self.get_static_route(self.state) - change_list = list() - if self.state == 'present': - for static_route in self.static_routes_info["sroute"]: - if self.is_prefix_exist(static_route, self.version): - info_dict = dict() - exist_dict = dict() - if self.vrf: - info_dict["vrfName"] = self.vrf - exist_dict["vrfName"] = static_route["vrfName"] - if self.destvrf: - info_dict["destVrfName"] = self.destvrf - exist_dict["destVrfName"] = static_route["destVrfName"] - if self.description: - info_dict["description"] = self.description - exist_dict["description"] = static_route["description"] - if self.tag: - info_dict["tag"] = self.tag - exist_dict["tag"] = static_route["tag"] - if self.pref: - info_dict["preference"] = str(self.pref) - exist_dict["preference"] = static_route["preference"] - if self.nhp_interface: - if self.nhp_interface.lower() == "invalid0": - info_dict["ifName"] = "Invalid0" - else: - info_dict["ifName"] = "Invalid0" - exist_dict["ifName"] = static_route["ifName"] - if self.next_hop: - info_dict["nexthop"] = self.next_hop - exist_dict["nexthop"] = static_route["nexthop"] - - if self.bfd_session_name: - info_dict["bfdEnable"] = 'true' - - else: - info_dict["bfdEnable"] = 'false' - exist_dict["bfdEnable"] = static_route["bfdEnable"] - - if exist_dict != info_dict: - change = True - else: - change = False - change_list.append(change) - - if False in change_list: - change = False - else: - change = True - return change - - else: - for static_route in self.static_routes_info["sroute"]: - if static_route["nexthop"] and self.next_hop: - if static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["nexthop"].lower() == self.next_hop.lower() \ - and static_route["afType"] == version: - change = True - return change - if static_route["ifName"] and self.nhp_interface: - if static_route["prefix"].lower() == self.prefix.lower() \ - and static_route["maskLength"] == self.mask \ - and static_route["ifName"].lower() == self.nhp_interface.lower() \ - and static_route["afType"] == version: - change = True - return change - else: - continue - change = False - return change - - def get_proposed(self): - """get proposed information""" - self.proposed['afType'] = self.aftype - self.proposed['state'] = self.state - if self.function_flag != 'globalBFD': - self.proposed['ifName'] = self.nhp_interface - self.proposed['destVrfName'] = self.destvrf - self.proposed['next_hop'] = self.next_hop - - if self.function_flag == 'singleBFD': - if self.prefix: - self.proposed['localAddress'] = self.prefix - - if self.function_flag == 'globalBFD' or self.function_flag == 'singleBFD': - self.proposed['minTxInterval'] = self.min_tx_interval - self.proposed['minRxInterval'] = self.min_rx_interval - self.proposed['multiplier'] = self.detect_multiplier - - if self.function_flag != 'globalBFD' and self.function_flag != 'singleBFD': - self.proposed['prefix'] = self.prefix - self.proposed['mask'] = self.mask - self.proposed['vrfName'] = self.vrf - if self.tag: - self.proposed['tag'] = self.tag - if self.description: - self.proposed['description'] = self.description - if self.pref is None: - self.proposed['preference'] = 60 - else: - self.proposed['preference'] = self.pref - - static_bfd_flag = True - if self.bfd_session_name: - static_bfd_flag = False - if not static_bfd_flag: - self.proposed['sessionName'] = self.bfd_session_name - else: - self.proposed['bfdEnable'] = 'true' - - def get_existing(self): - """get existing information""" - # globalBFD - if self.function_flag == 'globalBFD': - change = self.get_change_state_global_bfd() - self.existing['sroute_global_bfd'] = self.static_routes_info["sroute_global_bfd"] - # singleBFD - elif self.function_flag == 'singleBFD': - change = self.get_change_state_single_bfd() - self.existing['sroute_single_bfd'] = self.static_routes_info["sroute_single_bfd"] - # dynamicBFD / staticBFD - else: - change = self.get_ip_static_route() - self.existing['static_sroute'] = self.static_routes_info["sroute"] - self.changed = bool(change) - - def get_end_state(self): - """get end state information""" - - # globalBFD - if self.function_flag == 'globalBFD': - self.get_global_bfd(self.state) - self.end_state['sroute_global_bfd'] = self.static_routes_info["sroute_global_bfd"] - # singleBFD - elif self.function_flag == 'singleBFD': - self.static_routes_info["sroute_single_bfd"] = list() - self.get_single_bfd(self.state) - self.end_state['sroute_single_bfd'] = self.static_routes_info["sroute_single_bfd"] - # dynamicBFD / staticBFD - else: - self.get_static_route(self.state) - self.end_state['static_sroute'] = self.static_routes_info["sroute"] - - def work(self): - """worker""" - self._checkparams_() - self.get_existing() - self.get_proposed() - - if self.function_flag == 'globalBFD': - self.set_ip_static_route_globalbfd() - self.set_update_cmd_globalbfd() - elif self.function_flag == 'singleBFD': - self.set_ip_static_route_singlebfd() - self.set_update_cmd_singlebfd() - else: - self.set_ip_static_route() - self.set_update_cmd() - - self.get_end_state() - if self.existing == self.end_state: - self.changed = False - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """main""" - - argument_spec = dict( - prefix=dict(type='str'), - mask=dict(type='str'), - aftype=dict(choices=['v4', 'v6'], required=True), - next_hop=dict(type='str'), - nhp_interface=dict(type='str'), - vrf=dict(type='str'), - destvrf=dict(type='str'), - tag=dict(type='int'), - description=dict(type='str'), - pref=dict(type='int'), - # bfd - function_flag=dict(required=True, choices=['globalBFD', 'singleBFD', 'dynamicBFD', 'staticBFD']), - min_tx_interval=dict(type='int'), - min_rx_interval=dict(type='int'), - detect_multiplier=dict(type='int'), - # bfd session name - bfd_session_name=dict(type='str'), - commands=dict(type='list', required=False), - state=dict(choices=['absent', 'present'], default='present', required=False), - ) - interface = StaticRouteBFD(argument_spec) - interface.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_stp.py b/plugins/modules/network/cloudengine/ce_stp.py deleted file mode 100644 index a9c690305d..0000000000 --- a/plugins/modules/network/cloudengine/ce_stp.py +++ /dev/null @@ -1,973 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_stp -short_description: Manages STP configuration on HUAWEI CloudEngine switches. -description: - - Manages STP configurations on HUAWEI CloudEngine switches. -author: - - wangdezhuang (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - state: - description: - - Specify desired state of the resource. - default: present - choices: ['present', 'absent'] - stp_mode: - description: - - Set an operation mode for the current MSTP process. - The mode can be STP, RSTP, or MSTP. - choices: ['stp', 'rstp', 'mstp'] - stp_enable: - description: - - Enable or disable STP on a switch. - choices: ['enable', 'disable'] - stp_converge: - description: - - STP convergence mode. - Fast means set STP aging mode to Fast. - Normal means set STP aging mode to Normal. - choices: ['fast', 'normal'] - bpdu_protection: - description: - - Configure BPDU protection on an edge port. - This function prevents network flapping caused by attack packets. - choices: ['enable', 'disable'] - tc_protection: - description: - - Configure the TC BPDU protection function for an MSTP process. - choices: ['enable', 'disable'] - tc_protection_interval: - description: - - Set the time the MSTP device takes to handle the maximum number of TC BPDUs - and immediately refresh forwarding entries. - The value is an integer ranging from 1 to 600, in seconds. - tc_protection_threshold: - description: - - Set the maximum number of TC BPDUs that the MSTP can handle. - The value is an integer ranging from 1 to 255. The default value is 1 on the switch. - interface: - description: - - Interface name. - If the value is C(all), will apply configuration to all interfaces. - if the value is a special name, only support input the full name. - edged_port: - description: - - Set the current port as an edge port. - choices: ['enable', 'disable'] - bpdu_filter: - description: - - Specify a port as a BPDU filter port. - choices: ['enable', 'disable'] - cost: - description: - - Set the path cost of the current port. - The default instance is 0. - root_protection: - description: - - Enable root protection on the current port. - choices: ['enable', 'disable'] - loop_protection: - description: - - Enable loop protection on the current port. - choices: ['enable', 'disable'] -''' - -EXAMPLES = ''' - -- name: CloudEngine stp test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Config stp mode" - ce_stp: - state: present - stp_mode: stp - provider: "{{ cli }}" - - - name: "Undo stp mode" - ce_stp: - state: absent - stp_mode: stp - provider: "{{ cli }}" - - - name: "Enable bpdu protection" - ce_stp: - state: present - bpdu_protection: enable - provider: "{{ cli }}" - - - name: "Disable bpdu protection" - ce_stp: - state: present - bpdu_protection: disable - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"bpdu_protection": "enable", - "state": "present"} -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: {"bpdu_protection": "disable"} -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: {"bpdu_protection": "enable"} -updates: - description: command sent to the device - returned: always - type: list - sample: ["stp bpdu-protection"] -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import exec_command, load_config, ce_argument_spec - - -def get_config(module, flags): - - """Retrieves the current config from the device or cache""" - - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(module, cmd) - if rc != 0: - module.fail_json(msg=err) - config = str(out).strip() - if config.startswith("display"): - configs = config.split("\n") - if len(configs) > 1: - return "\n".join(configs[1:]) - else: - return "" - else: - return config - - -class Stp(object): - """ Manages stp/rstp/mstp configuration """ - - def __init__(self, **kwargs): - """ Stp module init """ - - # module - argument_spec = kwargs["argument_spec"] - self.spec = argument_spec - self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True) - - # config - self.cur_cfg = dict() - self.stp_cfg = None - self.interface_stp_cfg = None - - # module args - self.state = self.module.params['state'] or None - self.stp_mode = self.module.params['stp_mode'] or None - self.stp_enable = self.module.params['stp_enable'] or None - self.stp_converge = self.module.params['stp_converge'] or None - self.interface = self.module.params['interface'] or None - self.edged_port = self.module.params['edged_port'] or None - self.bpdu_filter = self.module.params['bpdu_filter'] or None - self.cost = self.module.params['cost'] or None - self.bpdu_protection = self.module.params['bpdu_protection'] or None - self.tc_protection = self.module.params['tc_protection'] or None - self.tc_protection_interval = self.module.params['tc_protection_interval'] or None - self.tc_protection_threshold = self.module.params['tc_protection_threshold'] or None - self.root_protection = self.module.params['root_protection'] or None - self.loop_protection = self.module.params['loop_protection'] or None - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def cli_load_config(self, commands): - """ Cli load configuration """ - - if not self.module.check_mode: - load_config(self.module, commands) - - def cli_get_stp_config(self): - """ Cli get stp configuration """ - - flags = [r"| section include #\s*\n\s*stp", r"| section exclude #\s*\n+\s*stp process \d+"] - self.stp_cfg = get_config(self.module, flags) - - def cli_get_interface_stp_config(self): - """ Cli get interface's stp configuration """ - - if self.interface: - regular = r"| ignore-case section include ^#\s+interface %s\s+" % self.interface.replace(" ", "") - flags = list() - flags.append(regular) - tmp_cfg = get_config(self.module, flags) - - if not tmp_cfg: - self.module.fail_json( - msg='Error: The interface %s is not exist.' % self.interface) - - if "undo portswitch" in tmp_cfg: - self.module.fail_json( - msg='Error: The interface %s is not switch mode.' % self.interface) - - self.interface_stp_cfg = tmp_cfg - - def check_params(self): - """ Check module params """ - - if self.cost: - if self.cost.isdigit(): - if int(self.cost) < 1 or int(self.cost) > 200000000: - self.module.fail_json( - msg='Error: The value of cost is out of [1 - 200000000].') - else: - self.module.fail_json( - msg='Error: The cost is not digit.') - - if self.tc_protection_interval: - if self.tc_protection_interval.isdigit(): - if int(self.tc_protection_interval) < 1 or int(self.tc_protection_interval) > 600: - self.module.fail_json( - msg='Error: The value of tc_protection_interval is out of [1 - 600].') - else: - self.module.fail_json( - msg='Error: The tc_protection_interval is not digit.') - - if self.tc_protection_threshold: - if self.tc_protection_threshold.isdigit(): - if int(self.tc_protection_threshold) < 1 or int(self.tc_protection_threshold) > 255: - self.module.fail_json( - msg='Error: The value of tc_protection_threshold is out of [1 - 255].') - else: - self.module.fail_json( - msg='Error: The tc_protection_threshold is not digit.') - - if self.root_protection or self.loop_protection or self.cost: - if not self.interface: - self.module.fail_json( - msg='Error: Please input interface.') - elif self.interface == "all": - self.module.fail_json( - msg='Error: Interface can not be all when config root_protection or loop_protection or cost.') - - if self.root_protection and self.root_protection == "enable": - if self.loop_protection and self.loop_protection == "enable": - self.module.fail_json( - msg='Error: Can not enable root_protection and loop_protection at the same interface.') - - if self.edged_port or self.bpdu_filter: - if not self.interface: - self.module.fail_json( - msg='Error: Please input interface.') - - def get_proposed(self): - """ Get module proposed """ - - self.proposed["state"] = self.state - - if self.stp_mode: - self.proposed["stp_mode"] = self.stp_mode - if self.stp_enable: - self.proposed["stp_enable"] = self.stp_enable - if self.stp_converge: - self.proposed["stp_converge"] = self.stp_converge - if self.interface: - self.proposed["interface"] = self.interface - if self.edged_port: - self.proposed["edged_port"] = self.edged_port - if self.bpdu_filter: - self.proposed["bpdu_filter"] = self.bpdu_filter - if self.cost: - self.proposed["cost"] = self.cost - if self.bpdu_protection: - self.proposed["bpdu_protection"] = self.bpdu_protection - if self.tc_protection: - self.proposed["tc_protection"] = self.tc_protection - if self.tc_protection_interval: - self.proposed["tc_protection_interval"] = self.tc_protection_interval - if self.tc_protection_threshold: - self.proposed["tc_protection_threshold"] = self.tc_protection_threshold - if self.root_protection: - self.proposed["root_protection"] = self.root_protection - if self.loop_protection: - self.proposed["loop_protection"] = self.loop_protection - - def get_existing(self): - """ Get existing configuration """ - - self.cli_get_stp_config() - if self.interface and self.interface != "all": - self.cli_get_interface_stp_config() - - if self.stp_mode: - if "stp mode stp" in self.stp_cfg: - self.cur_cfg["stp_mode"] = "stp" - self.existing["stp_mode"] = "stp" - elif "stp mode rstp" in self.stp_cfg: - self.cur_cfg["stp_mode"] = "rstp" - self.existing["stp_mode"] = "rstp" - else: - self.cur_cfg["stp_mode"] = "mstp" - self.existing["stp_mode"] = "mstp" - - if self.stp_enable: - if "stp disable" in self.stp_cfg: - self.cur_cfg["stp_enable"] = "disable" - self.existing["stp_enable"] = "disable" - else: - self.cur_cfg["stp_enable"] = "enable" - self.existing["stp_enable"] = "enable" - - if self.stp_converge: - if "stp converge fast" in self.stp_cfg: - self.cur_cfg["stp_converge"] = "fast" - self.existing["stp_converge"] = "fast" - else: - self.cur_cfg["stp_converge"] = "normal" - self.existing["stp_converge"] = "normal" - - if self.edged_port: - if self.interface == "all": - if "stp edged-port default" in self.stp_cfg: - self.cur_cfg["edged_port"] = "enable" - self.existing["edged_port"] = "enable" - else: - self.cur_cfg["edged_port"] = "disable" - self.existing["edged_port"] = "disable" - else: - if "stp edged-port enable" in self.interface_stp_cfg: - self.cur_cfg["edged_port"] = "enable" - self.existing["edged_port"] = "enable" - else: - self.cur_cfg["edged_port"] = "disable" - self.existing["edged_port"] = "disable" - - if self.bpdu_filter: - if self.interface == "all": - if "stp bpdu-filter default" in self.stp_cfg: - self.cur_cfg["bpdu_filter"] = "enable" - self.existing["bpdu_filter"] = "enable" - else: - self.cur_cfg["bpdu_filter"] = "disable" - self.existing["bpdu_filter"] = "disable" - else: - if "stp bpdu-filter enable" in self.interface_stp_cfg: - self.cur_cfg["bpdu_filter"] = "enable" - self.existing["bpdu_filter"] = "enable" - else: - self.cur_cfg["bpdu_filter"] = "disable" - self.existing["bpdu_filter"] = "disable" - - if self.bpdu_protection: - if "stp bpdu-protection" in self.stp_cfg: - self.cur_cfg["bpdu_protection"] = "enable" - self.existing["bpdu_protection"] = "enable" - else: - self.cur_cfg["bpdu_protection"] = "disable" - self.existing["bpdu_protection"] = "disable" - - if self.tc_protection: - pre_cfg = self.stp_cfg.split("\n") - if "stp tc-protection" in pre_cfg: - self.cur_cfg["tc_protection"] = "enable" - self.existing["tc_protection"] = "enable" - else: - self.cur_cfg["tc_protection"] = "disable" - self.existing["tc_protection"] = "disable" - - if self.tc_protection_interval: - if "stp tc-protection interval" in self.stp_cfg: - tmp_value = re.findall(r'stp tc-protection interval (.*)', self.stp_cfg) - if not tmp_value: - self.module.fail_json( - msg='Error: Can not find tc-protection interval on the device.') - self.cur_cfg["tc_protection_interval"] = tmp_value[0] - self.existing["tc_protection_interval"] = tmp_value[0] - else: - self.cur_cfg["tc_protection_interval"] = "null" - self.existing["tc_protection_interval"] = "null" - - if self.tc_protection_threshold: - if "stp tc-protection threshold" in self.stp_cfg: - tmp_value = re.findall(r'stp tc-protection threshold (.*)', self.stp_cfg) - if not tmp_value: - self.module.fail_json( - msg='Error: Can not find tc-protection threshold on the device.') - self.cur_cfg["tc_protection_threshold"] = tmp_value[0] - self.existing["tc_protection_threshold"] = tmp_value[0] - else: - self.cur_cfg["tc_protection_threshold"] = "1" - self.existing["tc_protection_threshold"] = "1" - - if self.cost: - tmp_value = re.findall(r'stp instance (.*) cost (.*)', self.interface_stp_cfg) - if not tmp_value: - self.cur_cfg["cost"] = "null" - self.existing["cost"] = "null" - else: - self.cur_cfg["cost"] = tmp_value[0][1] - self.existing["cost"] = tmp_value[0][1] - - # root_protection and loop_protection should get configuration at the same time - if self.root_protection or self.loop_protection: - if "stp root-protection" in self.interface_stp_cfg: - self.cur_cfg["root_protection"] = "enable" - self.existing["root_protection"] = "enable" - else: - self.cur_cfg["root_protection"] = "disable" - self.existing["root_protection"] = "disable" - - if "stp loop-protection" in self.interface_stp_cfg: - self.cur_cfg["loop_protection"] = "enable" - self.existing["loop_protection"] = "enable" - else: - self.cur_cfg["loop_protection"] = "disable" - self.existing["loop_protection"] = "disable" - - def get_end_state(self): - """ Get end state """ - - self.cli_get_stp_config() - if self.interface and self.interface != "all": - self.cli_get_interface_stp_config() - - if self.stp_mode: - if "stp mode stp" in self.stp_cfg: - self.end_state["stp_mode"] = "stp" - elif "stp mode rstp" in self.stp_cfg: - self.end_state["stp_mode"] = "rstp" - else: - self.end_state["stp_mode"] = "mstp" - - if self.stp_enable: - if "stp disable" in self.stp_cfg: - self.end_state["stp_enable"] = "disable" - else: - self.end_state["stp_enable"] = "enable" - - if self.stp_converge: - if "stp converge fast" in self.stp_cfg: - self.end_state["stp_converge"] = "fast" - else: - self.end_state["stp_converge"] = "normal" - - if self.edged_port: - if self.interface == "all": - if "stp edged-port default" in self.stp_cfg: - self.end_state["edged_port"] = "enable" - else: - self.end_state["edged_port"] = "disable" - else: - if "stp edged-port enable" in self.interface_stp_cfg: - self.end_state["edged_port"] = "enable" - else: - self.end_state["edged_port"] = "disable" - - if self.bpdu_filter: - if self.interface == "all": - if "stp bpdu-filter default" in self.stp_cfg: - self.end_state["bpdu_filter"] = "enable" - else: - self.end_state["bpdu_filter"] = "disable" - else: - if "stp bpdu-filter enable" in self.interface_stp_cfg: - self.end_state["bpdu_filter"] = "enable" - else: - self.end_state["bpdu_filter"] = "disable" - - if self.bpdu_protection: - if "stp bpdu-protection" in self.stp_cfg: - self.end_state["bpdu_protection"] = "enable" - else: - self.end_state["bpdu_protection"] = "disable" - - if self.tc_protection: - pre_cfg = self.stp_cfg.split("\n") - if "stp tc-protection" in pre_cfg: - self.end_state["tc_protection"] = "enable" - else: - self.end_state["tc_protection"] = "disable" - - if self.tc_protection_interval: - if "stp tc-protection interval" in self.stp_cfg: - tmp_value = re.findall(r'stp tc-protection interval (.*)', self.stp_cfg) - if not tmp_value: - self.module.fail_json( - msg='Error: Can not find tc-protection interval on the device.') - self.end_state["tc_protection_interval"] = tmp_value[0] - else: - self.end_state["tc_protection_interval"] = "null" - - if self.tc_protection_threshold: - if "stp tc-protection threshold" in self.stp_cfg: - tmp_value = re.findall(r'stp tc-protection threshold (.*)', self.stp_cfg) - if not tmp_value: - self.module.fail_json( - msg='Error: Can not find tc-protection threshold on the device.') - self.end_state["tc_protection_threshold"] = tmp_value[0] - else: - self.end_state["tc_protection_threshold"] = "1" - - if self.cost: - tmp_value = re.findall(r'stp instance (.*) cost (.*)', self.interface_stp_cfg) - if not tmp_value: - self.end_state["cost"] = "null" - else: - self.end_state["cost"] = tmp_value[0][1] - - if self.root_protection or self.loop_protection: - if "stp root-protection" in self.interface_stp_cfg: - self.end_state["root_protection"] = "enable" - else: - self.end_state["root_protection"] = "disable" - - if "stp loop-protection" in self.interface_stp_cfg: - self.end_state["loop_protection"] = "enable" - else: - self.end_state["loop_protection"] = "disable" - - if self.existing == self.end_state: - self.changed = False - self.updates_cmd = list() - - def present_stp(self): - """ Present stp configuration """ - - cmds = list() - - # config stp global - if self.stp_mode: - if self.stp_mode != self.cur_cfg["stp_mode"]: - cmd = "stp mode %s" % self.stp_mode - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.stp_enable: - if self.stp_enable != self.cur_cfg["stp_enable"]: - cmd = "stp %s" % self.stp_enable - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.stp_converge: - if self.stp_converge != self.cur_cfg["stp_converge"]: - cmd = "stp converge %s" % self.stp_converge - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.edged_port: - if self.interface == "all": - if self.edged_port != self.cur_cfg["edged_port"]: - if self.edged_port == "enable": - cmd = "stp edged-port default" - cmds.append(cmd) - self.updates_cmd.append(cmd) - else: - cmd = "undo stp edged-port default" - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.bpdu_filter: - if self.interface == "all": - if self.bpdu_filter != self.cur_cfg["bpdu_filter"]: - if self.bpdu_filter == "enable": - cmd = "stp bpdu-filter default" - cmds.append(cmd) - self.updates_cmd.append(cmd) - else: - cmd = "undo stp bpdu-filter default" - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.bpdu_protection: - if self.bpdu_protection != self.cur_cfg["bpdu_protection"]: - if self.bpdu_protection == "enable": - cmd = "stp bpdu-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - else: - cmd = "undo stp bpdu-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.tc_protection: - if self.tc_protection != self.cur_cfg["tc_protection"]: - if self.tc_protection == "enable": - cmd = "stp tc-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - else: - cmd = "undo stp tc-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.tc_protection_interval: - if self.tc_protection_interval != self.cur_cfg["tc_protection_interval"]: - cmd = "stp tc-protection interval %s" % self.tc_protection_interval - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.tc_protection_threshold: - if self.tc_protection_threshold != self.cur_cfg["tc_protection_threshold"]: - cmd = "stp tc-protection threshold %s" % self.tc_protection_threshold - cmds.append(cmd) - self.updates_cmd.append(cmd) - - # config interface stp - if self.interface and self.interface != "all": - tmp_changed = False - - cmd = "interface %s" % self.interface - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.edged_port: - if self.edged_port != self.cur_cfg["edged_port"]: - if self.edged_port == "enable": - cmd = "stp edged-port enable" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - else: - cmd = "undo stp edged-port" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if self.bpdu_filter: - if self.bpdu_filter != self.cur_cfg["bpdu_filter"]: - if self.bpdu_filter == "enable": - cmd = "stp bpdu-filter enable" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - else: - cmd = "undo stp bpdu-filter" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if self.root_protection: - if self.root_protection == "enable" and self.cur_cfg["loop_protection"] == "enable": - self.module.fail_json( - msg='Error: The interface has enable loop_protection, can not enable root_protection.') - if self.root_protection != self.cur_cfg["root_protection"]: - if self.root_protection == "enable": - cmd = "stp root-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - else: - cmd = "undo stp root-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if self.loop_protection: - if self.loop_protection == "enable" and self.cur_cfg["root_protection"] == "enable": - self.module.fail_json( - msg='Error: The interface has enable root_protection, can not enable loop_protection.') - if self.loop_protection != self.cur_cfg["loop_protection"]: - if self.loop_protection == "enable": - cmd = "stp loop-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - else: - cmd = "undo stp loop-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if self.cost: - if self.cost != self.cur_cfg["cost"]: - cmd = "stp cost %s" % self.cost - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if not tmp_changed: - cmd = "interface %s" % self.interface - self.updates_cmd.remove(cmd) - cmds.remove(cmd) - - if cmds: - self.cli_load_config(cmds) - self.changed = True - - def absent_stp(self): - """ Absent stp configuration """ - - cmds = list() - - if self.stp_mode: - if self.stp_mode == self.cur_cfg["stp_mode"]: - if self.stp_mode != "mstp": - cmd = "undo stp mode" - cmds.append(cmd) - self.updates_cmd.append(cmd) - self.changed = True - - if self.stp_enable: - if self.stp_enable != self.cur_cfg["stp_enable"]: - cmd = "stp %s" % self.stp_enable - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.stp_converge: - if self.stp_converge == self.cur_cfg["stp_converge"]: - cmd = "undo stp converge" - cmds.append(cmd) - self.updates_cmd.append(cmd) - self.changed = True - - if self.edged_port: - if self.interface == "all": - if self.edged_port != self.cur_cfg["edged_port"]: - if self.edged_port == "enable": - cmd = "stp edged-port default" - cmds.append(cmd) - self.updates_cmd.append(cmd) - else: - cmd = "undo stp edged-port default" - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.bpdu_filter: - if self.interface == "all": - if self.bpdu_filter != self.cur_cfg["bpdu_filter"]: - if self.bpdu_filter == "enable": - cmd = "stp bpdu-filter default" - cmds.append(cmd) - self.updates_cmd.append(cmd) - else: - cmd = "undo stp bpdu-filter default" - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.bpdu_protection: - if self.bpdu_protection != self.cur_cfg["bpdu_protection"]: - if self.bpdu_protection == "enable": - cmd = "stp bpdu-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - else: - cmd = "undo stp bpdu-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.tc_protection: - if self.tc_protection != self.cur_cfg["tc_protection"]: - if self.tc_protection == "enable": - cmd = "stp tc-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - else: - cmd = "undo stp tc-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.tc_protection_interval: - if self.tc_protection_interval == self.cur_cfg["tc_protection_interval"]: - cmd = "undo stp tc-protection interval" - cmds.append(cmd) - self.updates_cmd.append(cmd) - self.changed = True - - if self.tc_protection_threshold: - if self.tc_protection_threshold == self.cur_cfg["tc_protection_threshold"]: - if self.tc_protection_threshold != "1": - cmd = "undo stp tc-protection threshold" - cmds.append(cmd) - self.updates_cmd.append(cmd) - self.changed = True - - # undo interface stp - if self.interface and self.interface != "all": - tmp_changed = False - - cmd = "interface %s" % self.interface - cmds.append(cmd) - self.updates_cmd.append(cmd) - - if self.edged_port: - if self.edged_port != self.cur_cfg["edged_port"]: - if self.edged_port == "enable": - cmd = "stp edged-port enable" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - else: - cmd = "undo stp edged-port" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if self.bpdu_filter: - if self.bpdu_filter != self.cur_cfg["bpdu_filter"]: - if self.bpdu_filter == "enable": - cmd = "stp bpdu-filter enable" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - else: - cmd = "undo stp bpdu-filter" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if self.root_protection: - if self.root_protection == "enable" and self.cur_cfg["loop_protection"] == "enable": - self.module.fail_json( - msg='Error: The interface has enable loop_protection, can not enable root_protection.') - if self.root_protection != self.cur_cfg["root_protection"]: - if self.root_protection == "enable": - cmd = "stp root-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - else: - cmd = "undo stp root-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if self.loop_protection: - if self.loop_protection == "enable" and self.cur_cfg["root_protection"] == "enable": - self.module.fail_json( - msg='Error: The interface has enable root_protection, can not enable loop_protection.') - if self.loop_protection != self.cur_cfg["loop_protection"]: - if self.loop_protection == "enable": - cmd = "stp loop-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - else: - cmd = "undo stp loop-protection" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if self.cost: - if self.cost == self.cur_cfg["cost"]: - cmd = "undo stp cost" - cmds.append(cmd) - self.updates_cmd.append(cmd) - tmp_changed = True - - if not tmp_changed: - cmd = "interface %s" % self.interface - self.updates_cmd.remove(cmd) - cmds.remove(cmd) - - if cmds: - self.cli_load_config(cmds) - self.changed = True - - def work(self): - """ Work function """ - - self.check_params() - self.get_proposed() - self.get_existing() - - if self.state == "present": - self.present_stp() - else: - self.absent_stp() - - self.get_end_state() - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - self.results['updates'] = self.updates_cmd - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - state=dict(choices=['present', 'absent'], default='present'), - stp_mode=dict(choices=['stp', 'rstp', 'mstp']), - stp_enable=dict(choices=['enable', 'disable']), - stp_converge=dict(choices=['fast', 'normal']), - bpdu_protection=dict(choices=['enable', 'disable']), - tc_protection=dict(choices=['enable', 'disable']), - tc_protection_interval=dict(type='str'), - tc_protection_threshold=dict(type='str'), - interface=dict(type='str'), - edged_port=dict(choices=['enable', 'disable']), - bpdu_filter=dict(choices=['enable', 'disable']), - cost=dict(type='str'), - root_protection=dict(choices=['enable', 'disable']), - loop_protection=dict(choices=['enable', 'disable']) - ) - - argument_spec.update(ce_argument_spec) - module = Stp(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_switchport.py b/plugins/modules/network/cloudengine/ce_switchport.py deleted file mode 100644 index aeb8c365b9..0000000000 --- a/plugins/modules/network/cloudengine/ce_switchport.py +++ /dev/null @@ -1,1001 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_switchport -short_description: Manages Layer 2 switchport interfaces on HUAWEI CloudEngine switches. -description: - - Manages Layer 2 switchport interfaces on HUAWEI CloudEngine switches. -author: QijunPan (@QijunPan) -notes: - - When C(state=absent), VLANs can be added/removed from trunk links and - the existing access VLAN can be 'unconfigured' to just having VLAN 1 on that interface. - - When working with trunks VLANs the keywords add/remove are always sent - in the C(port trunk allow-pass vlan) command. Use verbose mode to see commands sent. - - When C(state=unconfigured), the interface will result with having a default Layer 2 interface, i.e. vlan 1 in access mode. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - interface: - description: - - Full name of the interface, i.e. 40GE1/0/22. - required: true - mode: - description: - - The link type of an interface. - choices: ['access','trunk', 'hybrid', 'dot1qtunnel'] - default_vlan: - description: - - If C(mode=access, or mode=dot1qtunnel), used as the access VLAN ID, in the range from 1 to 4094. - pvid_vlan: - description: - - If C(mode=trunk, or mode=hybrid), used as the trunk native VLAN ID, in the range from 1 to 4094. - trunk_vlans: - description: - - If C(mode=trunk), used as the VLAN range to ADD or REMOVE - from the trunk, such as 2-10 or 2,5,10-15, etc. - untagged_vlans: - description: - - If C(mode=hybrid), used as the VLAN range to ADD or REMOVE - from the trunk, such as 2-10 or 2,5,10-15, etc. - tagged_vlans: - description: - - If C(mode=hybrid), used as the VLAN range to ADD or REMOVE - from the trunk, such as 2-10 or 2,5,10-15, etc. - state: - description: - - Manage the state of the resource. - default: present - choices: ['present', 'absent', 'unconfigured'] -''' - -EXAMPLES = ''' -- name: switchport module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - name: Ensure 10GE1/0/22 is in its default switchport state - ce_switchport: - interface: 10GE1/0/22 - state: unconfigured - provider: '{{ cli }}' - - - name: Ensure 10GE1/0/22 is configured for access vlan 20 - ce_switchport: - interface: 10GE1/0/22 - mode: access - default_vlan: 20 - provider: '{{ cli }}' - - - name: Ensure 10GE1/0/22 only has vlans 5-10 as trunk vlans - ce_switchport: - interface: 10GE1/0/22 - mode: trunk - pvid_vlan: 10 - trunk_vlans: 5-10 - provider: '{{ cli }}' - - - name: Ensure 10GE1/0/22 is a trunk port and ensure 2-50 are being tagged (doesn't mean others aren't also being tagged) - ce_switchport: - interface: 10GE1/0/22 - mode: trunk - pvid_vlan: 10 - trunk_vlans: 2-50 - provider: '{{ cli }}' - - - name: Ensure these VLANs are not being tagged on the trunk - ce_switchport: - interface: 10GE1/0/22 - mode: trunk - trunk_vlans: 51-4000 - state: absent - provider: '{{ cli }}' -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"default_vlan": "20", "interface": "10GE1/0/22", "mode": "access"} -existing: - description: k/v pairs of existing switchport - returned: always - type: dict - sample: {"default_vlan": "10", "interface": "10GE1/0/22", - "mode": "access", "switchport": "enable"} -end_state: - description: k/v pairs of switchport after module execution - returned: always - type: dict - sample: {"default_vlan": "20", "interface": "10GE1/0/22", - "mode": "access", "switchport": "enable"} -updates: - description: command string sent to the device - returned: always - type: list - sample: ["10GE1/0/22", "port default vlan 20"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from xml.etree import ElementTree as ET -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_PORT_ATTR = """ - - - - - %s - - - - - - - - - - - -""" - -CE_NC_SET_PORT = """ - - - - %s - - %s - %s - %s - %s - - - - -""" - -CE_NC_SET_PORT_MODE = """ - - - - %s - - %s - - - - -""" - -CE_NC_SET_DEFAULT_PORT = """ - - - - - %s - - access - 1 - - - - - - - -""" - - -SWITCH_PORT_TYPE = ('ge', '10ge', '25ge', - '4x10ge', '40ge', '100ge', 'eth-trunk') - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -def is_portswitch_enalbed(iftype): - """"[undo] portswitch""" - - return bool(iftype in SWITCH_PORT_TYPE) - - -def vlan_bitmap_undo(bitmap): - """convert vlan bitmap to undo bitmap""" - - vlan_bit = ['F'] * 1024 - - if not bitmap or len(bitmap) == 0: - return ''.join(vlan_bit) - - bit_len = len(bitmap) - for num in range(bit_len): - undo = (~int(bitmap[num], 16)) & 0xF - vlan_bit[num] = hex(undo)[2] - - return ''.join(vlan_bit) - - -def is_vlan_bitmap_empty(bitmap): - """check vlan bitmap empty""" - - if not bitmap or len(bitmap) == 0: - return True - - bit_len = len(bitmap) - for num in range(bit_len): - if bitmap[num] != '0': - return False - - return True - - -class SwitchPort(object): - """ - Manages Layer 2 switchport interfaces. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # interface and vlan info - self.interface = self.module.params['interface'] - self.mode = self.module.params['mode'] - self.state = self.module.params['state'] - self.default_vlan = self.module.params['default_vlan'] - self.pvid_vlan = self.module.params['pvid_vlan'] - self.trunk_vlans = self.module.params['trunk_vlans'] - self.untagged_vlans = self.module.params['untagged_vlans'] - self.tagged_vlans = self.module.params['tagged_vlans'] - - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - self.intf_info = dict() # interface vlan info - self.intf_type = None # loopback tunnel ... - - def init_module(self): - """ init module """ - - required_if = [('state', 'absent', ['mode']), ('state', 'present', ['mode'])] - mutually_exclusive = [['default_vlan', 'trunk_vlans'], - ['default_vlan', 'pvid_vlan'], - ['default_vlan', 'untagged_vlans'], - ['trunk_vlans', 'untagged_vlans'], - ['trunk_vlans', 'tagged_vlans'], - ['default_vlan', 'tagged_vlans']] - - self.module = AnsibleModule( - argument_spec=self.spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_interface_dict(self, ifname): - """ get one interface attributes dict.""" - - intf_info = dict() - conf_str = CE_NC_GET_PORT_ATTR % ifname - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return intf_info - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - tree = ET.fromstring(xml_str) - l2Enable = tree.find('ethernet/ethernetIfs/ethernetIf/l2Enable') - intf_info["l2Enable"] = l2Enable.text - port_type = tree.find('ethernet/ethernetIfs/ethernetIf/l2Attribute') - for pre in port_type: - intf_info[pre.tag] = pre.text - intf_info["ifName"] = ifname - if intf_info["trunkVlans"] is None: - intf_info["trunkVlans"] = "" - if intf_info["untagVlans"] is None: - intf_info["untagVlans"] = "" - return intf_info - - def is_l2switchport(self): - """Check layer2 switch port""" - - return bool(self.intf_info["l2Enable"] == "enable") - - def merge_access_vlan(self, ifname, default_vlan): - """Merge access interface vlan""" - - change = False - conf_str = "" - - self.updates_cmd.append("interface %s" % ifname) - if self.state == "present": - if self.intf_info["linkType"] == "access": - if default_vlan and self.intf_info["pvid"] != default_vlan: - self.updates_cmd.append( - "port default vlan %s" % default_vlan) - conf_str = CE_NC_SET_PORT % (ifname, "access", default_vlan, "", "") - change = True - else: # not access - self.updates_cmd.append("port link-type access") - if default_vlan: - self.updates_cmd.append( - "port default vlan %s" % default_vlan) - conf_str = CE_NC_SET_PORT % (ifname, "access", default_vlan, "", "") - else: - conf_str = CE_NC_SET_PORT % (ifname, "access", "1", "", "") - change = True - elif self.state == "absent": - if self.intf_info["linkType"] == "access": - if default_vlan and self.intf_info["pvid"] == default_vlan and default_vlan != "1": - self.updates_cmd.append( - "undo port default vlan %s" % default_vlan) - conf_str = CE_NC_SET_PORT % (ifname, "access", "1", "", "") - change = True - - if not change: - self.updates_cmd.pop() # remove interface - return - conf_str = "" + conf_str + "" - rcv_xml = set_nc_config(self.module, conf_str) - self.check_response(rcv_xml, "MERGE_ACCESS_PORT") - self.changed = True - - def merge_trunk_vlan(self, ifname, pvid_vlan, trunk_vlans): - """Merge trunk interface vlan""" - - change = False - xmlstr = "" - pvid = "" - trunk = "" - self.updates_cmd.append("interface %s" % ifname) - if trunk_vlans: - vlan_list = self.vlan_range_to_list(trunk_vlans) - vlan_map = self.vlan_list_to_bitmap(vlan_list) - if self.state == "present": - if self.intf_info["linkType"] == "trunk": - if pvid_vlan and self.intf_info["pvid"] != pvid_vlan: - self.updates_cmd.append( - "port trunk pvid vlan %s" % pvid_vlan) - pvid = pvid_vlan - change = True - - if trunk_vlans: - add_vlans = self.vlan_bitmap_add( - self.intf_info["trunkVlans"], vlan_map) - if not is_vlan_bitmap_empty(add_vlans): - self.updates_cmd.append( - "port trunk allow-pass %s" - % trunk_vlans.replace(',', ' ').replace('-', ' to ')) - trunk = "%s:%s" % (add_vlans, add_vlans) - change = True - if pvid or trunk: - xmlstr += CE_NC_SET_PORT % (ifname, "trunk", pvid, trunk, "") - if not pvid: - xmlstr = xmlstr.replace("", "") - if not trunk: - xmlstr = xmlstr.replace("", "") - - else: # not trunk - self.updates_cmd.append("port link-type trunk") - change = True - if pvid_vlan: - self.updates_cmd.append( - "port trunk pvid vlan %s" % pvid_vlan) - pvid = pvid_vlan - if trunk_vlans: - self.updates_cmd.append( - "port trunk allow-pass %s" - % trunk_vlans.replace(',', ' ').replace('-', ' to ')) - trunk = "%s:%s" % (vlan_map, vlan_map) - if pvid or trunk: - xmlstr += CE_NC_SET_PORT % (ifname, "trunk", pvid, trunk, "") - if not pvid: - xmlstr = xmlstr.replace("", "") - if not trunk: - xmlstr = xmlstr.replace("", "") - - if not pvid_vlan and not trunk_vlans: - xmlstr += CE_NC_SET_PORT_MODE % (ifname, "trunk") - self.updates_cmd.append( - "undo port trunk allow-pass vlan 1") - elif self.state == "absent": - if self.intf_info["linkType"] == "trunk": - if pvid_vlan and self.intf_info["pvid"] == pvid_vlan and pvid_vlan != '1': - self.updates_cmd.append( - "undo port trunk pvid vlan %s" % pvid_vlan) - pvid = "1" - change = True - if trunk_vlans: - del_vlans = self.vlan_bitmap_del( - self.intf_info["trunkVlans"], vlan_map) - if not is_vlan_bitmap_empty(del_vlans): - self.updates_cmd.append( - "undo port trunk allow-pass %s" - % trunk_vlans.replace(',', ' ').replace('-', ' to ')) - undo_map = vlan_bitmap_undo(del_vlans) - trunk = "%s:%s" % (undo_map, del_vlans) - change = True - if pvid or trunk: - xmlstr += CE_NC_SET_PORT % (ifname, "trunk", pvid, trunk, "") - if not pvid: - xmlstr = xmlstr.replace("", "") - if not trunk: - xmlstr = xmlstr.replace("", "") - - if not change: - self.updates_cmd.pop() - return - conf_str = "" + xmlstr + "" - rcv_xml = set_nc_config(self.module, conf_str) - self.check_response(rcv_xml, "MERGE_TRUNK_PORT") - self.changed = True - - def merge_hybrid_vlan(self, ifname, pvid_vlan, tagged_vlans, untagged_vlans): - """Merge hybrid interface vlan""" - - change = False - xmlstr = "" - pvid = "" - tagged = "" - untagged = "" - self.updates_cmd.append("interface %s" % ifname) - if tagged_vlans: - vlan_targed_list = self.vlan_range_to_list(tagged_vlans) - vlan_targed_map = self.vlan_list_to_bitmap(vlan_targed_list) - if untagged_vlans: - vlan_untarged_list = self.vlan_range_to_list(untagged_vlans) - vlan_untarged_map = self.vlan_list_to_bitmap(vlan_untarged_list) - if self.state == "present": - if self.intf_info["linkType"] == "hybrid": - if pvid_vlan and self.intf_info["pvid"] != pvid_vlan: - self.updates_cmd.append( - "port hybrid pvid vlan %s" % pvid_vlan) - pvid = pvid_vlan - change = True - if tagged_vlans: - add_vlans = self.vlan_bitmap_add( - self.intf_info["trunkVlans"], vlan_targed_map) - if not is_vlan_bitmap_empty(add_vlans): - self.updates_cmd.append( - "port hybrid tagged vlan %s" - % tagged_vlans.replace(',', ' ').replace('-', ' to ')) - tagged = "%s:%s" % (add_vlans, add_vlans) - change = True - if untagged_vlans: - add_vlans = self.vlan_bitmap_add( - self.intf_info["untagVlans"], vlan_untarged_map) - if not is_vlan_bitmap_empty(add_vlans): - self.updates_cmd.append( - "port hybrid untagged vlan %s" - % untagged_vlans.replace(',', ' ').replace('-', ' to ')) - untagged = "%s:%s" % (add_vlans, add_vlans) - change = True - if pvid or tagged or untagged: - xmlstr += CE_NC_SET_PORT % (ifname, "hybrid", pvid, tagged, untagged) - if not pvid: - xmlstr = xmlstr.replace("", "") - if not tagged: - xmlstr = xmlstr.replace("", "") - if not untagged: - xmlstr = xmlstr.replace("", "") - else: - self.updates_cmd.append("port link-type hybrid") - change = True - if pvid_vlan: - self.updates_cmd.append( - "port hybrid pvid vlan %s" % pvid_vlan) - pvid = pvid_vlan - if tagged_vlans: - self.updates_cmd.append( - "port hybrid tagged vlan %s" - % tagged_vlans.replace(',', ' ').replace('-', ' to ')) - tagged = "%s:%s" % (vlan_targed_map, vlan_targed_map) - if untagged_vlans: - self.updates_cmd.append( - "port hybrid untagged vlan %s" - % untagged_vlans.replace(',', ' ').replace('-', ' to ')) - untagged = "%s:%s" % (vlan_untarged_map, vlan_untarged_map) - if pvid or tagged or untagged: - xmlstr += CE_NC_SET_PORT % (ifname, "hybrid", pvid, tagged, untagged) - if not pvid: - xmlstr = xmlstr.replace("", "") - if not tagged: - xmlstr = xmlstr.replace("", "") - if not untagged: - xmlstr = xmlstr.replace("", "") - if not pvid_vlan and not tagged_vlans and not untagged_vlans: - xmlstr += CE_NC_SET_PORT_MODE % (ifname, "hybrid") - self.updates_cmd.append( - "undo port hybrid untagged vlan 1") - elif self.state == "absent": - if self.intf_info["linkType"] == "hybrid": - if pvid_vlan and self.intf_info["pvid"] == pvid_vlan and pvid_vlan != '1': - self.updates_cmd.append( - "undo port hybrid pvid vlan %s" % pvid_vlan) - pvid = "1" - change = True - if tagged_vlans: - del_vlans = self.vlan_bitmap_del( - self.intf_info["trunkVlans"], vlan_targed_map) - if not is_vlan_bitmap_empty(del_vlans): - self.updates_cmd.append( - "undo port hybrid tagged vlan %s" - % tagged_vlans.replace(',', ' ').replace('-', ' to ')) - undo_map = vlan_bitmap_undo(del_vlans) - tagged = "%s:%s" % (undo_map, del_vlans) - change = True - if untagged_vlans: - del_vlans = self.vlan_bitmap_del( - self.intf_info["untagVlans"], vlan_untarged_map) - if not is_vlan_bitmap_empty(del_vlans): - self.updates_cmd.append( - "undo port hybrid untagged vlan %s" - % untagged_vlans.replace(',', ' ').replace('-', ' to ')) - undo_map = vlan_bitmap_undo(del_vlans) - untagged = "%s:%s" % (undo_map, del_vlans) - change = True - if pvid or tagged or untagged: - xmlstr += CE_NC_SET_PORT % (ifname, "hybrid", pvid, tagged, untagged) - if not pvid: - xmlstr = xmlstr.replace("", "") - if not tagged: - xmlstr = xmlstr.replace("", "") - if not untagged: - xmlstr = xmlstr.replace("", "") - - if not change: - self.updates_cmd.pop() - return - - conf_str = "" + xmlstr + "" - rcv_xml = set_nc_config(self.module, conf_str) - self.check_response(rcv_xml, "MERGE_HYBRID_PORT") - self.changed = True - - def merge_dot1qtunnel_vlan(self, ifname, default_vlan): - """Merge dot1qtunnel""" - - change = False - conf_str = "" - - self.updates_cmd.append("interface %s" % ifname) - if self.state == "present": - if self.intf_info["linkType"] == "dot1qtunnel": - if default_vlan and self.intf_info["pvid"] != default_vlan: - self.updates_cmd.append( - "port default vlan %s" % default_vlan) - conf_str = CE_NC_SET_PORT % (ifname, "dot1qtunnel", default_vlan, "", "") - change = True - else: - self.updates_cmd.append("port link-type dot1qtunnel") - if default_vlan: - self.updates_cmd.append( - "port default vlan %s" % default_vlan) - conf_str = CE_NC_SET_PORT % (ifname, "dot1qtunnel", default_vlan, "", "") - else: - conf_str = CE_NC_SET_PORT % (ifname, "dot1qtunnel", "1", "", "") - change = True - elif self.state == "absent": - if self.intf_info["linkType"] == "dot1qtunnel": - if default_vlan and self.intf_info["pvid"] == default_vlan and default_vlan != "1": - self.updates_cmd.append( - "undo port default vlan %s" % default_vlan) - conf_str = CE_NC_SET_PORT % (ifname, "dot1qtunnel", "1", "", "") - change = True - if not change: - self.updates_cmd.pop() # remove interface - return - conf_str = "" + conf_str + "" - rcv_xml = set_nc_config(self.module, conf_str) - self.check_response(rcv_xml, "MERGE_DOT1QTUNNEL_PORT") - self.changed = True - - def default_switchport(self, ifname): - """Set interface default or unconfigured""" - - change = False - if self.intf_info["linkType"] != "access": - self.updates_cmd.append("interface %s" % ifname) - self.updates_cmd.append("port link-type access") - self.updates_cmd.append("port default vlan 1") - change = True - else: - if self.intf_info["pvid"] != "1": - self.updates_cmd.append("interface %s" % ifname) - self.updates_cmd.append("port default vlan 1") - change = True - - if not change: - return - - conf_str = CE_NC_SET_DEFAULT_PORT % ifname - rcv_xml = set_nc_config(self.module, conf_str) - self.check_response(rcv_xml, "DEFAULT_INTF_VLAN") - self.changed = True - - def vlan_series(self, vlanid_s): - """ convert vlan range to vlan list """ - - vlan_list = [] - peerlistlen = len(vlanid_s) - if peerlistlen != 2: - self.module.fail_json(msg='Error: Format of vlanid is invalid.') - for num in range(peerlistlen): - if not vlanid_s[num].isdigit(): - self.module.fail_json( - msg='Error: Format of vlanid is invalid.') - if int(vlanid_s[0]) > int(vlanid_s[1]): - self.module.fail_json(msg='Error: Format of vlanid is invalid.') - elif int(vlanid_s[0]) == int(vlanid_s[1]): - vlan_list.append(str(vlanid_s[0])) - return vlan_list - for num in range(int(vlanid_s[0]), int(vlanid_s[1])): - vlan_list.append(str(num)) - vlan_list.append(vlanid_s[1]) - - return vlan_list - - def vlan_region(self, vlanid_list): - """ convert vlan range to vlan list """ - - vlan_list = [] - peerlistlen = len(vlanid_list) - for num in range(peerlistlen): - if vlanid_list[num].isdigit(): - vlan_list.append(vlanid_list[num]) - else: - vlan_s = self.vlan_series(vlanid_list[num].split('-')) - vlan_list.extend(vlan_s) - - return vlan_list - - def vlan_range_to_list(self, vlan_range): - """ convert vlan range to vlan list """ - - vlan_list = self.vlan_region(vlan_range.split(',')) - - return vlan_list - - def vlan_list_to_bitmap(self, vlanlist): - """ convert vlan list to vlan bitmap """ - - vlan_bit = ['0'] * 1024 - bit_int = [0] * 1024 - - vlan_list_len = len(vlanlist) - for num in range(vlan_list_len): - tagged_vlans = int(vlanlist[num]) - if tagged_vlans <= 0 or tagged_vlans > 4094: - self.module.fail_json( - msg='Error: Vlan id is not in the range from 1 to 4094.') - j = tagged_vlans // 4 - bit_int[j] |= 0x8 >> (tagged_vlans % 4) - vlan_bit[j] = hex(bit_int[j])[2] - - vlan_xml = ''.join(vlan_bit) - - return vlan_xml - - def vlan_bitmap_add(self, oldmap, newmap): - """vlan add bitmap""" - - vlan_bit = ['0'] * 1024 - - if len(newmap) != 1024: - self.module.fail_json(msg='Error: New vlan bitmap is invalid.') - - if len(oldmap) != 1024 and len(oldmap) != 0: - self.module.fail_json(msg='Error: old vlan bitmap is invalid.') - - if len(oldmap) == 0: - return newmap - - for num in range(1024): - new_tmp = int(newmap[num], 16) - old_tmp = int(oldmap[num], 16) - add = (~(new_tmp & old_tmp)) & new_tmp - vlan_bit[num] = hex(add)[2] - - vlan_xml = ''.join(vlan_bit) - - return vlan_xml - - def vlan_bitmap_del(self, oldmap, delmap): - """vlan del bitmap""" - - vlan_bit = ['0'] * 1024 - - if not oldmap or len(oldmap) == 0: - return ''.join(vlan_bit) - - if len(oldmap) != 1024 or len(delmap) != 1024: - self.module.fail_json(msg='Error: vlan bitmap is invalid.') - - for num in range(1024): - tmp = int(delmap[num], 16) & int(oldmap[num], 16) - vlan_bit[num] = hex(tmp)[2] - - vlan_xml = ''.join(vlan_bit) - - return vlan_xml - - def check_params(self): - """Check all input params""" - - # interface type check - if self.interface: - self.intf_type = get_interface_type(self.interface) - if not self.intf_type: - self.module.fail_json( - msg='Error: Interface name of %s is error.' % self.interface) - - if not self.intf_type or not is_portswitch_enalbed(self.intf_type): - self.module.fail_json(msg='Error: Interface %s is error.') - - # check default_vlan - if self.default_vlan: - if not self.default_vlan.isdigit(): - self.module.fail_json(msg='Error: Access vlan id is invalid.') - if int(self.default_vlan) <= 0 or int(self.default_vlan) > 4094: - self.module.fail_json( - msg='Error: Access vlan id is not in the range from 1 to 4094.') - - # check pvid_vlan - if self.pvid_vlan: - if not self.pvid_vlan.isdigit(): - self.module.fail_json(msg='Error: Pvid vlan id is invalid.') - if int(self.pvid_vlan) <= 0 or int(self.pvid_vlan) > 4094: - self.module.fail_json( - msg='Error: Pvid vlan id is not in the range from 1 to 4094.') - - # get interface info - self.intf_info = self.get_interface_dict(self.interface) - if not self.intf_info: - self.module.fail_json(msg='Error: Interface does not exist.') - - if not self.is_l2switchport(): - self.module.fail_json( - msg='Error: Interface is not layer2 switch port.') - if self.state == "unconfigured": - if any([self.mode, self.default_vlan, self.pvid_vlan, self.trunk_vlans, self.untagged_vlans, self.tagged_vlans]): - self.module.fail_json( - msg='Error: When state is unconfigured, only interface name exists.') - else: - if self.mode == "access": - if any([self.pvid_vlan, self.trunk_vlans, self.untagged_vlans, self.tagged_vlans]): - self.module.fail_json( - msg='Error: When mode is access, only default_vlan can be supported.') - elif self.mode == "trunk": - if any([self.default_vlan, self.untagged_vlans, self.tagged_vlans]): - self.module.fail_json( - msg='Error: When mode is trunk, only pvid_vlan and trunk_vlans can exist.') - elif self.mode == "hybrid": - if any([self.default_vlan, self.trunk_vlans]): - self.module.fail_json( - msg='Error: When mode is hybrid, default_vlan and trunk_vlans cannot exist') - else: - if any([self.pvid_vlan, self.trunk_vlans, self.untagged_vlans, self.tagged_vlans]): - self.module.fail_json( - msg='Error: When mode is dot1qtunnel, only default_vlan can be supported.') - - def get_proposed(self): - """get proposed info""" - - self.proposed['state'] = self.state - self.proposed['interface'] = self.interface - self.proposed['mode'] = self.mode - if self.mode: - if self.mode == "access": - self.proposed['access_pvid'] = self.default_vlan - elif self.mode == "trunk": - self.proposed['pvid_vlan'] = self.pvid_vlan - self.proposed['trunk_vlans'] = self.trunk_vlans - elif self.mode == "hybrid": - self.proposed['pvid_vlan'] = self.pvid_vlan - self.proposed['untagged_vlans'] = self.untagged_vlans - self.proposed['tagged_vlans'] = self.tagged_vlans - else: - self.proposed['dot1qtunnel_pvid'] = self.default_vlan - - def get_existing(self): - """get existing info""" - - if self.intf_info: - self.existing["interface"] = self.intf_info["ifName"] - self.existing["switchport"] = self.intf_info["l2Enable"] - self.existing["mode"] = self.intf_info["linkType"] - if self.intf_info["linkType"] == "access": - self.existing['access_pvid'] = self.intf_info["pvid"] - elif self.intf_info["linkType"] == "trunk": - self.existing['trunk_pvid'] = self.intf_info["pvid"] - self.existing['trunk_vlans'] = self.intf_info["trunkVlans"] - elif self.intf_info["linkType"] == "hybrid": - self.existing['hybrid_pvid'] = self.intf_info["pvid"] - self.existing['hybrid_untagged_vlans'] = self.intf_info["untagVlans"] - self.existing['hybrid_tagged_vlans'] = self.intf_info["trunkVlans"] - else: - self.existing['dot1qtunnel_pvid'] = self.intf_info["pvid"] - - def get_end_state(self): - """get end state info""" - - end_info = self.get_interface_dict(self.interface) - if end_info: - self.end_state["interface"] = end_info["ifName"] - self.end_state["switchport"] = end_info["l2Enable"] - self.end_state["mode"] = end_info["linkType"] - if end_info["linkType"] == "access": - self.end_state['access_pvid'] = end_info["pvid"] - elif end_info["linkType"] == "trunk": - self.end_state['trunk_pvid'] = end_info["pvid"] - self.end_state['trunk_vlans'] = end_info["trunkVlans"] - elif end_info["linkType"] == "hybrid": - self.end_state['hybrid_pvid'] = end_info["pvid"] - self.end_state['hybrid_untagged_vlans'] = end_info["untagVlans"] - self.end_state['hybrid_tagged_vlans'] = end_info["trunkVlans"] - else: - self.end_state['dot1qtunnel_pvid'] = end_info["pvid"] - if self.end_state == self.existing: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - if not self.intf_info: - self.module.fail_json(msg='Error: interface does not exist.') - self.get_existing() - self.get_proposed() - - # present or absent - if self.state == "present" or self.state == "absent": - if self.mode == "access": - self.merge_access_vlan(self.interface, self.default_vlan) - elif self.mode == "trunk": - self.merge_trunk_vlan( - self.interface, self.pvid_vlan, self.trunk_vlans) - elif self.mode == "hybrid": - self.merge_hybrid_vlan(self.interface, self.pvid_vlan, self.tagged_vlans, self.untagged_vlans) - else: - self.merge_dot1qtunnel_vlan(self.interface, self.default_vlan) - - # unconfigured - else: - self.default_switchport(self.interface) - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - interface=dict(required=True, type='str'), - mode=dict(choices=['access', 'trunk', 'dot1qtunnel', 'hybrid'], required=False), - default_vlan=dict(type='str', required=False), - pvid_vlan=dict(type='str', required=False), - trunk_vlans=dict(type='str', required=False), - untagged_vlans=dict(type='str', required=False), - tagged_vlans=dict(type='str', required=False), - state=dict(choices=['absent', 'present', 'unconfigured'], - default='present') - ) - - argument_spec.update(ce_argument_spec) - switchport = SwitchPort(argument_spec) - switchport.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vlan.py b/plugins/modules/network/cloudengine/ce_vlan.py deleted file mode 100644 index 05d109456a..0000000000 --- a/plugins/modules/network/cloudengine/ce_vlan.py +++ /dev/null @@ -1,691 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vlan -short_description: Manages VLAN resources and attributes on Huawei CloudEngine switches. -description: - - Manages VLAN configurations on Huawei CloudEngine switches. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - vlan_id: - description: - - Single VLAN ID, in the range from 1 to 4094. - vlan_range: - description: - - Range of VLANs such as C(2-10) or C(2,5,10-15), etc. - name: - description: - - Name of VLAN, minimum of 1 character, maximum of 31 characters. - description: - description: - - Specify VLAN description, minimum of 1 character, maximum of 80 characters. - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: vlan module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Ensure a range of VLANs are not present on the switch - ce_vlan: - vlan_range: "2-10,20,50,55-60,100-150" - state: absent - provider: "{{ cli }}" - - - name: Ensure VLAN 50 exists with the name WEB - ce_vlan: - vlan_id: 50 - name: WEB - state: absent - provider: "{{ cli }}" - - - name: Ensure VLAN is NOT on the device - ce_vlan: - vlan_id: 50 - state: absent - provider: "{{ cli }}" - -''' - -RETURN = ''' -proposed_vlans_list: - description: list of VLANs being proposed - returned: always - type: list - sample: ["100"] -existing_vlans_list: - description: list of existing VLANs on the switch prior to making changes - returned: always - type: list - sample: ["1", "2", "3", "4", "5", "20"] -end_state_vlans_list: - description: list of VLANs after the module is executed - returned: always - type: list - sample: ["1", "2", "3", "4", "5", "20", "100"] -proposed: - description: k/v pairs of parameters passed into module (does not include - vlan_id or vlan_range) - returned: always - type: dict - sample: {"vlan_id":"20", "name": "VLAN_APP", "description": "vlan for app" } -existing: - description: k/v pairs of existing vlan or null when using vlan_range - returned: always - type: dict - sample: {"vlan_id":"20", "name": "VLAN_APP", "description": "" } -end_state: - description: k/v pairs of the VLAN after executing module or null - when using vlan_range - returned: always - type: dict - sample: {"vlan_id":"20", "name": "VLAN_APP", "description": "vlan for app" } -updates: - description: command string sent to the device - returned: always - type: list - sample: ["vlan 20", "name VLAN20"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, execute_nc_action, ce_argument_spec - -CE_NC_CREATE_VLAN = """ - - - - - %s - %s - %s - - - - - - -""" - -CE_NC_DELETE_VLAN = """ - - - - - %s - - - - -""" - -CE_NC_MERGE_VLAN_DES = """ - - - - - %s - %s - - - - - - -""" - -CE_NC_MERGE_VLAN_NAME = """ - - - - - %s - %s - - - - - - -""" - - -CE_NC_MERGE_VLAN = """ - - - - - %s - %s - %s - - - - - - -""" - -CE_NC_GET_VLAN = """ - - - - - %s - - - - - - -""" - -CE_NC_GET_VLANS = """ - - - - - - - - - - -""" - -CE_NC_CREATE_VLAN_BATCH = """ - - - - %s:%s - - - -""" - -CE_NC_DELETE_VLAN_BATCH = """ - - - - %s:%s - - - -""" - - -class Vlan(object): - """ - Manages VLAN resources and attributes - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # vlan config info - self.vlan_id = self.module.params['vlan_id'] - self.vlan_range = self.module.params['vlan_range'] - self.name = self.module.params['name'] - self.description = self.module.params['description'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.vlan_exist = False - self.vlan_attr_exist = None - self.vlans_list_exist = list() - self.vlans_list_change = list() - self.updates_cmd = list() - self.results = dict() - self.vlan_attr_end = dict() - - def init_module(self): - """ - init ansible NetworkModule. - """ - - required_one_of = [["vlan_id", "vlan_range"]] - mutually_exclusive = [["vlan_id", "vlan_range"]] - - self.module = AnsibleModule( - argument_spec=self.spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def config_vlan(self, vlan_id, name='', description=''): - """Create vlan.""" - - if name is None: - name = '' - if description is None: - description = '' - - conf_str = CE_NC_CREATE_VLAN % (vlan_id, name, description) - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "CREATE_VLAN") - self.changed = True - - def merge_vlan(self, vlan_id, name, description): - """Merge vlan.""" - - conf_str = None - - if not name and description: - conf_str = CE_NC_MERGE_VLAN_DES % (vlan_id, description) - if not description and name: - conf_str = CE_NC_MERGE_VLAN_NAME % (vlan_id, name) - if description and name: - conf_str = CE_NC_MERGE_VLAN % (vlan_id, name, description) - - if not conf_str: - return - - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "MERGE_VLAN") - self.changed = True - - def create_vlan_batch(self, vlan_list): - """Create vlan batch.""" - - if not vlan_list: - return - - vlan_bitmap = self.vlan_list_to_bitmap(vlan_list) - xmlstr = CE_NC_CREATE_VLAN_BATCH % (vlan_bitmap, vlan_bitmap) - - recv_xml = execute_nc_action(self.module, xmlstr) - self.check_response(recv_xml, "CREATE_VLAN_BATCH") - self.updates_cmd.append('vlan batch %s' % ( - self.vlan_range.replace(',', ' ').replace('-', ' to '))) - self.changed = True - - def delete_vlan_batch(self, vlan_list): - """Delete vlan batch.""" - - if not vlan_list: - return - - vlan_bitmap = self.vlan_list_to_bitmap(vlan_list) - xmlstr = CE_NC_DELETE_VLAN_BATCH % (vlan_bitmap, vlan_bitmap) - - recv_xml = execute_nc_action(self.module, xmlstr) - self.check_response(recv_xml, "DELETE_VLAN_BATCH") - self.updates_cmd.append('undo vlan batch %s' % ( - self.vlan_range.replace(',', ' ').replace('-', ' to '))) - self.changed = True - - def undo_config_vlan(self, vlanid): - """Delete vlan.""" - - conf_str = CE_NC_DELETE_VLAN % vlanid - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "DELETE_VLAN") - self.changed = True - self.updates_cmd.append('undo vlan %s' % self.vlan_id) - - def get_vlan_attr(self, vlan_id): - """ get vlan attributes.""" - - conf_str = CE_NC_GET_VLAN % vlan_id - xml_str = get_nc_config(self.module, conf_str) - attr = dict() - - if "" in xml_str: - return attr - else: - re_find_id = re.findall(r'.*(.*).*\s*', xml_str) - re_find_name = re.findall(r'.*(.*).*\s*', xml_str) - re_find_desc = re.findall(r'.*(.*).*\s*', xml_str) - - if re_find_id: - if re_find_name: - attr = dict(vlan_id=re_find_id[0], name=re_find_name[0], - description=re_find_desc[0]) - else: - attr = dict(vlan_id=re_find_id[0], name=None, - description=re_find_desc[0]) - return attr - - def get_vlans_name(self): - """ get all vlan vid and its name list, - sample: [ ("20", "VLAN_NAME_20"), ("30", "VLAN_NAME_30") ]""" - - conf_str = CE_NC_GET_VLANS - xml_str = get_nc_config(self.module, conf_str) - vlan_list = list() - - if "" in xml_str: - return vlan_list - else: - vlan_list = re.findall( - r'.*(.*).*\s*(.*).*', xml_str) - return vlan_list - - def get_vlans_list(self): - """ get all vlan vid list, sample: [ "20", "30", "31" ]""" - - conf_str = CE_NC_GET_VLANS - xml_str = get_nc_config(self.module, conf_str) - vlan_list = list() - - if "" in xml_str: - return vlan_list - else: - vlan_list = re.findall( - r'.*(.*).*', xml_str) - return vlan_list - - def vlan_series(self, vlanid_s): - """ convert vlan range to list """ - - vlan_list = [] - peerlistlen = len(vlanid_s) - if peerlistlen != 2: - self.module.fail_json(msg='Error: Format of vlanid is invalid.') - for num in range(peerlistlen): - if not vlanid_s[num].isdigit(): - self.module.fail_json( - msg='Error: Format of vlanid is invalid.') - if int(vlanid_s[0]) > int(vlanid_s[1]): - self.module.fail_json(msg='Error: Format of vlanid is invalid.') - elif int(vlanid_s[0]) == int(vlanid_s[1]): - vlan_list.append(str(vlanid_s[0])) - return vlan_list - for num in range(int(vlanid_s[0]), int(vlanid_s[1])): - vlan_list.append(str(num)) - vlan_list.append(vlanid_s[1]) - - return vlan_list - - def vlan_region(self, vlanid_list): - """ convert vlan range to vlan list """ - - vlan_list = [] - peerlistlen = len(vlanid_list) - for num in range(peerlistlen): - if vlanid_list[num].isdigit(): - vlan_list.append(vlanid_list[num]) - else: - vlan_s = self.vlan_series(vlanid_list[num].split('-')) - vlan_list.extend(vlan_s) - - return vlan_list - - def vlan_range_to_list(self, vlan_range): - """ convert vlan range to vlan list """ - - vlan_list = self.vlan_region(vlan_range.split(',')) - - return vlan_list - - def vlan_list_to_bitmap(self, vlanlist): - """ convert vlan list to vlan bitmap """ - - vlan_bit = ['0'] * 1024 - bit_int = [0] * 1024 - - vlan_list_len = len(vlanlist) - for num in range(vlan_list_len): - tagged_vlans = int(vlanlist[num]) - if tagged_vlans <= 0 or tagged_vlans > 4094: - self.module.fail_json( - msg='Error: Vlan id is not in the range from 1 to 4094.') - j = tagged_vlans // 4 - bit_int[j] |= 0x8 >> (tagged_vlans % 4) - vlan_bit[j] = hex(bit_int[j])[2] - - vlan_xml = ''.join(vlan_bit) - - return vlan_xml - - def check_params(self): - """Check all input params""" - - if not self.vlan_id and self.description: - self.module.fail_json( - msg='Error: Vlan description could be set only at one vlan.') - - if not self.vlan_id and self.name: - self.module.fail_json( - msg='Error: Vlan name could be set only at one vlan.') - - # check vlan id - if self.vlan_id: - if not self.vlan_id.isdigit(): - self.module.fail_json( - msg='Error: Vlan id is not digit.') - if int(self.vlan_id) <= 0 or int(self.vlan_id) > 4094: - self.module.fail_json( - msg='Error: Vlan id is not in the range from 1 to 4094.') - - # check vlan description - if self.description: - if len(self.description) > 81 or len(self.description.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: vlan description is not in the range from 1 to 80.') - - # check vlan name - if self.name: - if len(self.name) > 31 or len(self.name.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: Vlan name is not in the range from 1 to 31.') - - def get_proposed(self): - """ - get proposed config. - """ - - if self.vlans_list_change: - if self.state == 'present': - proposed_vlans_tmp = list(self.vlans_list_change) - proposed_vlans_tmp.extend(self.vlans_list_exist) - self.results['proposed_vlans_list'] = list( - set(proposed_vlans_tmp)) - else: - self.results['proposed_vlans_list'] = list( - set(self.vlans_list_exist) - set(self.vlans_list_change)) - self.results['proposed_vlans_list'].sort() - else: - self.results['proposed_vlans_list'] = self.vlans_list_exist - - if self.vlan_id: - if self.state == "present": - self.results['proposed'] = dict( - vlan_id=self.vlan_id, - name=self.name, - description=self.description - ) - else: - self.results['proposed'] = None - else: - self.results['proposed'] = None - - def get_existing(self): - """ - get existing config. - """ - - self.results['existing_vlans_list'] = self.vlans_list_exist - - if self.vlan_id: - if self.vlan_attr_exist: - self.results['existing'] = dict( - vlan_id=self.vlan_attr_exist['vlan_id'], - name=self.vlan_attr_exist['name'], - description=self.vlan_attr_exist['description'] - ) - else: - self.results['existing'] = None - else: - self.results['existing'] = None - - def get_end_state(self): - """ - get end state config. - """ - - self.results['end_state_vlans_list'] = self.get_vlans_list() - - if self.vlan_id: - if self.vlan_attr_end: - self.results['end_state'] = dict( - vlan_id=self.vlan_attr_end['vlan_id'], - name=self.vlan_attr_end['name'], - description=self.vlan_attr_end['description'] - ) - else: - self.results['end_state'] = None - - else: - self.results['end_state'] = None - - def work(self): - """ - worker. - """ - - # check param - self.check_params() - - # get all vlan info - self.vlans_list_exist = self.get_vlans_list() - - # get vlan attributes - if self.vlan_id: - self.vlans_list_change.append(self.vlan_id) - self.vlan_attr_exist = self.get_vlan_attr(self.vlan_id) - if self.vlan_attr_exist: - self.vlan_exist = True - - if self.vlan_range: - new_vlans_tmp = self.vlan_range_to_list(self.vlan_range) - if self.state == 'present': - self.vlans_list_change = list( - set(new_vlans_tmp) - set(self.vlans_list_exist)) - else: - self.vlans_list_change = [ - val for val in new_vlans_tmp if val in self.vlans_list_exist] - - if self.state == 'present': - if self.vlan_id: - if not self.vlan_exist: - # create a new vlan - self.config_vlan(self.vlan_id, self.name, self.description) - elif self.description and self.description != self.vlan_attr_exist['description']: - # merge vlan description - self.merge_vlan(self.vlan_id, self.name, self.description) - elif self.name and self.name != self.vlan_attr_exist['name']: - # merge vlan name - self.merge_vlan(self.vlan_id, self.name, self.description) - - # update command for results - if self.changed: - self.updates_cmd.append('vlan %s' % self.vlan_id) - if self.name: - self.updates_cmd.append('name %s' % self.name) - if self.description: - self.updates_cmd.append( - 'description %s' % self.description) - elif self.vlan_range and self.vlans_list_change: - self.create_vlan_batch(self.vlans_list_change) - else: # absent - if self.vlan_id: - if self.vlan_exist: - # delete the vlan - self.undo_config_vlan(self.vlan_id) - elif self.vlan_range and self.vlans_list_change: - self.delete_vlan_batch(self.vlans_list_change) - - # result - if self.vlan_id: - self.vlan_attr_end = self.get_vlan_attr(self.vlan_id) - - self.get_existing() - self.get_proposed() - self.get_end_state() - - self.results['changed'] = self.changed - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """ module main """ - - argument_spec = dict( - vlan_id=dict(required=False), - vlan_range=dict(required=False, type='str'), - name=dict(required=False, type='str'), - description=dict(required=False, type='str'), - state=dict(choices=['absent', 'present'], - default='present', required=False), - ) - - argument_spec.update(ce_argument_spec) - vlancfg = Vlan(argument_spec) - vlancfg.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vrf.py b/plugins/modules/network/cloudengine/ce_vrf.py deleted file mode 100644 index 7586e03292..0000000000 --- a/plugins/modules/network/cloudengine/ce_vrf.py +++ /dev/null @@ -1,356 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vrf -short_description: Manages VPN instance on HUAWEI CloudEngine switches. -description: - - Manages VPN instance of HUAWEI CloudEngine switches. -author: Yang yang (@QijunPan) -notes: - - If I(state=absent), the route will be removed, regardless of the non-required options. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - vrf: - description: - - VPN instance, the length of vrf name is 1 - 31, i.e. "test", but can not be C(_public_). - required: true - description: - description: - - Description of the vrf, the string length is 1 - 242 . - state: - description: - - Manage the state of the resource. - choices: ['present','absent'] - default: present -''' - -EXAMPLES = ''' -- name: vrf module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Config a vpn install named vpna, description is test - ce_vrf: - vrf: vpna - description: test - state: present - provider: "{{ cli }}" - - name: Delete a vpn install named vpna - ce_vrf: - vrf: vpna - state: absent - provider: "{{ cli }}" -''' -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"vrf": "vpna", - "description": "test", - "state": "present"} -existing: - description: k/v pairs of existing switchport - returned: always - type: dict - sample: {} -end_state: - description: k/v pairs of switchport after module execution - returned: always - type: dict - sample: {"vrf": "vpna", - "description": "test", - "present": "present"} -updates: - description: command list sent to the device - returned: always - type: list - sample: ["ip vpn-instance vpna", - "description test"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_VRF = """ - - - - - - - - - - - - -""" - -CE_NC_CREATE_VRF = """ - - - - - %s - %s - - - - -""" - -CE_NC_DELETE_VRF = """ - - - - - %s - %s - - - - -""" - - -def build_config_xml(xmlstr): - """build_config_xml""" - - return ' ' + xmlstr + ' ' - - -class Vrf(object): - """Manage vpn instance""" - - def __init__(self, argument_spec, ): - self.spec = argument_spec - self.module = None - self.init_module() - - # vpn instance info - self.vrf = self.module.params['vrf'] - self.description = self.module.params['description'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """init_module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def set_update_cmd(self): - """ set update command""" - if not self.changed: - return - if self.state == "present": - self.updates_cmd.append('ip vpn-instance %s' % (self.vrf)) - if self.description: - self.updates_cmd.append('description %s' % (self.description)) - else: - self.updates_cmd.append('undo ip vpn-instance %s' % (self.vrf)) - - def get_vrf(self): - """ check if vrf is need to change""" - - getxmlstr = CE_NC_GET_VRF - xml_str = get_nc_config(self.module, getxmlstr) - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - vpn_instances = root.findall( - "l3vpn/l3vpncomm/l3vpnInstances/l3vpnInstance") - if vpn_instances: - for vpn_instance in vpn_instances: - if vpn_instance.find('vrfName').text == self.vrf: - if vpn_instance.find('vrfDescription').text == self.description: - if self.state == "present": - return False - else: - return True - else: - return True - return self.state == "present" - else: - return self.state == "present" - - def check_params(self): - """Check all input params""" - - # vrf and description check - if self.vrf == '_public_': - self.module.fail_json( - msg='Error: The vrf name _public_ is reserved.') - if len(self.vrf) < 1 or len(self.vrf) > 31: - self.module.fail_json( - msg='Error: The vrf name length must between 1 and 242.') - if self.description: - if len(self.description) < 1 or len(self.description) > 242: - self.module.fail_json( - msg='Error: The vrf description length must between 1 and 242.') - - def operate_vrf(self): - """config/delete vrf""" - if not self.changed: - return - if self.state == "present": - if self.description is None: - configxmlstr = CE_NC_CREATE_VRF % (self.vrf, '') - else: - configxmlstr = CE_NC_CREATE_VRF % (self.vrf, self.description) - else: - configxmlstr = CE_NC_DELETE_VRF % (self.vrf, self.description) - - conf_str = build_config_xml(configxmlstr) - - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "OPERATE_VRF") - - def get_proposed(self): - """get_proposed""" - - if self.state == 'present': - self.proposed['vrf'] = self.vrf - if self.description: - self.proposed['description'] = self.description - - else: - self.proposed = dict() - self.proposed['state'] = self.state - - def get_existing(self): - """get_existing""" - - change = self.get_vrf() - if change: - if self.state == 'present': - self.existing = dict() - else: - self.existing['vrf'] = self.vrf - if self.description: - self.existing['description'] = self.description - self.changed = True - else: - if self.state == 'absent': - self.existing = dict() - else: - self.existing['vrf'] = self.vrf - if self.description: - self.existing['description'] = self.description - self.changed = False - - def get_end_state(self): - """get_end_state""" - - change = self.get_vrf() - if not change: - if self.state == 'present': - self.end_state['vrf'] = self.vrf - if self.description: - self.end_state['description'] = self.description - else: - self.end_state = dict() - else: - if self.state == 'present': - self.end_state = dict() - else: - self.end_state['vrf'] = self.vrf - if self.description: - self.end_state['description'] = self.description - - def work(self): - """worker""" - - self.check_params() - self.get_existing() - self.get_proposed() - self.operate_vrf() - self.set_update_cmd() - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """main""" - - argument_spec = dict( - vrf=dict(required=True, type='str'), - description=dict(required=False, type='str'), - state=dict(choices=['absent', 'present'], - default='present', required=False), - ) - argument_spec.update(ce_argument_spec) - interface = Vrf(argument_spec) - interface.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vrf_af.py b/plugins/modules/network/cloudengine/ce_vrf_af.py deleted file mode 100644 index 562ffd800a..0000000000 --- a/plugins/modules/network/cloudengine/ce_vrf_af.py +++ /dev/null @@ -1,851 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} -DOCUMENTATION = ''' ---- -module: ce_vrf_af -short_description: Manages VPN instance address family on HUAWEI CloudEngine switches. -description: - - Manages VPN instance address family of HUAWEI CloudEngine switches. -author: Yang yang (@QijunPan) -notes: - - If I(state=absent), the vrf will be removed, regardless of the non-required parameters. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - vrf: - description: - - VPN instance. - required: true - vrf_aftype: - description: - - VPN instance address family. - choices: ['v4','v6'] - default: v4 - route_distinguisher: - description: - - VPN instance route distinguisher,the RD used to distinguish same route prefix from different vpn. - The RD must be setted before setting vpn_target_value. - vpn_target_state: - description: - - Manage the state of the vpn target. - choices: ['present','absent'] - vpn_target_type: - description: - - VPN instance vpn target type. - choices: ['export_extcommunity', 'import_extcommunity'] - vpn_target_value: - description: - - VPN instance target value. Such as X.X.X.X:number<0-65535> or number<0-65535>:number<0-4294967295> - or number<0-65535>.number<0-65535>:number<0-65535> or number<65536-4294967295>:number<0-65535> - but not support 0:0 and 0.0:0. - evpn: - description: - - Is extend vpn or normal vpn. - type: bool - default: 'no' - state: - description: - - Manage the state of the af. - choices: ['present','absent'] - default: present -''' - -EXAMPLES = ''' -- name: vrf af module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Config vpna, set address family is ipv4 - ce_vrf_af: - vrf: vpna - vrf_aftype: v4 - state: present - provider: "{{ cli }}" - - name: Config vpna, delete address family is ipv4 - ce_vrf_af: - vrf: vpna - vrf_aftype: v4 - state: absent - provider: "{{ cli }}" - - name: Config vpna, set address family is ipv4,rd=1:1,set vpn_target_type=export_extcommunity,vpn_target_value=2:2 - ce_vrf_af: - vrf: vpna - vrf_aftype: v4 - route_distinguisher: 1:1 - vpn_target_type: export_extcommunity - vpn_target_value: 2:2 - vpn_target_state: present - state: present - provider: "{{ cli }}" - - name: Config vpna, set address family is ipv4,rd=1:1,delete vpn_target_type=export_extcommunity,vpn_target_value=2:2 - ce_vrf_af: - vrf: vpna - vrf_aftype: v4 - route_distinguisher: 1:1 - vpn_target_type: export_extcommunity - vpn_target_value: 2:2 - vpn_target_state: absent - state: present - provider: "{{ cli }}" -''' -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"vrf": "vpna", - "vrf_aftype": "v4", - "state": "present", - "vpn_targe_state":"absent", - "evpn": "none", - "vpn_target_type": "none", - "vpn_target_value": "none"} -existing: - description: k/v pairs of existing switchport - returned: always - type: dict - sample: { - "route_distinguisher": [ - "1:1", - "2:2" - ], - "vpn_target_type": [], - "vpn_target_value": [], - "vrf": "vpna", - "vrf_aftype": [ - "ipv4uni", - "ipv6uni" - ] - } -end_state: - description: k/v pairs of switchport after module execution - returned: always - type: dict - sample: { - "route_distinguisher": [ - "1:1", - "2:2" - ], - "vpn_target_type": [ - "import_extcommunity", - "3:3" - ], - "vpn_target_value": [], - "vrf": "vpna", - "vrf_aftype": [ - "ipv4uni", - "ipv6uni" - ] - } -updates: - description: command list sent to the device - returned: always - type: list - sample: [ - "ip vpn-instance vpna", - "vpn-target 3:3 import_extcommunity" - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - - -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_VRF = """ - - - - - - - - - - - - -""" - -CE_NC_GET_VRF_AF = """ - - - - - - %s - - - - %s - - - - - - - -""" - -CE_NC_DELETE_VRF_AF = """ - - - - - %s - - - %s - - - - - - -""" - -CE_NC_CREATE_VRF_AF = """ - - - - - %s - - - %s - %s%s - - - - - -""" -CE_NC_CREATE_VRF_TARGET = """ - - - %s - %s - - -""" - -CE_NC_DELETE_VRF_TARGET = """ - - - %s - %s - - -""" - -CE_NC_GET_VRF_TARGET = """ - - - - - - -""" - -CE_NC_CREATE_EXTEND_VRF_TARGET = """ - - - %s - %s - evpn - - -""" - -CE_NC_DELETE_EXTEND_VRF_TARGET = """ - - - %s - %s - evpn - - -""" - -CE_NC_GET_EXTEND_VRF_TARGET = """ - - - - - - - -""" - - -def build_config_xml(xmlstr): - """build_config_xml""" - - return ' ' + xmlstr + ' ' - - -def is_valid_value(vrf_targe_value): - """check if the vrf target value is valid""" - - each_num = None - if len(vrf_targe_value) > 21 or len(vrf_targe_value) < 3: - return False - if vrf_targe_value.find(':') == -1: - return False - elif vrf_targe_value == '0:0': - return False - elif vrf_targe_value == '0.0:0': - return False - else: - value_list = vrf_targe_value.split(':') - if value_list[0].find('.') != -1: - if not value_list[1].isdigit(): - return False - if int(value_list[1]) > 65535: - return False - value = value_list[0].split('.') - if len(value) == 4: - for each_num in value: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - elif len(value) == 2: - for each_num in value: - if not each_num.isdigit(): - return False - if int(each_num) > 65535: - return False - return True - else: - return False - elif not value_list[0].isdigit(): - return False - elif not value_list[1].isdigit(): - return False - elif int(value_list[0]) < 65536 and int(value_list[1]) < 4294967296: - return True - elif int(value_list[0]) > 65535 and int(value_list[0]) < 4294967296: - return bool(int(value_list[1]) < 65536) - else: - return False - - -class VrfAf(object): - """manage the vrf address family and export/import target""" - - def __init__(self, argument_spec, ): - self.spec = argument_spec - self.module = None - self.init_module() - - # vpn instance info - self.vrf = self.module.params['vrf'] - self.vrf_aftype = self.module.params['vrf_aftype'] - if self.vrf_aftype == 'v4': - self.vrf_aftype = 'ipv4uni' - else: - self.vrf_aftype = 'ipv6uni' - self.route_distinguisher = self.module.params['route_distinguisher'] - self.evpn = self.module.params['evpn'] - self.vpn_target_type = self.module.params['vpn_target_type'] - self.vpn_target_value = self.module.params['vpn_target_value'] - self.vpn_target_state = self.module.params['vpn_target_state'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - self.vpn_target_changed = False - self.vrf_af_type_changed = False - self.vrf_rd_changed = False - self.vrf_af_info = dict() - - def init_module(self): - """init_module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def is_vrf_af_exist(self): - """is vrf address family exist""" - - if not self.vrf_af_info: - return False - - for vrf_af_ele in self.vrf_af_info["vpnInstAF"]: - if vrf_af_ele["afType"] == self.vrf_aftype: - return True - else: - continue - return False - - def get_exist_rd(self): - """get exist route distinguisher """ - - if not self.vrf_af_info: - return None - - for vrf_af_ele in self.vrf_af_info["vpnInstAF"]: - if vrf_af_ele["afType"] == self.vrf_aftype: - if vrf_af_ele["vrfRD"] is None: - return None - else: - return vrf_af_ele["vrfRD"] - else: - continue - return None - - def is_vrf_rd_exist(self): - """is vrf route distinguisher exist""" - - if not self.vrf_af_info: - return False - - for vrf_af_ele in self.vrf_af_info["vpnInstAF"]: - if vrf_af_ele["afType"] == self.vrf_aftype: - if vrf_af_ele["vrfRD"] is None: - return False - if self.route_distinguisher is not None: - return bool(vrf_af_ele["vrfRD"] == self.route_distinguisher) - else: - return True - else: - continue - return False - - def is_vrf_rt_exist(self): - """is vpn target exist""" - - if not self.vrf_af_info: - return False - - for vrf_af_ele in self.vrf_af_info["vpnInstAF"]: - if vrf_af_ele["afType"] == self.vrf_aftype: - if self.evpn is False: - if not vrf_af_ele.get("vpnTargets"): - return False - for vpn_target in vrf_af_ele.get("vpnTargets"): - if vpn_target["vrfRTType"] == self.vpn_target_type \ - and vpn_target["vrfRTValue"] == self.vpn_target_value: - return True - else: - continue - else: - if not vrf_af_ele.get("evpnTargets"): - return False - for evpn_target in vrf_af_ele.get("evpnTargets"): - if evpn_target["vrfRTType"] == self.vpn_target_type \ - and evpn_target["vrfRTValue"] == self.vpn_target_value: - return True - else: - continue - else: - continue - return False - - def set_update_cmd(self): - """ set update command""" - if not self.changed: - return - if self.vpn_target_type: - if self.vpn_target_type == "export_extcommunity": - vpn_target_type = "export-extcommunity" - else: - vpn_target_type = "import-extcommunity" - if self.state == "present": - self.updates_cmd.append('ip vpn-instance %s' % (self.vrf)) - if self.vrf_aftype == 'ipv4uni': - self.updates_cmd.append('ipv4-family') - elif self.vrf_aftype == 'ipv6uni': - self.updates_cmd.append('ipv6-family') - if self.route_distinguisher: - if not self.is_vrf_rd_exist(): - self.updates_cmd.append( - 'route-distinguisher %s' % self.route_distinguisher) - else: - if self.get_exist_rd() is not None: - self.updates_cmd.append( - 'undo route-distinguisher %s' % self.get_exist_rd()) - if self.vpn_target_state == "present": - if not self.is_vrf_rt_exist(): - if self.evpn is False: - self.updates_cmd.append( - 'vpn-target %s %s' % (self.vpn_target_value, vpn_target_type)) - else: - self.updates_cmd.append( - 'vpn-target %s %s evpn' % (self.vpn_target_value, vpn_target_type)) - elif self.vpn_target_state == "absent": - if self.is_vrf_rt_exist(): - if self.evpn is False: - self.updates_cmd.append( - 'undo vpn-target %s %s' % (self.vpn_target_value, vpn_target_type)) - else: - self.updates_cmd.append( - 'undo vpn-target %s %s evpn' % (self.vpn_target_value, vpn_target_type)) - else: - self.updates_cmd.append('ip vpn-instance %s' % (self.vrf)) - if self.vrf_aftype == 'ipv4uni': - self.updates_cmd.append('undo ipv4-family') - elif self.vrf_aftype == 'ipv6uni': - self.updates_cmd.append('undo ipv6-family') - - def get_vrf(self): - """ check if vrf is need to change""" - - getxmlstr = CE_NC_GET_VRF - xmlstr_new_1 = (self.vrf.lower()) - - xml_str = get_nc_config(self.module, getxmlstr) - re_find_1 = re.findall( - r'.*(.*).*', xml_str.lower()) - - if re_find_1 is None: - return False - - return xmlstr_new_1 in re_find_1 - - def get_vrf_af(self): - """ check if vrf is need to change""" - - self.vrf_af_info["vpnInstAF"] = list() - if self.evpn is True: - getxmlstr = CE_NC_GET_VRF_AF % ( - self.vrf, CE_NC_GET_EXTEND_VRF_TARGET) - else: - getxmlstr = CE_NC_GET_VRF_AF % (self.vrf, CE_NC_GET_VRF_TARGET) - - xml_str = get_nc_config(self.module, getxmlstr) - - if 'data/' in xml_str: - return self.state == 'present' - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - - # get the vpn address family and RD text - vrf_addr_types = root.findall( - "l3vpn/l3vpncomm/l3vpnInstances/l3vpnInstance/vpnInstAFs/vpnInstAF") - if vrf_addr_types: - for vrf_addr_type in vrf_addr_types: - vrf_af_info = dict() - for vrf_addr_type_ele in vrf_addr_type: - if vrf_addr_type_ele.tag in ["vrfName", "afType", "vrfRD"]: - vrf_af_info[vrf_addr_type_ele.tag] = vrf_addr_type_ele.text - if vrf_addr_type_ele.tag == 'vpnTargets': - vrf_af_info["vpnTargets"] = list() - for rtargets in vrf_addr_type_ele: - rt_dict = dict() - for rtarget in rtargets: - if rtarget.tag in ["vrfRTValue", "vrfRTType"]: - rt_dict[rtarget.tag] = rtarget.text - vrf_af_info["vpnTargets"].append(rt_dict) - if vrf_addr_type_ele.tag == 'exVpnTargets': - vrf_af_info["evpnTargets"] = list() - for rtargets in vrf_addr_type_ele: - rt_dict = dict() - for rtarget in rtargets: - if rtarget.tag in ["vrfRTValue", "vrfRTType"]: - rt_dict[rtarget.tag] = rtarget.text - vrf_af_info["evpnTargets"].append(rt_dict) - self.vrf_af_info["vpnInstAF"].append(vrf_af_info) - - def check_params(self): - """Check all input params""" - - # vrf and description check - if self.vrf == '_public_': - self.module.fail_json( - msg='Error: The vrf name _public_ is reserved.') - if not self.get_vrf(): - self.module.fail_json( - msg='Error: The vrf name do not exist.') - if self.state == 'present': - if self.route_distinguisher: - if not is_valid_value(self.route_distinguisher): - self.module.fail_json(msg='Error:The vrf route distinguisher length must between 3 ~ 21,' - 'i.e. X.X.X.X:number<0-65535> or number<0-65535>:number<0-4294967295>' - 'or number<0-65535>.number<0-65535>:number<0-65535>' - 'or number<65536-4294967295>:number<0-65535>' - ' but not be 0:0 or 0.0:0.') - if not self.vpn_target_state: - if self.vpn_target_value or self.vpn_target_type: - self.module.fail_json( - msg='Error: The vpn target state should be exist.') - if self.vpn_target_state: - if not self.vpn_target_value or not self.vpn_target_type: - self.module.fail_json( - msg='Error: The vpn target value and type should be exist.') - if self.vpn_target_value: - if not is_valid_value(self.vpn_target_value): - self.module.fail_json(msg='Error:The vrf target value length must between 3 ~ 21,' - 'i.e. X.X.X.X:number<0-65535> or number<0-65535>:number<0-4294967295>' - 'or number<0-65535>.number<0-65535>:number<0-65535>' - 'or number<65536-4294967295>:number<0-65535>' - ' but not be 0:0 or 0.0:0.') - - def operate_vrf_af(self): - """config/delete vrf""" - - vrf_target_operate = '' - if self.route_distinguisher is None: - route_d = '' - else: - route_d = self.route_distinguisher - - if self.state == 'present': - if self.vrf_aftype: - if self.is_vrf_af_exist(): - self.vrf_af_type_changed = False - else: - self.vrf_af_type_changed = True - configxmlstr = CE_NC_CREATE_VRF_AF % ( - self.vrf, self.vrf_aftype, route_d, vrf_target_operate) - else: - self.vrf_af_type_changed = bool(self.is_vrf_af_exist()) - - if self.vpn_target_state == 'present': - if self.evpn is False and not self.is_vrf_rt_exist(): - vrf_target_operate = CE_NC_CREATE_VRF_TARGET % ( - self.vpn_target_type, self.vpn_target_value) - configxmlstr = CE_NC_CREATE_VRF_AF % ( - self.vrf, self.vrf_aftype, route_d, vrf_target_operate) - self.vpn_target_changed = True - if self.evpn is True and not self.is_vrf_rt_exist(): - vrf_target_operate = CE_NC_CREATE_EXTEND_VRF_TARGET % ( - self.vpn_target_type, self.vpn_target_value) - configxmlstr = CE_NC_CREATE_VRF_AF % ( - self.vrf, self.vrf_aftype, route_d, vrf_target_operate) - self.vpn_target_changed = True - elif self.vpn_target_state == 'absent': - if self.evpn is False and self.is_vrf_rt_exist(): - vrf_target_operate = CE_NC_DELETE_VRF_TARGET % ( - self.vpn_target_type, self.vpn_target_value) - configxmlstr = CE_NC_CREATE_VRF_AF % ( - self.vrf, self.vrf_aftype, route_d, vrf_target_operate) - self.vpn_target_changed = True - if self.evpn is True and self.is_vrf_rt_exist(): - vrf_target_operate = CE_NC_DELETE_EXTEND_VRF_TARGET % ( - self.vpn_target_type, self.vpn_target_value) - configxmlstr = CE_NC_CREATE_VRF_AF % ( - self.vrf, self.vrf_aftype, route_d, vrf_target_operate) - self.vpn_target_changed = True - else: - if self.route_distinguisher: - if not self.is_vrf_rd_exist(): - configxmlstr = CE_NC_CREATE_VRF_AF % ( - self.vrf, self.vrf_aftype, route_d, vrf_target_operate) - self.vrf_rd_changed = True - else: - self.vrf_rd_changed = False - else: - if self.is_vrf_rd_exist(): - configxmlstr = CE_NC_CREATE_VRF_AF % ( - self.vrf, self.vrf_aftype, route_d, vrf_target_operate) - self.vrf_rd_changed = True - else: - self.vrf_rd_changed = False - if not self.vrf_rd_changed and not self.vrf_af_type_changed and not self.vpn_target_changed: - self.changed = False - else: - self.changed = True - else: - if self.is_vrf_af_exist(): - configxmlstr = CE_NC_DELETE_VRF_AF % ( - self.vrf, self.vrf_aftype) - self.changed = True - else: - self.changed = False - - if not self.changed: - return - - conf_str = build_config_xml(configxmlstr) - - recv_xml = set_nc_config(self.module, conf_str) - self.check_response(recv_xml, "OPERATE_VRF_AF") - - def get_proposed(self): - """get_proposed""" - - if self.state == 'present': - self.proposed['vrf'] = self.vrf - if self.vrf_aftype is None: - self.proposed['vrf_aftype'] = 'ipv4uni' - else: - self.proposed['vrf_aftype'] = self.vrf_aftype - if self.route_distinguisher is not None: - self.proposed['route_distinguisher'] = self.route_distinguisher - else: - self.proposed['route_distinguisher'] = list() - if self.vpn_target_state == 'present': - self.proposed['evpn'] = self.evpn - self.proposed['vpn_target_type'] = self.vpn_target_type - self.proposed['vpn_target_value'] = self.vpn_target_value - else: - self.proposed['vpn_target_type'] = list() - self.proposed['vpn_target_value'] = list() - else: - self.proposed = dict() - self.proposed['state'] = self.state - self.proposed['vrf'] = self.vrf - self.proposed['vrf_aftype'] = list() - self.proposed['route_distinguisher'] = list() - self.proposed['vpn_target_value'] = list() - self.proposed['vpn_target_type'] = list() - - def get_existing(self): - """get_existing""" - - self.get_vrf_af() - self.existing['vrf'] = self.vrf - self.existing['vrf_aftype'] = list() - self.existing['route_distinguisher'] = list() - self.existing['vpn_target_value'] = list() - self.existing['vpn_target_type'] = list() - self.existing['evpn_target_value'] = list() - self.existing['evpn_target_type'] = list() - if self.vrf_af_info["vpnInstAF"] is None: - return - for vrf_af_ele in self.vrf_af_info["vpnInstAF"]: - self.existing['vrf_aftype'].append(vrf_af_ele["afType"]) - self.existing['route_distinguisher'].append( - vrf_af_ele["vrfRD"]) - if vrf_af_ele.get("vpnTargets"): - for vpn_target in vrf_af_ele.get("vpnTargets"): - self.existing['vpn_target_type'].append( - vpn_target["vrfRTType"]) - self.existing['vpn_target_value'].append( - vpn_target["vrfRTValue"]) - if vrf_af_ele.get("evpnTargets"): - for evpn_target in vrf_af_ele.get("evpnTargets"): - self.existing['evpn_target_type'].append( - evpn_target["vrfRTType"]) - self.existing['evpn_target_value'].append( - evpn_target["vrfRTValue"]) - - def get_end_state(self): - """get_end_state""" - - self.get_vrf_af() - self.end_state['vrf'] = self.vrf - self.end_state['vrf_aftype'] = list() - self.end_state['route_distinguisher'] = list() - self.end_state['vpn_target_value'] = list() - self.end_state['vpn_target_type'] = list() - self.end_state['evpn_target_value'] = list() - self.end_state['evpn_target_type'] = list() - if self.vrf_af_info["vpnInstAF"] is None: - return - for vrf_af_ele in self.vrf_af_info["vpnInstAF"]: - self.end_state['vrf_aftype'].append(vrf_af_ele["afType"]) - self.end_state['route_distinguisher'].append(vrf_af_ele["vrfRD"]) - if vrf_af_ele.get("vpnTargets"): - for vpn_target in vrf_af_ele.get("vpnTargets"): - self.end_state['vpn_target_type'].append( - vpn_target["vrfRTType"]) - self.end_state['vpn_target_value'].append( - vpn_target["vrfRTValue"]) - if vrf_af_ele.get("evpnTargets"): - for evpn_target in vrf_af_ele.get("evpnTargets"): - self.end_state['evpn_target_type'].append( - evpn_target["vrfRTType"]) - self.end_state['evpn_target_value'].append( - evpn_target["vrfRTValue"]) - - def work(self): - """worker""" - - self.check_params() - self.get_existing() - self.get_proposed() - self.operate_vrf_af() - self.set_update_cmd() - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """main""" - - argument_spec = dict( - vrf=dict(required=True, type='str'), - vrf_aftype=dict(choices=['v4', 'v6'], - default='v4', required=False), - route_distinguisher=dict(required=False, type='str'), - evpn=dict(type='bool', default=False), - vpn_target_type=dict( - choices=['export_extcommunity', 'import_extcommunity'], required=False), - vpn_target_value=dict(required=False, type='str'), - vpn_target_state=dict(choices=['absent', 'present'], required=False), - state=dict(choices=['absent', 'present'], - default='present', required=False), - ) - argument_spec.update(ce_argument_spec) - interface = VrfAf(argument_spec) - interface.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vrf_interface.py b/plugins/modules/network/cloudengine/ce_vrf_interface.py deleted file mode 100644 index 0eff09eee5..0000000000 --- a/plugins/modules/network/cloudengine/ce_vrf_interface.py +++ /dev/null @@ -1,521 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vrf_interface -short_description: Manages interface specific VPN configuration on HUAWEI CloudEngine switches. -description: - - Manages interface specific VPN configuration of HUAWEI CloudEngine switches. -author: Zhijin Zhou (@QijunPan) -notes: - - Ensure that a VPN instance has been created and the IPv4 address family has been enabled for the VPN instance. - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - vrf: - description: - - VPN instance, the length of vrf name is 1 ~ 31, i.e. "test", but can not be C(_public_). - required: true - vpn_interface: - description: - - An interface that can binding VPN instance, i.e. 40GE1/0/22, Vlanif10. - Must be fully qualified interface name. - Interface types, such as 10GE, 40GE, 100GE, LoopBack, MEth, Tunnel, Vlanif.... - required: true - state: - description: - - Manage the state of the resource. - required: false - choices: ['present','absent'] - default: present -''' - -EXAMPLES = ''' -- name: VRF interface test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: "Configure a VPN instance for the interface" - ce_vrf_interface: - vpn_interface: 40GE1/0/2 - vrf: test - state: present - provider: "{{ cli }}" - - - name: "Disable the association between a VPN instance and an interface" - ce_vrf_interface: - vpn_interface: 40GE1/0/2 - vrf: test - state: absent - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: { - "state": "present", - "vpn_interface": "40GE2/0/17", - "vrf": "jss" - } -existing: - description: k/v pairs of existing attributes on the interface - returned: verbose mode - type: dict - sample: { - "vpn_interface": "40GE2/0/17", - "vrf": null - } -end_state: - description: k/v pairs of end attributes on the interface - returned: verbose mode - type: dict - sample: { - "vpn_interface": "40GE2/0/17", - "vrf": "jss" - } -updates: - description: command list sent to the device - returned: always - type: list - sample: [ - "ip binding vpn-instance jss", - ] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, get_nc_config, set_nc_config - -CE_NC_GET_VRF = """ - - - - - - %s - - - - - -""" - -CE_NC_GET_VRF_INTERFACE = """ - - - - - - - - - - - - - - - - -""" - -CE_NC_MERGE_VRF_INTERFACE = """ - - - - - - %s - - - %s - - - - - - - -""" - -CE_NC_GET_INTF = """ - - - - - %s - - - - - -""" - -CE_NC_DEL_INTF_VPN = """ - - - - - - %s - - - %s - - - - - - - -""" - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-Port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class VrfInterface(object): - """Manage vpn instance""" - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # vpn instance info - self.vrf = self.module.params['vrf'] - self.vpn_interface = self.module.params['vpn_interface'] - self.vpn_interface = self.vpn_interface.upper().replace(' ', '') - self.state = self.module.params['state'] - self.intf_info = dict() - self.intf_info['isL2SwitchPort'] = None - self.intf_info['vrfName'] = None - self.conf_exist = False - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """init_module""" - - required_one_of = [("vrf", "vpn_interface")] - self.module = AnsibleModule( - argument_spec=self.spec, required_one_of=required_one_of, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_update_cmd(self): - """ get updated command""" - - if self.conf_exist: - return - - if self.state == 'absent': - self.updates_cmd.append( - "undo ip binding vpn-instance %s" % self.vrf) - return - - if self.vrf != self.intf_info['vrfName']: - self.updates_cmd.append("ip binding vpn-instance %s" % self.vrf) - - return - - def check_params(self): - """Check all input params""" - - if not self.is_vrf_exist(): - self.module.fail_json( - msg='Error: The VPN instance is not existed.') - - if self.state == 'absent': - if self.vrf != self.intf_info['vrfName']: - self.module.fail_json( - msg='Error: The VPN instance is not bound to the interface.') - - if self.intf_info['isL2SwitchPort'] == 'true': - self.module.fail_json( - msg='Error: L2Switch Port can not binding a VPN instance.') - - # interface type check - if self.vpn_interface: - intf_type = get_interface_type(self.vpn_interface) - if not intf_type: - self.module.fail_json( - msg='Error: interface name of %s' - ' is error.' % self.vpn_interface) - - # vrf check - if self.vrf == '_public_': - self.module.fail_json( - msg='Error: The vrf name _public_ is reserved.') - if len(self.vrf) < 1 or len(self.vrf) > 31: - self.module.fail_json( - msg='Error: The vrf name length must be between 1 and 31.') - - def get_interface_vpn_name(self, vpninfo, vpn_name): - """ get vpn instance name""" - - l3vpn_if = vpninfo.findall("l3vpnIf") - for l3vpn_ifinfo in l3vpn_if: - for ele in l3vpn_ifinfo: - if ele.tag in ['ifName']: - if ele.text.lower() == self.vpn_interface.lower(): - self.intf_info['vrfName'] = vpn_name - - def get_interface_vpn(self): - """ get the VPN instance associated with the interface""" - - xml_str = CE_NC_GET_VRF_INTERFACE - con_obj = get_nc_config(self.module, xml_str) - if "" in con_obj: - return - - xml_str = con_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get global vrf interface info - root = ElementTree.fromstring(xml_str) - vpns = root.findall( - "l3vpn/l3vpncomm/l3vpnInstances/l3vpnInstance") - if vpns: - for vpnele in vpns: - vpn_name = None - for vpninfo in vpnele: - if vpninfo.tag == 'vrfName': - vpn_name = vpninfo.text - if vpninfo.tag == 'l3vpnIfs': - self.get_interface_vpn_name(vpninfo, vpn_name) - - return - - def is_vrf_exist(self): - """ judge whether the VPN instance is existed""" - - conf_str = CE_NC_GET_VRF % self.vrf - con_obj = get_nc_config(self.module, conf_str) - if "" in con_obj: - return False - - return True - - def get_intf_conf_info(self): - """ get related configuration of the interface""" - - conf_str = CE_NC_GET_INTF % self.vpn_interface - con_obj = get_nc_config(self.module, conf_str) - if "" in con_obj: - return - - # get interface base info - xml_str = con_obj.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - interface = root.find("ifm/interfaces/interface") - if interface: - for eles in interface: - if eles.tag in ["isL2SwitchPort"]: - self.intf_info[eles.tag] = eles.text - - self.get_interface_vpn() - return - - def get_existing(self): - """get existing config""" - - self.existing = dict(vrf=self.intf_info['vrfName'], - vpn_interface=self.vpn_interface) - - def get_proposed(self): - """get_proposed""" - - self.proposed = dict(vrf=self.vrf, - vpn_interface=self.vpn_interface, - state=self.state) - - def get_end_state(self): - """get_end_state""" - - self.intf_info['vrfName'] = None - self.get_intf_conf_info() - - self.end_state = dict(vrf=self.intf_info['vrfName'], - vpn_interface=self.vpn_interface) - - def show_result(self): - """ show result""" - - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - def judge_if_config_exist(self): - """ judge whether configuration has existed""" - - if self.state == 'absent': - return False - - delta = set(self.proposed.items()).difference( - self.existing.items()) - delta = dict(delta) - if len(delta) == 1 and delta['state']: - return True - - return False - - def config_interface_vrf(self): - """ configure VPN instance of the interface""" - - if not self.conf_exist and self.state == 'present': - - xml_str = CE_NC_MERGE_VRF_INTERFACE % ( - self.vrf, self.vpn_interface) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "VRF_INTERFACE_CONFIG") - self.changed = True - elif self.state == 'absent': - xml_str = CE_NC_DEL_INTF_VPN % (self.vrf, self.vpn_interface) - ret_xml = set_nc_config(self.module, xml_str) - self.check_response(ret_xml, "DEL_VRF_INTERFACE_CONFIG") - self.changed = True - - def work(self): - """execute task""" - - self.get_intf_conf_info() - self.check_params() - self.get_existing() - self.get_proposed() - self.conf_exist = self.judge_if_config_exist() - - self.config_interface_vrf() - - self.get_update_cmd() - self.get_end_state() - self.show_result() - - -def main(): - """main""" - - argument_spec = dict( - vrf=dict(required=True, type='str'), - vpn_interface=dict(required=True, type='str'), - state=dict(choices=['absent', 'present'], - default='present', required=False), - ) - argument_spec.update(ce_argument_spec) - vrf_intf = VrfInterface(argument_spec) - vrf_intf.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vrrp.py b/plugins/modules/network/cloudengine/ce_vrrp.py deleted file mode 100644 index 6e3e5e1c26..0000000000 --- a/plugins/modules/network/cloudengine/ce_vrrp.py +++ /dev/null @@ -1,1331 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vrrp -short_description: Manages VRRP interfaces on HUAWEI CloudEngine devices. -description: - - Manages VRRP interface attributes on HUAWEI CloudEngine devices. -author: - - Li Yanfeng (@numone213) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - interface: - description: - - Name of an interface. The value is a string of 1 to 63 characters. - vrid: - description: - - VRRP backup group ID. - The value is an integer ranging from 1 to 255. - default: present - virtual_ip : - description: - - Virtual IP address. The value is a string of 0 to 255 characters. - vrrp_type: - description: - - Type of a VRRP backup group. - type: str - choices: ['normal', 'member', 'admin'] - admin_ignore_if_down: - description: - - mVRRP ignores an interface Down event. - type: bool - default: 'false' - admin_vrid: - description: - - Tracked mVRRP ID. The value is an integer ranging from 1 to 255. - admin_interface: - description: - - Tracked mVRRP interface name. The value is a string of 1 to 63 characters. - admin_flowdown: - description: - - Disable the flowdown function for service VRRP. - type: bool - default: 'false' - priority: - description: - - Configured VRRP priority. - The value ranges from 1 to 254. The default value is 100. A larger value indicates a higher priority. - version: - description: - - VRRP version. The default version is v2. - type: str - choices: ['v2','v3'] - advertise_interval: - description: - - Configured interval between sending advertisements, in milliseconds. - Only the master router sends VRRP advertisements. The default value is 1000 milliseconds. - preempt_timer_delay: - description: - - Preemption delay. - The value is an integer ranging from 0 to 3600. The default value is 0. - gratuitous_arp_interval: - description: - - Interval at which gratuitous ARP packets are sent, in seconds. - The value ranges from 30 to 1200.The default value is 300. - recover_delay: - description: - - Delay in recovering after an interface goes Up. - The delay is used for interface flapping suppression. - The value is an integer ranging from 0 to 3600. - The default value is 0 seconds. - holding_multiplier: - description: - - The configured holdMultiplier.The value is an integer ranging from 3 to 10. The default value is 3. - auth_mode: - description: - - Authentication type used for VRRP packet exchanges between virtual routers. - The values are noAuthentication, simpleTextPassword, md5Authentication. - The default value is noAuthentication. - type: str - choices: ['simple','md5','none'] - is_plain: - description: - - Select the display mode of an authentication key. - By default, an authentication key is displayed in ciphertext. - type: bool - default: 'false' - auth_key: - description: - - This object is set based on the authentication type. - When noAuthentication is specified, the value is empty. - When simpleTextPassword or md5Authentication is specified, the value is a string of 1 to 8 characters - in plaintext and displayed as a blank text for security. - fast_resume: - description: - - mVRRP's fast resume mode. - type: str - choices: ['enable','disable'] - state: - description: - - Specify desired state of the resource. - type: str - default: present - choices: ['present','absent'] -''' - -EXAMPLES = ''' -- name: vrrp module test - hosts: cloudengine - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - tasks: - - name: Set vrrp version - ce_vrrp: - version: v3 - provider: "{{ cli }}" - - name: Set vrrp gratuitous-arp interval - ce_vrrp: - gratuitous_arp_interval: 40 - mlag_id: 4 - provider: "{{ cli }}" - - name: Set vrrp recover-delay - ce_vrrp: - recover_delay: 10 - provider: "{{ cli }}" - - name: Set vrrp vrid virtual-ip - ce_vrrp: - interface: 40GE2/0/8 - vrid: 1 - virtual_ip: 10.14.2.7 - provider: "{{ cli }}" - - name: Set vrrp vrid admin - ce_vrrp: - interface: 40GE2/0/8 - vrid: 1 - vrrp_type: admin - provider: "{{ cli }}" - - name: Set vrrp vrid fast_resume - ce_vrrp: - interface: 40GE2/0/8 - vrid: 1 - fast_resume: enable - provider: "{{ cli }}" - - name: Set vrrp vrid holding-multiplier - ce_vrrp: - interface: 40GE2/0/8 - vrid: 1 - holding_multiplier: 4 - provider: "{{ cli }}" - - name: Set vrrp vrid preempt timer delay - ce_vrrp: - interface: 40GE2/0/8 - vrid: 1 - preempt_timer_delay: 10 - provider: "{{ cli }}" - - name: Set vrrp vrid admin-vrrp - ce_vrrp: - interface: 40GE2/0/8 - vrid: 1 - admin_interface: 40GE2/0/9 - admin_vrid: 2 - vrrp_type: member - provider: "{{ cli }}" - - name: Set vrrp vrid authentication-mode - ce_vrrp: - interface: 40GE2/0/8 - vrid: 1 - is_plain: true - auth_mode: simple - auth_key: aaa - provider: "{{ cli }}" -''' - -RETURN = ''' -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: { - "auth_key": "aaa", - "auth_mode": "simple", - "interface": "40GE2/0/8", - "is_plain": true, - "state": "present", - "vrid": "1" - } -existing: - description: k/v pairs of existing aaa server - returned: always - type: dict - sample: { - "auth_mode": "none", - "interface": "40GE2/0/8", - "is_plain": "false", - "vrid": "1", - "vrrp_type": "normal" - } -end_state: - description: k/v pairs of aaa params after module execution - returned: always - type: dict - sample: { - "auth_mode": "simple", - "interface": "40GE2/0/8", - "is_plain": "true", - "vrid": "1", - "vrrp_type": "normal" - } -updates: - description: command sent to the device - returned: always - type: list - sample: { "interface 40GE2/0/8", - "vrrp vrid 1 authentication-mode simple plain aaa"} -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - - -CE_NC_GET_VRRP_GROUP_INFO = """ - - - - - %s - %s - - - - -""" - -CE_NC_SET_VRRP_GROUP_INFO_HEAD = """ - - - - - %s - %s -""" -CE_NC_SET_VRRP_GROUP_INFO_TAIL = """ - - - - -""" -CE_NC_GET_VRRP_GLOBAL_INFO = """ - - - - - - - - - - -""" - -CE_NC_SET_VRRP_GLOBAL_HEAD = """ - - - -""" -CE_NC_SET_VRRP_GLOBAL_TAIL = """ - - - -""" - -CE_NC_GET_VRRP_VIRTUAL_IP_INFO = """ - - - - - %s - %s - - - - - - - - - -""" -CE_NC_CREATE_VRRP_VIRTUAL_IP_INFO = """ - - - - - %s - %s - - - %s - - - - - - -""" -CE_NC_DELETE_VRRP_VIRTUAL_IP_INFO = """ - - - - - %s - %s - - - %s - - - - - - -""" - - -def is_valid_address(address): - """check ip-address is valid""" - - if address.find('.') != -1: - addr_list = address.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('NULL'): - iftype = 'null' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - else: - return None - - return iftype.lower() - - -class Vrrp(object): - """ - Manages Manages vrrp information. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.interface = self.module.params['interface'] - self.vrid = self.module.params['vrid'] - self.virtual_ip = self.module.params['virtual_ip'] - self.vrrp_type = self.module.params['vrrp_type'] - self.admin_ignore_if_down = 'false' if self.module.params['admin_ignore_if_down'] is False else 'true' - self.admin_vrid = self.module.params['admin_vrid'] - self.admin_interface = self.module.params['admin_interface'] - self.admin_flowdown = 'false' if self.module.params['admin_flowdown'] is False else 'true' - self.priority = self.module.params['priority'] - self.version = self.module.params['version'] - self.advertise_interval = self.module.params['advertise_interval'] - self.preempt_timer_delay = self.module.params['preempt_timer_delay'] - self.gratuitous_arp_interval = self.module.params[ - 'gratuitous_arp_interval'] - self.recover_delay = self.module.params['recover_delay'] - self.holding_multiplier = self.module.params['holding_multiplier'] - self.auth_mode = self.module.params['auth_mode'] - self.is_plain = 'false' if self.module.params['is_plain'] is False else 'true' - self.auth_key = self.module.params['auth_key'] - self.fast_resume = self.module.params['fast_resume'] - self.state = self.module.params['state'] - - # vrrp info - self.vrrp_global_info = None - self.virtual_ip_info = None - self.vrrp_group_info = None - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.existing = dict() - self.proposed = dict() - self.end_state = dict() - - def init_module(self): - """ init module """ - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def get_virtual_ip_info(self): - """ get vrrp virtual ip info.""" - virtual_ip_info = dict() - conf_str = CE_NC_GET_VRRP_VIRTUAL_IP_INFO % (self.vrid, self.interface) - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return virtual_ip_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - virtual_ip_info["vrrpVirtualIpInfos"] = list() - root = ElementTree.fromstring(xml_str) - vrrp_virtual_ip_infos = root.findall( - "vrrp/vrrpGroups/vrrpGroup/virtualIps/virtualIp") - if vrrp_virtual_ip_infos: - for vrrp_virtual_ip_info in vrrp_virtual_ip_infos: - virtual_ip_dict = dict() - for ele in vrrp_virtual_ip_info: - if ele.tag in ["virtualIpAddress"]: - virtual_ip_dict[ele.tag] = ele.text - virtual_ip_info["vrrpVirtualIpInfos"].append( - virtual_ip_dict) - return virtual_ip_info - - def get_vrrp_global_info(self): - """ get vrrp global info.""" - - vrrp_global_info = dict() - conf_str = CE_NC_GET_VRRP_GLOBAL_INFO - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return vrrp_global_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - global_info = root.findall( - "vrrp/vrrpGlobalCfg") - - if global_info: - for tmp in global_info: - for site in tmp: - if site.tag in ["gratuitousArpTimeOut", "gratuitousArpFlag", "recoverDelay", "version"]: - vrrp_global_info[site.tag] = site.text - return vrrp_global_info - - def get_vrrp_group_info(self): - """ get vrrp group info.""" - - vrrp_group_info = dict() - conf_str = CE_NC_GET_VRRP_GROUP_INFO % (self.interface, self.vrid) - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return vrrp_group_info - else: - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - root = ElementTree.fromstring(xml_str) - global_info = root.findall( - "vrrp/vrrpGroups/vrrpGroup") - - if global_info: - for tmp in global_info: - for site in tmp: - if site.tag in ["ifName", "vrrpId", "priority", "advertiseInterval", "preemptMode", "delayTime", - "authenticationMode", "authenticationKey", "vrrpType", "adminVrrpId", - "adminIfName", "adminIgnoreIfDown", "isPlain", "unflowdown", "fastResume", - "holdMultiplier"]: - vrrp_group_info[site.tag] = site.text - return vrrp_group_info - - def check_params(self): - """Check all input params""" - - # interface check - if self.interface: - intf_type = get_interface_type(self.interface) - if not intf_type: - self.module.fail_json( - msg='Error: Interface name of %s ' - 'is error.' % self.interface) - - # vrid check - if self.vrid: - if not self.vrid.isdigit(): - self.module.fail_json( - msg='Error: The value of vrid is an integer.') - if int(self.vrid) < 1 or int(self.vrid) > 255: - self.module.fail_json( - msg='Error: The value of vrid ranges from 1 to 255.') - - # virtual_ip check - if self.virtual_ip: - if not is_valid_address(self.virtual_ip): - self.module.fail_json( - msg='Error: The %s is not a valid ip address.' % self.virtual_ip) - - # admin_vrid check - if self.admin_vrid: - if not self.admin_vrid.isdigit(): - self.module.fail_json( - msg='Error: The value of admin_vrid is an integer.') - if int(self.admin_vrid) < 1 or int(self.admin_vrid) > 255: - self.module.fail_json( - msg='Error: The value of admin_vrid ranges from 1 to 255.') - - # admin_interface check - if self.admin_interface: - intf_type = get_interface_type(self.admin_interface) - if not intf_type: - self.module.fail_json( - msg='Error: Admin interface name of %s ' - 'is error.' % self.admin_interface) - - # priority check - if self.priority: - if not self.priority.isdigit(): - self.module.fail_json( - msg='Error: The value of priority is an integer.') - if int(self.priority) < 1 or int(self.priority) > 254: - self.module.fail_json( - msg='Error: The value of priority ranges from 1 to 254. The default value is 100.') - - # advertise_interval check - if self.advertise_interval: - if not self.advertise_interval.isdigit(): - self.module.fail_json( - msg='Error: The value of advertise_interval is an integer.') - if int(self.advertise_interval) < 1000 or int(self.advertise_interval) > 255000: - self.module.fail_json( - msg='Error: The value of advertise_interval ranges from 1000 to 255000 milliseconds. The default value is 1000 milliseconds.') - if int(self.advertise_interval) % 1000 != 0: - self.module.fail_json( - msg='Error: The advertisement interval value of VRRP must be a multiple of 1000 milliseconds.') - # preempt_timer_delay check - if self.preempt_timer_delay: - if not self.preempt_timer_delay.isdigit(): - self.module.fail_json( - msg='Error: The value of preempt_timer_delay is an integer.') - if int(self.preempt_timer_delay) < 1 or int(self.preempt_timer_delay) > 3600: - self.module.fail_json( - msg='Error: The value of preempt_timer_delay ranges from 1 to 3600. The default value is 0.') - - # holding_multiplier check - if self.holding_multiplier: - if not self.holding_multiplier.isdigit(): - self.module.fail_json( - msg='Error: The value of holding_multiplier is an integer.') - if int(self.holding_multiplier) < 3 or int(self.holding_multiplier) > 10: - self.module.fail_json( - msg='Error: The value of holding_multiplier ranges from 3 to 10. The default value is 3.') - - # auth_key check - if self.auth_key: - if len(self.auth_key) > 16 \ - or len(self.auth_key.replace(' ', '')) < 1: - self.module.fail_json( - msg='Error: The length of auth_key is not in the range from 1 to 16.') - - def is_virtual_ip_change(self): - """whether virtual ip change""" - - if not self.virtual_ip_info: - return True - - for info in self.virtual_ip_info["vrrpVirtualIpInfos"]: - if info["virtualIpAddress"] == self.virtual_ip: - return False - return True - - def is_virtual_ip_exist(self): - """whether virtual ip info exist""" - - if not self.virtual_ip_info: - return False - - for info in self.virtual_ip_info["vrrpVirtualIpInfos"]: - if info["virtualIpAddress"] == self.virtual_ip: - return True - return False - - def is_vrrp_global_info_change(self): - """whether vrrp global attribute info change""" - - if not self.vrrp_global_info: - return True - - if self.gratuitous_arp_interval: - if self.vrrp_global_info["gratuitousArpFlag"] == "false": - self.module.fail_json(msg="Error: gratuitousArpFlag is false.") - if self.vrrp_global_info["gratuitousArpTimeOut"] != self.gratuitous_arp_interval: - return True - if self.recover_delay: - if self.vrrp_global_info["recoverDelay"] != self.recover_delay: - return True - if self.version: - if self.vrrp_global_info["version"] != self.version: - return True - return False - - def is_vrrp_global_info_exist(self): - """whether vrrp global attribute info exist""" - - if self.gratuitous_arp_interval or self.recover_delay or self.version: - if self.gratuitous_arp_interval: - if self.vrrp_global_info["gratuitousArpFlag"] == "false": - self.module.fail_json( - msg="Error: gratuitousArpFlag is false.") - if self.vrrp_global_info["gratuitousArpTimeOut"] != self.gratuitous_arp_interval: - return False - if self.recover_delay: - if self.vrrp_global_info["recoverDelay"] != self.recover_delay: - return False - if self.version: - if self.vrrp_global_info["version"] != self.version: - return False - return True - - return False - - def is_vrrp_group_info_change(self): - """whether vrrp group attribute info change""" - if self.vrrp_type: - if self.vrrp_group_info["vrrpType"] != self.vrrp_type: - return True - if self.admin_ignore_if_down: - if self.vrrp_group_info["adminIgnoreIfDown"] != self.admin_ignore_if_down: - return True - if self.admin_vrid: - if self.vrrp_group_info["adminVrrpId"] != self.admin_vrid: - return True - if self.admin_interface: - if self.vrrp_group_info["adminIfName"] != self.admin_interface: - return True - if self.admin_flowdown: - if self.vrrp_group_info["unflowdown"] != self.admin_flowdown: - return True - if self.priority: - if self.vrrp_group_info["priority"] != self.priority: - return True - if self.fast_resume: - fast_resume = "false" - if self.fast_resume == "enable": - fast_resume = "true" - if self.vrrp_group_info["fastResume"] != fast_resume: - return True - if self.advertise_interval: - if self.vrrp_group_info["advertiseInterval"] != self.advertise_interval: - return True - if self.preempt_timer_delay: - if self.vrrp_group_info["delayTime"] != self.preempt_timer_delay: - return True - if self.holding_multiplier: - if self.vrrp_group_info["holdMultiplier"] != self.holding_multiplier: - return True - if self.auth_mode: - if self.vrrp_group_info["authenticationMode"] != self.auth_mode: - return True - if self.auth_key: - return True - if self.is_plain: - if self.vrrp_group_info["isPlain"] != self.is_plain: - return True - - return False - - def is_vrrp_group_info_exist(self): - """whether vrrp group attribute info exist""" - - if self.vrrp_type: - if self.vrrp_group_info["vrrpType"] != self.vrrp_type: - return False - if self.admin_ignore_if_down: - if self.vrrp_group_info["adminIgnoreIfDown"] != self.admin_ignore_if_down: - return False - if self.admin_vrid: - if self.vrrp_group_info["adminVrrpId"] != self.admin_vrid: - return False - if self.admin_interface: - if self.vrrp_group_info["adminIfName"] != self.admin_interface: - return False - if self.admin_flowdown: - if self.vrrp_group_info["unflowdown"] != self.admin_flowdown: - return False - if self.priority: - if self.vrrp_group_info["priority"] != self.priority: - return False - if self.fast_resume: - fast_resume = "false" - if self.fast_resume == "enable": - fast_resume = "true" - if self.vrrp_group_info["fastResume"] != fast_resume: - return False - if self.advertise_interval: - if self.vrrp_group_info["advertiseInterval"] != self.advertise_interval: - return False - if self.preempt_timer_delay: - if self.vrrp_group_info["delayTime"] != self.preempt_timer_delay: - return False - if self.holding_multiplier: - if self.vrrp_group_info["holdMultiplier"] != self.holding_multiplier: - return False - if self.auth_mode: - if self.vrrp_group_info["authenticationMode"] != self.auth_mode: - return False - if self.is_plain: - if self.vrrp_group_info["isPlain"] != self.is_plain: - return False - return True - - def create_virtual_ip(self): - """create virtual ip info""" - - if self.is_virtual_ip_change(): - conf_str = CE_NC_CREATE_VRRP_VIRTUAL_IP_INFO % ( - self.vrid, self.interface, self.virtual_ip) - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: create virtual ip info failed.') - - self.updates_cmd.append("interface %s" % self.interface) - self.updates_cmd.append( - "vrrp vrid %s virtual-ip %s" % (self.vrid, self.virtual_ip)) - self.changed = True - - def delete_virtual_ip(self): - """delete virtual ip info""" - - if self.is_virtual_ip_exist(): - conf_str = CE_NC_DELETE_VRRP_VIRTUAL_IP_INFO % ( - self.vrid, self.interface, self.virtual_ip) - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: delete virtual ip info failed.') - - self.updates_cmd.append("interface %s" % self.interface) - self.updates_cmd.append( - "undo vrrp vrid %s virtual-ip %s " % (self.vrid, self.virtual_ip)) - self.changed = True - - def set_vrrp_global(self): - """set vrrp global attribute info""" - - if self.is_vrrp_global_info_change(): - conf_str = CE_NC_SET_VRRP_GLOBAL_HEAD - if self.gratuitous_arp_interval: - conf_str += "%s" % self.gratuitous_arp_interval - if self.recover_delay: - conf_str += "%s" % self.recover_delay - if self.version: - conf_str += "%s" % self.version - conf_str += CE_NC_SET_VRRP_GLOBAL_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: set vrrp global attribute info failed.') - - if self.gratuitous_arp_interval: - self.updates_cmd.append( - "vrrp gratuitous-arp interval %s" % self.gratuitous_arp_interval) - - if self.recover_delay: - self.updates_cmd.append( - "vrrp recover-delay %s" % self.recover_delay) - - if self.version: - version = "3" - if self.version == "v2": - version = "2" - self.updates_cmd.append("vrrp version %s" % version) - self.changed = True - - def delete_vrrp_global(self): - """delete vrrp global attribute info""" - - if self.is_vrrp_global_info_exist(): - conf_str = CE_NC_SET_VRRP_GLOBAL_HEAD - if self.gratuitous_arp_interval: - if self.gratuitous_arp_interval == "120": - self.module.fail_json( - msg='Error: The default value of gratuitous_arp_interval is 120.') - gratuitous_arp_interval = "120" - conf_str += "%s" % gratuitous_arp_interval - if self.recover_delay: - if self.recover_delay == "0": - self.module.fail_json( - msg='Error: The default value of recover_delay is 0.') - recover_delay = "0" - conf_str += "%s" % recover_delay - if self.version: - if self.version == "v2": - self.module.fail_json( - msg='Error: The default value of version is v2.') - version = "v2" - conf_str += "%s" % version - conf_str += CE_NC_SET_VRRP_GLOBAL_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: set vrrp global attribute info failed.') - if self.gratuitous_arp_interval: - self.updates_cmd.append("undo vrrp gratuitous-arp interval") - - if self.recover_delay: - self.updates_cmd.append("undo vrrp recover-delay") - - if self.version == "v3": - self.updates_cmd.append("undo vrrp version") - self.changed = True - - def set_vrrp_group(self): - """set vrrp group attribute info""" - - if self.is_vrrp_group_info_change(): - conf_str = CE_NC_SET_VRRP_GROUP_INFO_HEAD % ( - self.interface, self.vrid) - if self.vrrp_type: - conf_str += "%s" % self.vrrp_type - if self.admin_vrid: - conf_str += "%s" % self.admin_vrid - if self.admin_interface: - conf_str += "%s" % self.admin_interface - if self.admin_flowdown: - conf_str += "%s" % self.admin_flowdown - if self.priority: - conf_str += "%s" % self.priority - if self.vrrp_type == "admin": - if self.admin_ignore_if_down: - conf_str += "%s" % self.admin_ignore_if_down - if self.fast_resume: - fast_resume = "false" - if self.fast_resume == "enable": - fast_resume = "true" - conf_str += "%s" % fast_resume - if self.advertise_interval: - conf_str += "%s" % self.advertise_interval - if self.preempt_timer_delay: - conf_str += "%s" % self.preempt_timer_delay - if self.holding_multiplier: - conf_str += "%s" % self.holding_multiplier - if self.auth_mode: - conf_str += "%s" % self.auth_mode - if self.auth_key: - conf_str += "%s" % self.auth_key - if self.auth_mode == "simple": - conf_str += "%s" % self.is_plain - - conf_str += CE_NC_SET_VRRP_GROUP_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: set vrrp group attribute info failed.') - if self.interface and self.vrid: - self.updates_cmd.append("interface %s" % self.interface) - if self.vrrp_type == "admin": - if self.admin_ignore_if_down == "true": - self.updates_cmd.append( - "vrrp vrid %s admin ignore-if-down" % self.vrid) - else: - self.updates_cmd.append( - "vrrp vrid %s admin" % self.vrid) - - if self.priority: - self.updates_cmd.append( - "vrrp vrid %s priority %s" % (self.vrid, self.priority)) - - if self.fast_resume == "enable": - self.updates_cmd.append( - "vrrp vrid %s fast-resume" % self.vrid) - if self.fast_resume == "disable": - self.updates_cmd.append( - "undo vrrp vrid %s fast-resume" % self.vrid) - - if self.advertise_interval: - advertise_interval = int(self.advertise_interval) / 1000 - self.updates_cmd.append("vrrp vrid %s timer advertise %s" % ( - self.vrid, int(advertise_interval))) - - if self.preempt_timer_delay: - self.updates_cmd.append("vrrp vrid %s preempt timer delay %s" % (self.vrid, - self.preempt_timer_delay)) - - if self.holding_multiplier: - self.updates_cmd.append( - "vrrp vrid %s holding-multiplier %s" % (self.vrid, self.holding_multiplier)) - - if self.admin_vrid and self.admin_interface: - if self.admin_flowdown == "true": - self.updates_cmd.append("vrrp vrid %s track admin-vrrp interface %s vrid %s unflowdown" % - (self.vrid, self.admin_interface, self.admin_vrid)) - else: - self.updates_cmd.append("vrrp vrid %s track admin-vrrp interface %s vrid %s" % - (self.vrid, self.admin_interface, self.admin_vrid)) - - if self.auth_mode and self.auth_key: - if self.auth_mode == "simple": - if self.is_plain == "true": - self.updates_cmd.append("vrrp vrid %s authentication-mode simple plain %s" % - (self.vrid, self.auth_key)) - else: - self.updates_cmd.append("vrrp vrid %s authentication-mode simple cipher %s" % - (self.vrid, self.auth_key)) - if self.auth_mode == "md5": - self.updates_cmd.append( - "vrrp vrid %s authentication-mode md5 %s" % (self.vrid, self.auth_key)) - self.changed = True - - def delete_vrrp_group(self): - """delete vrrp group attribute info""" - - if self.is_vrrp_group_info_exist(): - conf_str = CE_NC_SET_VRRP_GROUP_INFO_HEAD % ( - self.interface, self.vrid) - if self.vrrp_type: - vrrp_type = self.vrrp_type - if self.vrrp_type == "admin": - vrrp_type = "normal" - if self.vrrp_type == "member" and self.admin_vrid and self.admin_interface: - vrrp_type = "normal" - conf_str += "%s" % vrrp_type - if self.priority: - if self.priority == "100": - self.module.fail_json( - msg='Error: The default value of priority is 100.') - priority = "100" - conf_str += "%s" % priority - - if self.fast_resume: - fast_resume = "false" - if self.fast_resume == "enable": - fast_resume = "true" - conf_str += "%s" % fast_resume - if self.advertise_interval: - if self.advertise_interval == "1000": - self.module.fail_json( - msg='Error: The default value of advertise_interval is 1000.') - advertise_interval = "1000" - conf_str += "%s" % advertise_interval - if self.preempt_timer_delay: - if self.preempt_timer_delay == "0": - self.module.fail_json( - msg='Error: The default value of preempt_timer_delay is 0.') - preempt_timer_delay = "0" - conf_str += "%s" % preempt_timer_delay - if self.holding_multiplier: - if self.holding_multiplier == "0": - self.module.fail_json( - msg='Error: The default value of holding_multiplier is 3.') - holding_multiplier = "3" - conf_str += "%s" % holding_multiplier - if self.auth_mode: - auth_mode = self.auth_mode - if self.auth_mode == "md5" or self.auth_mode == "simple": - auth_mode = "none" - conf_str += "%s" % auth_mode - - conf_str += CE_NC_SET_VRRP_GROUP_INFO_TAIL - recv_xml = set_nc_config(self.module, conf_str) - if "" not in recv_xml: - self.module.fail_json( - msg='Error: set vrrp global attribute info failed.') - if self.interface and self.vrid: - self.updates_cmd.append("interface %s" % self.interface) - if self.vrrp_type == "admin": - self.updates_cmd.append( - "undo vrrp vrid %s admin" % self.vrid) - - if self.priority: - self.updates_cmd.append( - "undo vrrp vrid %s priority" % self.vrid) - - if self.fast_resume: - self.updates_cmd.append( - "undo vrrp vrid %s fast-resume" % self.vrid) - - if self.advertise_interval: - self.updates_cmd.append( - "undo vrrp vrid %s timer advertise" % self.vrid) - - if self.preempt_timer_delay: - self.updates_cmd.append( - "undo vrrp vrid %s preempt timer delay" % self.vrid) - - if self.holding_multiplier: - self.updates_cmd.append( - "undo vrrp vrid %s holding-multiplier" % self.vrid) - - if self.admin_vrid and self.admin_interface: - self.updates_cmd.append( - "undo vrrp vrid %s track admin-vrrp" % self.vrid) - - if self.auth_mode: - self.updates_cmd.append( - "undo vrrp vrid %s authentication-mode" % self.vrid) - self.changed = True - - def get_proposed(self): - """get proposed info""" - - if self.interface: - self.proposed["interface"] = self.interface - if self.vrid: - self.proposed["vrid"] = self.vrid - if self.virtual_ip: - self.proposed["virtual_ip"] = self.virtual_ip - if self.vrrp_type: - self.proposed["vrrp_type"] = self.vrrp_type - if self.admin_vrid: - self.proposed["admin_vrid"] = self.admin_vrid - if self.admin_interface: - self.proposed["admin_interface"] = self.admin_interface - if self.admin_flowdown: - self.proposed["unflowdown"] = self.admin_flowdown - if self.admin_ignore_if_down: - self.proposed["admin_ignore_if_down"] = self.admin_ignore_if_down - if self.priority: - self.proposed["priority"] = self.priority - if self.version: - self.proposed["version"] = self.version - if self.advertise_interval: - self.proposed["advertise_interval"] = self.advertise_interval - if self.preempt_timer_delay: - self.proposed["preempt_timer_delay"] = self.preempt_timer_delay - if self.gratuitous_arp_interval: - self.proposed[ - "gratuitous_arp_interval"] = self.gratuitous_arp_interval - if self.recover_delay: - self.proposed["recover_delay"] = self.recover_delay - if self.holding_multiplier: - self.proposed["holding_multiplier"] = self.holding_multiplier - if self.auth_mode: - self.proposed["auth_mode"] = self.auth_mode - if self.is_plain: - self.proposed["is_plain"] = self.is_plain - if self.auth_key: - self.proposed["auth_key"] = self.auth_key - if self.fast_resume: - self.proposed["fast_resume"] = self.fast_resume - if self.state: - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if self.gratuitous_arp_interval: - self.existing["gratuitous_arp_interval"] = self.vrrp_global_info[ - "gratuitousArpTimeOut"] - if self.version: - self.existing["version"] = self.vrrp_global_info["version"] - if self.recover_delay: - self.existing["recover_delay"] = self.vrrp_global_info[ - "recoverDelay"] - - if self.virtual_ip: - if self.virtual_ip_info: - self.existing["interface"] = self.interface - self.existing["vrid"] = self.vrid - self.existing["virtual_ip_info"] = self.virtual_ip_info[ - "vrrpVirtualIpInfos"] - - if self.vrrp_group_info: - self.existing["interface"] = self.vrrp_group_info["ifName"] - self.existing["vrid"] = self.vrrp_group_info["vrrpId"] - self.existing["vrrp_type"] = self.vrrp_group_info["vrrpType"] - if self.vrrp_type == "admin": - self.existing["admin_ignore_if_down"] = self.vrrp_group_info[ - "adminIgnoreIfDown"] - if self.admin_vrid and self.admin_interface: - self.existing["admin_vrid"] = self.vrrp_group_info[ - "adminVrrpId"] - self.existing["admin_interface"] = self.vrrp_group_info[ - "adminIfName"] - self.existing["admin_flowdown"] = self.vrrp_group_info[ - "unflowdown"] - if self.priority: - self.existing["priority"] = self.vrrp_group_info["priority"] - if self.advertise_interval: - self.existing["advertise_interval"] = self.vrrp_group_info[ - "advertiseInterval"] - if self.preempt_timer_delay: - self.existing["preempt_timer_delay"] = self.vrrp_group_info[ - "delayTime"] - if self.holding_multiplier: - self.existing["holding_multiplier"] = self.vrrp_group_info[ - "holdMultiplier"] - if self.fast_resume: - fast_resume_exist = "disable" - fast_resume = self.vrrp_group_info["fastResume"] - if fast_resume == "true": - fast_resume_exist = "enable" - self.existing["fast_resume"] = fast_resume_exist - if self.auth_mode: - self.existing["auth_mode"] = self.vrrp_group_info[ - "authenticationMode"] - self.existing["is_plain"] = self.vrrp_group_info["isPlain"] - - def get_end_state(self): - """get end state info""" - - if self.gratuitous_arp_interval or self.version or self.recover_delay: - self.vrrp_global_info = self.get_vrrp_global_info() - if self.interface and self.vrid: - if self.virtual_ip: - self.virtual_ip_info = self.get_virtual_ip_info() - if self.virtual_ip_info: - self.vrrp_group_info = self.get_vrrp_group_info() - - if self.gratuitous_arp_interval: - self.end_state["gratuitous_arp_interval"] = self.vrrp_global_info[ - "gratuitousArpTimeOut"] - if self.version: - self.end_state["version"] = self.vrrp_global_info["version"] - if self.recover_delay: - self.end_state["recover_delay"] = self.vrrp_global_info[ - "recoverDelay"] - - if self.virtual_ip: - if self.virtual_ip_info: - self.end_state["interface"] = self.interface - self.end_state["vrid"] = self.vrid - self.end_state["virtual_ip_info"] = self.virtual_ip_info[ - "vrrpVirtualIpInfos"] - - if self.vrrp_group_info: - self.end_state["interface"] = self.vrrp_group_info["ifName"] - self.end_state["vrid"] = self.vrrp_group_info["vrrpId"] - self.end_state["vrrp_type"] = self.vrrp_group_info["vrrpType"] - if self.vrrp_type == "admin": - self.end_state["admin_ignore_if_down"] = self.vrrp_group_info[ - "adminIgnoreIfDown"] - if self.admin_vrid and self.admin_interface: - self.end_state["admin_vrid"] = self.vrrp_group_info[ - "adminVrrpId"] - self.end_state["admin_interface"] = self.vrrp_group_info[ - "adminIfName"] - self.end_state["admin_flowdown"] = self.vrrp_group_info[ - "unflowdown"] - if self.priority: - self.end_state["priority"] = self.vrrp_group_info["priority"] - if self.advertise_interval: - self.end_state["advertise_interval"] = self.vrrp_group_info[ - "advertiseInterval"] - if self.preempt_timer_delay: - self.end_state["preempt_timer_delay"] = self.vrrp_group_info[ - "delayTime"] - if self.holding_multiplier: - self.end_state["holding_multiplier"] = self.vrrp_group_info[ - "holdMultiplier"] - if self.fast_resume: - fast_resume_end = "disable" - fast_resume = self.vrrp_group_info["fastResume"] - if fast_resume == "true": - fast_resume_end = "enable" - self.end_state["fast_resume"] = fast_resume_end - if self.auth_mode: - self.end_state["auth_mode"] = self.vrrp_group_info[ - "authenticationMode"] - self.end_state["is_plain"] = self.vrrp_group_info["isPlain"] - if self.existing == self.end_state: - self.changed = False - - def work(self): - """worker""" - - self.check_params() - if self.gratuitous_arp_interval or self.version or self.recover_delay: - self.vrrp_global_info = self.get_vrrp_global_info() - if self.interface and self.vrid: - self.virtual_ip_info = self.get_virtual_ip_info() - if self.virtual_ip_info: - self.vrrp_group_info = self.get_vrrp_group_info() - self.get_proposed() - self.get_existing() - - if self.gratuitous_arp_interval or self.version or self.recover_delay: - if self.state == "present": - self.set_vrrp_global() - else: - self.delete_vrrp_global() - else: - if not self.interface or not self.vrid: - self.module.fail_json( - msg='Error: interface, vrid must be config at the same time.') - - if self.interface and self.vrid: - if self.virtual_ip: - if self.state == "present": - self.create_virtual_ip() - else: - self.delete_virtual_ip() - else: - if not self.vrrp_group_info: - self.module.fail_json( - msg='Error: The VRRP group does not exist.') - if self.admin_ignore_if_down == "true": - if self.vrrp_type != "admin": - self.module.fail_json( - msg='Error: vrrpType must be admin when admin_ignore_if_down is true.') - if self.admin_interface or self.admin_vrid: - if self.vrrp_type != "member": - self.module.fail_json( - msg='Error: it binds a VRRP group to an mVRRP group, vrrp_type must be "member".') - if not self.vrrp_type or not self.interface or not self.vrid: - self.module.fail_json( - msg='Error: admin_interface admin_vrid vrrp_type interface vrid must ' - 'be config at the same time.') - if self.auth_mode == "md5" and self.is_plain == "true": - self.module.fail_json( - msg='Error: is_plain can not be True when auth_mode is md5.') - - if self.state == "present": - self.set_vrrp_group() - else: - self.delete_vrrp_group() - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """ Module main """ - - argument_spec = dict( - interface=dict(type='str'), - vrid=dict(type='str'), - virtual_ip=dict(type='str'), - vrrp_type=dict(type='str', choices=['normal', 'member', 'admin']), - admin_ignore_if_down=dict(type='bool', default=False), - admin_vrid=dict(type='str'), - admin_interface=dict(type='str'), - admin_flowdown=dict(type='bool', default=False), - priority=dict(type='str'), - version=dict(type='str', choices=['v2', 'v3']), - advertise_interval=dict(type='str'), - preempt_timer_delay=dict(type='str'), - gratuitous_arp_interval=dict(type='str'), - recover_delay=dict(type='str'), - holding_multiplier=dict(type='str'), - auth_mode=dict(type='str', choices=['simple', 'md5', 'none']), - is_plain=dict(type='bool', default=False), - auth_key=dict(type='str'), - fast_resume=dict(type='str', choices=['enable', 'disable']), - state=dict(type='str', default='present', - choices=['present', 'absent']) - ) - - argument_spec.update(ce_argument_spec) - module = Vrrp(argument_spec=argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vxlan_arp.py b/plugins/modules/network/cloudengine/ce_vxlan_arp.py deleted file mode 100644 index 7198877371..0000000000 --- a/plugins/modules/network/cloudengine/ce_vxlan_arp.py +++ /dev/null @@ -1,692 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vxlan_arp -short_description: Manages ARP attributes of VXLAN on HUAWEI CloudEngine devices. -description: - - Manages ARP attributes of VXLAN on HUAWEI CloudEngine devices. -author: QijunPan (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - evn_bgp: - description: - - Enables EVN BGP. - choices: ['enable', 'disable'] - evn_source_ip: - description: - - Specifies the source address of an EVN BGP peer. - The value is in dotted decimal notation. - evn_peer_ip: - description: - - Specifies the IP address of an EVN BGP peer. - The value is in dotted decimal notation. - evn_server: - description: - - Configures the local device as the router reflector (RR) on the EVN network. - choices: ['enable', 'disable'] - evn_reflect_client: - description: - - Configures the local device as the route reflector (RR) and its peer as the client. - choices: ['enable', 'disable'] - vbdif_name: - description: - - Full name of VBDIF interface, i.e. Vbdif100. - arp_collect_host: - description: - - Enables EVN BGP or BGP EVPN to collect host information. - choices: ['enable', 'disable'] - host_collect_protocol: - description: - - Enables EVN BGP or BGP EVPN to advertise host information. - choices: ['bgp','none'] - bridge_domain_id: - description: - - Specifies a BD(bridge domain) ID. - The value is an integer ranging from 1 to 16777215. - arp_suppress: - description: - - Enables ARP broadcast suppression in a BD. - choices: ['enable', 'disable'] - state: - description: - - Determines whether the config should be present or not - on the device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' -- name: vxlan arp module test - hosts: ce128 - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Configure EVN BGP on Layer 2 and Layer 3 VXLAN gateways to establish EVN BGP peer relationships. - ce_vxlan_arp: - evn_bgp: enable - evn_source_ip: 6.6.6.6 - evn_peer_ip: 7.7.7.7 - provider: "{{ cli }}" - - name: Configure a Layer 3 VXLAN gateway as a BGP RR. - ce_vxlan_arp: - evn_bgp: enable - evn_server: enable - provider: "{{ cli }}" - - name: Enable EVN BGP on a Layer 3 VXLAN gateway to collect host information. - ce_vxlan_arp: - vbdif_name: Vbdif100 - arp_collect_host: enable - provider: "{{ cli }}" - - name: Enable Layer 2 and Layer 3 VXLAN gateways to use EVN BGP to advertise host information. - ce_vxlan_arp: - host_collect_protocol: bgp - provider: "{{ cli }}" - - name: Enable ARP broadcast suppression on a Layer 2 VXLAN gateway. - ce_vxlan_arp: - bridge_domain_id: 100 - arp_suppress: enable - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"evn_bgp": "enable", "evn_source_ip": "6.6.6.6", "evn_peer_ip":"7.7.7.7", state: "present"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"evn_bgp": "disable", "evn_source_ip": null, "evn_peer_ip": []} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"evn_bgp": "enable", "evn_source_ip": "6.6.6.6", "evn_peer_ip": ["7.7.7.7"]} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["evn bgp", - "source-address 6.6.6.6", - "peer 7.7.7.7"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec -from ansible.module_utils.connection import exec_command - - -def is_config_exist(cmp_cfg, test_cfg): - """is configuration exist""" - - if not cmp_cfg or not test_cfg: - return False - - return bool(test_cfg in cmp_cfg) - - -def is_valid_v4addr(addr): - """check is ipv4 addr is valid""" - - if addr.count('.') == 3: - addr_list = addr.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - -def get_evn_peers(config): - """get evn peer ip list""" - - get = re.findall(r"peer ([0-9]+.[0-9]+.[0-9]+.[0-9]+)", config) - if not get: - return None - else: - return list(set(get)) - - -def get_evn_srouce(config): - """get evn peer ip list""" - - get = re.findall( - r"source-address ([0-9]+.[0-9]+.[0-9]+.[0-9]+)", config) - if not get: - return None - else: - return get[0] - - -def get_evn_reflect_client(config): - """get evn reflect client list""" - - get = re.findall( - r"peer ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\s*reflect-client", config) - if not get: - return None - else: - return list(get) - - -class VxlanArp(object): - """ - Manages arp attributes of VXLAN. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.evn_bgp = self.module.params['evn_bgp'] - self.evn_source_ip = self.module.params['evn_source_ip'] - self.evn_peer_ip = self.module.params['evn_peer_ip'] - self.evn_server = self.module.params['evn_server'] - self.evn_reflect_client = self.module.params['evn_reflect_client'] - self.vbdif_name = self.module.params['vbdif_name'] - self.arp_collect_host = self.module.params['arp_collect_host'] - self.host_collect_protocol = self.module.params[ - 'host_collect_protocol'] - self.bridge_domain_id = self.module.params['bridge_domain_id'] - self.arp_suppress = self.module.params['arp_suppress'] - self.state = self.module.params['state'] - - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.config = "" # current config - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """init module""" - - required_together = [("vbdif_name", "arp_collect_host"), ("bridge_domain_id", "arp_suppress")] - self.module = AnsibleModule(argument_spec=self.spec, - required_together=required_together, - supports_check_mode=True) - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def get_current_config(self): - """get current configuration""" - - flags = list() - exp = r"| ignore-case section include evn bgp|host collect protocol bgp" - if self.vbdif_name: - exp += r"|^#\s+interface %s\s+" % self.vbdif_name.lower().capitalize().replace(" ", "") - - if self.bridge_domain_id: - exp += r"|^#\s+bridge-domain %s\s+" % self.bridge_domain_id - - flags.append(exp) - cfg_str = self.get_config(flags) - config = cfg_str.split("\n") - - exist_config = "" - for cfg in config: - if not cfg.startswith("display"): - exist_config += cfg - return exist_config - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) # show updates result - - def config_bridge_domain(self): - """manage bridge domain configuration""" - - if not self.bridge_domain_id: - return - - # bridge-domain bd-id - # [undo] arp broadcast-suppress enable - - cmd = "bridge-domain %s" % self.bridge_domain_id - if not is_config_exist(self.config, cmd): - self.module.fail_json(msg="Error: Bridge domain %s is not exist." % self.bridge_domain_id) - - cmd = "arp broadcast-suppress enable" - exist = is_config_exist(self.config, cmd) - if self.arp_suppress == "enable" and not exist: - self.cli_add_command("bridge-domain %s" % self.bridge_domain_id) - self.cli_add_command(cmd) - self.cli_add_command("quit") - elif self.arp_suppress == "disable" and exist: - self.cli_add_command("bridge-domain %s" % self.bridge_domain_id) - self.cli_add_command(cmd, undo=True) - self.cli_add_command("quit") - - def config_evn_bgp(self): - """enables EVN BGP and configure evn bgp command""" - - evn_bgp_view = False - evn_bgp_enable = False - - cmd = "evn bgp" - exist = is_config_exist(self.config, cmd) - if self.evn_bgp == "enable" or exist: - evn_bgp_enable = True - - # [undo] evn bgp - if self.evn_bgp: - if self.evn_bgp == "enable" and not exist: - self.cli_add_command(cmd) - evn_bgp_view = True - elif self.evn_bgp == "disable" and exist: - self.cli_add_command(cmd, undo=True) - return - - # [undo] source-address ip-address - if evn_bgp_enable and self.evn_source_ip: - cmd = "source-address %s" % self.evn_source_ip - exist = is_config_exist(self.config, cmd) - if self.state == "present" and not exist: - if not evn_bgp_view: - self.cli_add_command("evn bgp") - evn_bgp_view = True - self.cli_add_command(cmd) - elif self.state == "absent" and exist: - if not evn_bgp_view: - self.cli_add_command("evn bgp") - evn_bgp_view = True - self.cli_add_command(cmd, undo=True) - - # [undo] peer ip-address - # [undo] peer ipv4-address reflect-client - if evn_bgp_enable and self.evn_peer_ip: - cmd = "peer %s" % self.evn_peer_ip - exist = is_config_exist(self.config, cmd) - if self.state == "present": - if not exist: - if not evn_bgp_view: - self.cli_add_command("evn bgp") - evn_bgp_view = True - self.cli_add_command(cmd) - if self.evn_reflect_client == "enable": - self.cli_add_command( - "peer %s reflect-client" % self.evn_peer_ip) - else: - if self.evn_reflect_client: - cmd = "peer %s reflect-client" % self.evn_peer_ip - exist = is_config_exist(self.config, cmd) - if self.evn_reflect_client == "enable" and not exist: - if not evn_bgp_view: - self.cli_add_command("evn bgp") - evn_bgp_view = True - self.cli_add_command(cmd) - elif self.evn_reflect_client == "disable" and exist: - if not evn_bgp_view: - self.cli_add_command("evn bgp") - evn_bgp_view = True - self.cli_add_command(cmd, undo=True) - else: - if exist: - if not evn_bgp_view: - self.cli_add_command("evn bgp") - evn_bgp_view = True - self.cli_add_command(cmd, undo=True) - - # [undo] server enable - if evn_bgp_enable and self.evn_server: - cmd = "server enable" - exist = is_config_exist(self.config, cmd) - if self.evn_server == "enable" and not exist: - if not evn_bgp_view: - self.cli_add_command("evn bgp") - evn_bgp_view = True - self.cli_add_command(cmd) - elif self.evn_server == "disable" and exist: - if not evn_bgp_view: - self.cli_add_command("evn bgp") - evn_bgp_view = True - self.cli_add_command(cmd, undo=True) - - if evn_bgp_view: - self.cli_add_command("quit") - - def config_vbdif(self): - """configure command at the VBDIF interface view""" - - # interface vbdif bd-id - # [undo] arp collect host enable - - cmd = "interface %s" % self.vbdif_name.lower().capitalize() - exist = is_config_exist(self.config, cmd) - - if not exist: - self.module.fail_json( - msg="Error: Interface %s does not exist." % self.vbdif_name) - - cmd = "arp collect host enable" - exist = is_config_exist(self.config, cmd) - if self.arp_collect_host == "enable" and not exist: - self.cli_add_command("interface %s" % - self.vbdif_name.lower().capitalize()) - self.cli_add_command(cmd) - self.cli_add_command("quit") - elif self.arp_collect_host == "disable" and exist: - self.cli_add_command("interface %s" % - self.vbdif_name.lower().capitalize()) - self.cli_add_command(cmd, undo=True) - self.cli_add_command("quit") - - def config_host_collect_protocal(self): - """Enable EVN BGP or BGP EVPN to advertise host information""" - - # [undo] host collect protocol bgp - cmd = "host collect protocol bgp" - exist = is_config_exist(self.config, cmd) - - if self.state == "present": - if self.host_collect_protocol == "bgp" and not exist: - self.cli_add_command(cmd) - elif self.host_collect_protocol == "none" and exist: - self.cli_add_command(cmd, undo=True) - else: - if self.host_collect_protocol == "bgp" and exist: - self.cli_add_command(cmd, undo=True) - - def is_valid_vbdif(self, ifname): - """check is interface vbdif is valid""" - - if not ifname.upper().startswith('VBDIF'): - return False - bdid = self.vbdif_name.replace(" ", "").upper().replace("VBDIF", "") - if not bdid.isdigit(): - return False - if int(bdid) < 1 or int(bdid) > 16777215: - return False - - return True - - def check_params(self): - """Check all input params""" - - # bridge domain id check - if self.bridge_domain_id: - if not self.bridge_domain_id.isdigit(): - self.module.fail_json( - msg="Error: Bridge domain id is not digit.") - if int(self.bridge_domain_id) < 1 or int(self.bridge_domain_id) > 16777215: - self.module.fail_json( - msg="Error: Bridge domain id is not in the range from 1 to 16777215.") - - # evn_source_ip check - if self.evn_source_ip: - if not is_valid_v4addr(self.evn_source_ip): - self.module.fail_json(msg="Error: evn_source_ip is invalid.") - - # evn_peer_ip check - if self.evn_peer_ip: - if not is_valid_v4addr(self.evn_peer_ip): - self.module.fail_json(msg="Error: evn_peer_ip is invalid.") - - # vbdif_name check - if self.vbdif_name: - self.vbdif_name = self.vbdif_name.replace( - " ", "").lower().capitalize() - if not self.is_valid_vbdif(self.vbdif_name): - self.module.fail_json(msg="Error: vbdif_name is invalid.") - - # evn_reflect_client and evn_peer_ip must set at the same time - if self.evn_reflect_client and not self.evn_peer_ip: - self.module.fail_json( - msg="Error: evn_reflect_client and evn_peer_ip must set at the same time.") - - # evn_server and evn_reflect_client can not set at the same time - if self.evn_server == "enable" and self.evn_reflect_client == "enable": - self.module.fail_json( - msg="Error: evn_server and evn_reflect_client can not set at the same time.") - - def get_proposed(self): - """get proposed info""" - - if self.evn_bgp: - self.proposed["evn_bgp"] = self.evn_bgp - if self.evn_source_ip: - self.proposed["evn_source_ip"] = self.evn_source_ip - if self.evn_peer_ip: - self.proposed["evn_peer_ip"] = self.evn_peer_ip - if self.evn_server: - self.proposed["evn_server"] = self.evn_server - if self.evn_reflect_client: - self.proposed["evn_reflect_client"] = self.evn_reflect_client - if self.arp_collect_host: - self.proposed["arp_collect_host"] = self.arp_collect_host - if self.host_collect_protocol: - self.proposed["host_collect_protocol"] = self.host_collect_protocol - if self.arp_suppress: - self.proposed["arp_suppress"] = self.arp_suppress - if self.vbdif_name: - self.proposed["vbdif_name"] = self.evn_peer_ip - if self.bridge_domain_id: - self.proposed["bridge_domain_id"] = self.bridge_domain_id - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - evn_bgp_exist = is_config_exist(self.config, "evn bgp") - if evn_bgp_exist: - self.existing["evn_bgp"] = "enable" - else: - self.existing["evn_bgp"] = "disable" - - if evn_bgp_exist: - if is_config_exist(self.config, "server enable"): - self.existing["evn_server"] = "enable" - else: - self.existing["evn_server"] = "disable" - - self.existing["evn_source_ip"] = get_evn_srouce(self.config) - self.existing["evn_peer_ip"] = get_evn_peers(self.config) - self.existing["evn_reflect_client"] = get_evn_reflect_client( - self.config) - - if is_config_exist(self.config, "arp collect host enable"): - self.existing["host_collect_protocol"] = "enable" - else: - self.existing["host_collect_protocol"] = "disable" - - if is_config_exist(self.config, "host collect protocol bgp"): - self.existing["host_collect_protocol"] = "bgp" - else: - self.existing["host_collect_protocol"] = None - - if is_config_exist(self.config, "arp broadcast-suppress enable"): - self.existing["arp_suppress"] = "enable" - else: - self.existing["arp_suppress"] = "disable" - - def get_end_state(self): - """get end state info""" - - config = self.get_current_config() - evn_bgp_exist = is_config_exist(config, "evn bgp") - if evn_bgp_exist: - self.end_state["evn_bgp"] = "enable" - else: - self.end_state["evn_bgp"] = "disable" - - if evn_bgp_exist: - if is_config_exist(config, "server enable"): - self.end_state["evn_server"] = "enable" - else: - self.end_state["evn_server"] = "disable" - - self.end_state["evn_source_ip"] = get_evn_srouce(config) - self.end_state["evn_peer_ip"] = get_evn_peers(config) - self.end_state[ - "evn_reflect_client"] = get_evn_reflect_client(config) - - if is_config_exist(config, "arp collect host enable"): - self.end_state["host_collect_protocol"] = "enable" - else: - self.end_state["host_collect_protocol"] = "disable" - - if is_config_exist(config, "host collect protocol bgp"): - self.end_state["host_collect_protocol"] = "bgp" - else: - self.end_state["host_collect_protocol"] = None - - if is_config_exist(config, "arp broadcast-suppress enable"): - self.end_state["arp_suppress"] = "enable" - else: - self.end_state["arp_suppress"] = "disable" - - def work(self): - """worker""" - - self.check_params() - self.config = self.get_current_config() - self.get_existing() - self.get_proposed() - - # deal present or absent - if self.evn_bgp or self.evn_server or self.evn_peer_ip or self.evn_source_ip: - self.config_evn_bgp() - - if self.vbdif_name and self.arp_collect_host: - self.config_vbdif() - - if self.host_collect_protocol: - self.config_host_collect_protocal() - - if self.bridge_domain_id and self.arp_suppress: - self.config_bridge_domain() - - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - evn_bgp=dict(required=False, type='str', - choices=['enable', 'disable']), - evn_source_ip=dict(required=False, type='str'), - evn_peer_ip=dict(required=False, type='str'), - evn_server=dict(required=False, type='str', - choices=['enable', 'disable']), - evn_reflect_client=dict( - required=False, type='str', choices=['enable', 'disable']), - vbdif_name=dict(required=False, type='str'), - arp_collect_host=dict(required=False, type='str', - choices=['enable', 'disable']), - host_collect_protocol=dict( - required=False, type='str', choices=['bgp', 'none']), - bridge_domain_id=dict(required=False, type='str'), - arp_suppress=dict(required=False, type='str', - choices=['enable', 'disable']), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = VxlanArp(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vxlan_gateway.py b/plugins/modules/network/cloudengine/ce_vxlan_gateway.py deleted file mode 100644 index da8e9a23fc..0000000000 --- a/plugins/modules/network/cloudengine/ce_vxlan_gateway.py +++ /dev/null @@ -1,940 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vxlan_gateway -short_description: Manages gateway for the VXLAN network on HUAWEI CloudEngine devices. -description: - - Configuring Centralized All-Active Gateways or Distributed Gateway for - the VXLAN Network on HUAWEI CloudEngine devices. -author: QijunPan (@QijunPan) -notes: - - Ensure All-Active Gateways or Distributed Gateway for the VXLAN Network can not configure at the same time. - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - dfs_id: - description: - - Specifies the ID of a DFS group. - The value must be 1. - dfs_source_ip: - description: - - Specifies the IPv4 address bound to a DFS group. - The value is in dotted decimal notation. - dfs_source_vpn: - description: - - Specifies the name of a VPN instance bound to a DFS group. - The value is a string of 1 to 31 case-sensitive characters without spaces. - If the character string is quoted by double quotation marks, the character string can contain spaces. - The value C(_public_) is reserved and cannot be used as the VPN instance name. - dfs_udp_port: - description: - - Specifies the UDP port number of the DFS group. - The value is an integer that ranges from 1025 to 65535. - dfs_all_active: - description: - - Creates all-active gateways. - choices: ['enable', 'disable'] - dfs_peer_ip: - description: - - Configure the IP address of an all-active gateway peer. - The value is in dotted decimal notation. - dfs_peer_vpn: - description: - - Specifies the name of the VPN instance that is associated with all-active gateway peer. - The value is a string of 1 to 31 case-sensitive characters, spaces not supported. - When double quotation marks are used around the string, spaces are allowed in the string. - The value C(_public_) is reserved and cannot be used as the VPN instance name. - vpn_instance: - description: - - Specifies the name of a VPN instance. - The value is a string of 1 to 31 case-sensitive characters, spaces not supported. - When double quotation marks are used around the string, spaces are allowed in the string. - The value C(_public_) is reserved and cannot be used as the VPN instance name. - vpn_vni: - description: - - Specifies a VNI ID. - Binds a VXLAN network identifier (VNI) to a virtual private network (VPN) instance. - The value is an integer ranging from 1 to 16000000. - vbdif_name: - description: - - Full name of VBDIF interface, i.e. Vbdif100. - vbdif_bind_vpn: - description: - - Specifies the name of the VPN instance that is associated with the interface. - The value is a string of 1 to 31 case-sensitive characters, spaces not supported. - When double quotation marks are used around the string, spaces are allowed in the string. - The value C(_public_) is reserved and cannot be used as the VPN instance name. - vbdif_mac: - description: - - Specifies a MAC address for a VBDIF interface. - The value is in the format of H-H-H. Each H is a 4-digit hexadecimal number, such as C(00e0) or C(fc01). - If an H contains less than four digits, 0s are added ahead. For example, C(e0) is equal to C(00e0). - A MAC address cannot be all 0s or 1s or a multicast MAC address. - arp_distribute_gateway: - description: - - Enable the distributed gateway function on VBDIF interface. - choices: ['enable','disable'] - arp_direct_route: - description: - - Enable VLINK direct route on VBDIF interface. - choices: ['enable','disable'] - state: - description: - - Determines whether the config should be present or not - on the device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' -- name: vxlan gateway module test - hosts: ce128 - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Configuring Centralized All-Active Gateways for the VXLAN Network - ce_vxlan_gateway: - dfs_id: 1 - dfs_source_ip: 6.6.6.6 - dfs_all_active: enable - dfs_peer_ip: 7.7.7.7 - provider: "{{ cli }}" - - name: Bind the VPN instance to a Layer 3 gateway, enable distributed gateway, and configure host route advertisement. - ce_vxlan_gateway: - vbdif_name: Vbdif100 - vbdif_bind_vpn: vpn1 - arp_distribute_gateway: enable - arp_direct_route: enable - provider: "{{ cli }}" - - name: Assign a VNI to a VPN instance. - ce_vxlan_gateway: - vpn_instance: vpn1 - vpn_vni: 100 - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"dfs_id": "1", "dfs_source_ip": "6.6.6.6", "dfs_all_active":"enable", "dfs_peer_ip": "7.7.7.7"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"dfs_id": "1", "dfs_source_ip": null, "evn_peer_ip": [], "dfs_all_active": "disable"} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"dfs_id": "1", "evn_source_ip": "6.6.6.6", "evn_source_vpn": null, - "evn_peers": [{"ip": "7.7.7.7", "vpn": ""}], "dfs_all_active": "enable"} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["dfs-group 1", - "source ip 6.6.6.6", - "active-active-gateway", - "peer 7.7.7.7"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec -from ansible.module_utils.connection import exec_command - - -def is_config_exist(cmp_cfg, test_cfg): - """is configuration exist?""" - - if not cmp_cfg or not test_cfg: - return False - - return bool(test_cfg in cmp_cfg) - - -def is_valid_v4addr(addr): - """check is ipv4 addr""" - - if not addr: - return False - - if addr.count('.') == 3: - addr_list = addr.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - -def mac_format(mac): - """convert mac format to xxxx-xxxx-xxxx""" - - if not mac: - return None - - if mac.count("-") != 2: - return None - - addrs = mac.split("-") - for i in range(3): - if not addrs[i] or not addrs[i].isalnum(): - return None - if len(addrs[i]) < 1 or len(addrs[i]) > 4: - return None - try: - addrs[i] = int(addrs[i], 16) - except ValueError: - return None - - try: - return "%04x-%04x-%04x" % (addrs[0], addrs[1], addrs[2]) - except ValueError: - return None - except TypeError: - return None - - -def get_dfs_source_ip(config): - """get dfs source ip address""" - - get = re.findall(r"source ip ([0-9]+.[0-9]+.[0-9]+.[0-9]+)", config) - if not get: - return None - else: - return get[0] - - -def get_dfs_source_vpn(config): - """get dfs source ip vpn instance name""" - - get = re.findall( - r"source ip [0-9]+.[0-9]+.[0-9]+.[0-9]+ vpn-instance (\S+)", config) - if not get: - return None - else: - return get[0] - - -def get_dfs_udp_port(config): - """get dfs udp port""" - - get = re.findall(r"udp port (\d+)", config) - if not get: - return None - else: - return get[0] - - -def get_dfs_peers(config): - """get evn peer ip list""" - - get = re.findall( - r"peer ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\s?(vpn-instance)?\s?(\S*)", config) - if not get: - return None - else: - peers = list() - for item in get: - peers.append(dict(ip=item[0], vpn=item[2])) - return peers - - -def get_ip_vpn(config): - """get ip vpn instance""" - - get = re.findall(r"ip vpn-instance (\S+)", config) - if not get: - return None - else: - return get[0] - - -def get_ip_vpn_vni(config): - """get ip vpn vxlan vni""" - - get = re.findall(r"vxlan vni (\d+)", config) - if not get: - return None - else: - return get[0] - - -def get_vbdif_vpn(config): - """get ip vpn name of interface vbdif""" - - get = re.findall(r"ip binding vpn-instance (\S+)", config) - if not get: - return None - else: - return get[0] - - -def get_vbdif_mac(config): - """get mac address of interface vbdif""" - - get = re.findall( - r" mac-address ([0-9a-fA-F]{1,4}-[0-9a-fA-F]{1,4}-[0-9a-fA-F]{1,4})", config) - if not get: - return None - else: - return get[0] - - -class VxlanGateway(object): - """ - Manages Gateway for the VXLAN Network. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.dfs_id = self.module.params['dfs_id'] - self.dfs_source_ip = self.module.params['dfs_source_ip'] - self.dfs_source_vpn = self.module.params['dfs_source_vpn'] - self.dfs_udp_port = self.module.params['dfs_udp_port'] - self.dfs_all_active = self.module.params['dfs_all_active'] - self.dfs_peer_ip = self.module.params['dfs_peer_ip'] - self.dfs_peer_vpn = self.module.params['dfs_peer_vpn'] - self.vpn_instance = self.module.params['vpn_instance'] - self.vpn_vni = self.module.params['vpn_vni'] - self.vbdif_name = self.module.params['vbdif_name'] - self.vbdif_mac = self.module.params['vbdif_mac'] - self.vbdif_bind_vpn = self.module.params['vbdif_bind_vpn'] - self.arp_distribute_gateway = self.module.params['arp_distribute_gateway'] - self.arp_direct_route = self.module.params['arp_direct_route'] - self.state = self.module.params['state'] - - # host info - self.host = self.module.params['host'] - self.username = self.module.params['username'] - self.port = self.module.params['port'] - - # state - self.config = "" # current config - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def get_current_config(self): - """get current configuration""" - - flags = list() - exp = r" | ignore-case section include ^#\s+dfs-group" - if self.vpn_instance: - exp += r"|^#\s+ip vpn-instance %s" % self.vpn_instance - if self.vbdif_name: - exp += r"|^#\s+interface %s" % self.vbdif_name - flags.append(exp) - return self.get_config(flags) - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) # show updates result - - def config_dfs_group(self): - """manage Dynamic Fabric Service (DFS) group configuration""" - - if not self.dfs_id: - return - - dfs_view = False - view_cmd = "dfs-group %s" % self.dfs_id - exist = is_config_exist(self.config, view_cmd) - if self.state == "present" and not exist: - self.cli_add_command(view_cmd) - dfs_view = True - - # undo dfs-group dfs-group-id - if self.state == "absent" and exist: - if not self.dfs_source_ip and not self.dfs_udp_port and not self.dfs_all_active and not self.dfs_peer_ip: - self.cli_add_command(view_cmd, undo=True) - return - - # [undo] source ip ip-address [ vpn-instance vpn-instance-name ] - if self.dfs_source_ip: - cmd = "source ip %s" % self.dfs_source_ip - if self.dfs_source_vpn: - cmd += " vpn-instance %s" % self.dfs_source_vpn - exist = is_config_exist(self.config, cmd) - if self.state == "present" and not exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(cmd) - if self.state == "absent" and exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(cmd, undo=True) - - # [undo] udp port port-number - if self.dfs_udp_port: - cmd = "udp port %s" % self.dfs_udp_port - exist = is_config_exist(self.config, cmd) - if self.state == "present" and not exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(cmd) - elif self.state == "absent" and exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(cmd, undo=True) - - # [undo] active-active-gateway - # [undo]peer[ vpn-instance vpn-instance-name ] - aa_cmd = "active-active-gateway" - aa_exist = is_config_exist(self.config, aa_cmd) - aa_view = False - if self.dfs_all_active == "disable": - if aa_exist: - cmd = "peer %s" % self.dfs_peer_ip - if self.dfs_source_vpn: - cmd += " vpn-instance %s" % self.dfs_peer_vpn - exist = is_config_exist(self.config, cmd) - if self.state == "absent" and exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(aa_cmd) - self.cli_add_command(cmd, undo=True) - self.cli_add_command("quit") - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(aa_cmd, undo=True) - elif self.dfs_all_active == "enable": - if not aa_exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(aa_cmd) - aa_view = True - - if self.dfs_peer_ip: - cmd = "peer %s" % self.dfs_peer_ip - if self.dfs_peer_vpn: - cmd += " vpn-instance %s" % self.dfs_peer_vpn - exist = is_config_exist(self.config, cmd) - - if self.state == "present" and not exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - if not aa_view: - self.cli_add_command(aa_cmd) - self.cli_add_command(cmd) - self.cli_add_command("quit") - elif self.state == "absent" and exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - if not aa_view: - self.cli_add_command(aa_cmd) - self.cli_add_command(cmd, undo=True) - self.cli_add_command("quit") - else: # not input dfs_all_active - if aa_exist and self.dfs_peer_ip: - cmd = "peer %s" % self.dfs_peer_ip - if self.dfs_peer_vpn: - cmd += " vpn-instance %s" % self.dfs_peer_vpn - exist = is_config_exist(self.config, cmd) - if self.state == "present" and not exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(aa_cmd) - self.cli_add_command(cmd) - self.cli_add_command("quit") - elif self.state == "absent" and exist: - if not dfs_view: - self.cli_add_command(view_cmd) - dfs_view = True - self.cli_add_command(aa_cmd) - self.cli_add_command(cmd, undo=True) - self.cli_add_command("quit") - else: - pass - elif not aa_exist and self.dfs_peer_ip and self.state == "present": - self.module.fail_json( - msg="Error: All-active gateways is not enable.") - else: - pass - - if dfs_view: - self.cli_add_command("quit") - - def config_ip_vpn(self): - """configure command at the ip vpn view""" - - if not self.vpn_instance or not self.vpn_vni: - return - - # ip vpn-instance vpn-instance-name - view_cmd = "ip vpn-instance %s" % self.vpn_instance - exist = is_config_exist(self.config, view_cmd) - if not exist: - self.module.fail_json( - msg="Error: ip vpn instance %s is not exist." % self.vpn_instance) - - # [undo] vxlan vni vni-id - cmd = "vxlan vni %s" % self.vpn_vni - exist = is_config_exist(self.config, cmd) - if self.state == "present" and not exist: - self.cli_add_command(view_cmd) - self.cli_add_command(cmd) - self.cli_add_command("quit") - elif self.state == "absent" and exist: - self.cli_add_command(view_cmd) - self.cli_add_command(cmd, undo=True) - self.cli_add_command("quit") - - def config_vbdif(self): - """configure command at the VBDIF interface view""" - - if not self.vbdif_name: - return - - vbdif_cmd = "interface %s" % self.vbdif_name.lower().capitalize() - exist = is_config_exist(self.config, vbdif_cmd) - - if not exist: - self.module.fail_json( - msg="Error: Interface %s is not exist." % self.vbdif_name) - - # interface vbdif bd-id - # [undo] ip binding vpn-instance vpn-instance-name - vbdif_view = False - if self.vbdif_bind_vpn: - cmd = "ip binding vpn-instance %s" % self.vbdif_bind_vpn - exist = is_config_exist(self.config, cmd) - - if self.state == "present" and not exist: - if not vbdif_view: - self.cli_add_command(vbdif_cmd) - vbdif_view = True - self.cli_add_command(cmd) - elif self.state == "absent" and exist: - if not vbdif_view: - self.cli_add_command(vbdif_cmd) - vbdif_view = True - self.cli_add_command(cmd, undo=True) - - # [undo] arp distribute-gateway enable - if self.arp_distribute_gateway: - cmd = "arp distribute-gateway enable" - exist = is_config_exist(self.config, cmd) - if self.arp_distribute_gateway == "enable" and not exist: - if not vbdif_view: - self.cli_add_command(vbdif_cmd) - vbdif_view = True - self.cli_add_command(cmd) - elif self.arp_distribute_gateway == "disable" and exist: - if not vbdif_view: - self.cli_add_command(vbdif_cmd) - vbdif_view = True - self.cli_add_command(cmd, undo=True) - - # [undo] arp direct-route enable - if self.arp_direct_route: - cmd = "arp direct-route enable" - exist = is_config_exist(self.config, cmd) - if self.arp_direct_route == "enable" and not exist: - if not vbdif_view: - self.cli_add_command(vbdif_cmd) - vbdif_view = True - self.cli_add_command(cmd) - elif self.arp_direct_route == "disable" and exist: - if not vbdif_view: - self.cli_add_command(vbdif_cmd) - vbdif_view = True - self.cli_add_command(cmd, undo=True) - - # mac-address mac-address - # undo mac-address - if self.vbdif_mac: - cmd = "mac-address %s" % self.vbdif_mac - exist = is_config_exist(self.config, cmd) - if self.state == "present" and not exist: - if not vbdif_view: - self.cli_add_command(vbdif_cmd) - vbdif_view = True - self.cli_add_command(cmd) - elif self.state == "absent" and exist: - if not vbdif_view: - self.cli_add_command(vbdif_cmd) - vbdif_view = True - self.cli_add_command("undo mac-address") - - # quit - if vbdif_view: - self.cli_add_command("quit") - - def is_valid_vbdif(self, ifname): - """check is interface vbdif""" - - if not ifname.upper().startswith('VBDIF'): - return False - bdid = self.vbdif_name.replace(" ", "").upper().replace("VBDIF", "") - if not bdid.isdigit(): - return False - if int(bdid) < 1 or int(bdid) > 16777215: - return False - - return True - - def is_valid_ip_vpn(self, vpname): - """check ip vpn""" - - if not vpname: - return False - - if vpname == "_public_": - self.module.fail_json( - msg="Error: The value C(_public_) is reserved and cannot be used as the VPN instance name.") - - if len(vpname) < 1 or len(vpname) > 31: - self.module.fail_json( - msg="Error: IP vpn name length is not in the range from 1 to 31.") - - return True - - def check_params(self): - """Check all input params""" - - # dfs id check - if self.dfs_id: - if not self.dfs_id.isdigit(): - self.module.fail_json(msg="Error: DFS id is not digit.") - if int(self.dfs_id) != 1: - self.module.fail_json(msg="Error: DFS is not 1.") - - # dfs_source_ip check - if self.dfs_source_ip: - if not is_valid_v4addr(self.dfs_source_ip): - self.module.fail_json(msg="Error: dfs_source_ip is invalid.") - # dfs_source_vpn check - if self.dfs_source_vpn and not self.is_valid_ip_vpn(self.dfs_source_vpn): - self.module.fail_json(msg="Error: dfs_source_vpn is invalid.") - - # dfs_source_vpn and dfs_source_ip must set at the same time - if self.dfs_source_vpn and not self.dfs_source_ip: - self.module.fail_json( - msg="Error: dfs_source_vpn and dfs_source_ip must set at the same time.") - - # dfs_udp_port check - if self.dfs_udp_port: - if not self.dfs_udp_port.isdigit(): - self.module.fail_json( - msg="Error: dfs_udp_port id is not digit.") - if int(self.dfs_udp_port) < 1025 or int(self.dfs_udp_port) > 65535: - self.module.fail_json( - msg="dfs_udp_port is not ranges from 1025 to 65535.") - - # dfs_peer_ip check - if self.dfs_peer_ip: - if not is_valid_v4addr(self.dfs_peer_ip): - self.module.fail_json(msg="Error: dfs_peer_ip is invalid.") - # dfs_peer_vpn check - if self.dfs_peer_vpn and not self.is_valid_ip_vpn(self.dfs_peer_vpn): - self.module.fail_json(msg="Error: dfs_peer_vpn is invalid.") - - # dfs_peer_vpn and dfs_peer_ip must set at the same time - if self.dfs_peer_vpn and not self.dfs_peer_ip: - self.module.fail_json( - msg="Error: dfs_peer_vpn and dfs_peer_ip must set at the same time.") - - # vpn_instance check - if self.vpn_instance and not self.is_valid_ip_vpn(self.vpn_instance): - self.module.fail_json(msg="Error: vpn_instance is invalid.") - - # vpn_vni check - if self.vpn_vni: - if not self.vpn_vni.isdigit(): - self.module.fail_json(msg="Error: vpn_vni id is not digit.") - if int(self.vpn_vni) < 1 or int(self.vpn_vni) > 16000000: - self.module.fail_json( - msg="vpn_vni is not ranges from 1 to 16000000.") - - # vpn_instance and vpn_vni must set at the same time - if bool(self.vpn_instance) != bool(self.vpn_vni): - self.module.fail_json( - msg="Error: vpn_instance and vpn_vni must set at the same time.") - - # vbdif_name check - if self.vbdif_name: - self.vbdif_name = self.vbdif_name.replace(" ", "").lower().capitalize() - if not self.is_valid_vbdif(self.vbdif_name): - self.module.fail_json(msg="Error: vbdif_name is invalid.") - - # vbdif_mac check - if self.vbdif_mac: - mac = mac_format(self.vbdif_mac) - if not mac: - self.module.fail_json(msg="Error: vbdif_mac is invalid.") - self.vbdif_mac = mac - - # vbdif_bind_vpn check - if self.vbdif_bind_vpn and not self.is_valid_ip_vpn(self.vbdif_bind_vpn): - self.module.fail_json(msg="Error: vbdif_bind_vpn is invalid.") - - # All-Active Gateways or Distributed Gateway config can not set at the - # same time. - if self.dfs_id: - if self.vpn_vni or self.arp_distribute_gateway == "enable": - self.module.fail_json(msg="Error: All-Active Gateways or Distributed Gateway config " - "can not set at the same time.") - - def get_proposed(self): - """get proposed info""" - - if self.dfs_id: - self.proposed["dfs_id"] = self.dfs_id - self.proposed["dfs_source_ip"] = self.dfs_source_ip - self.proposed["dfs_source_vpn"] = self.dfs_source_vpn - self.proposed["dfs_udp_port"] = self.dfs_udp_port - self.proposed["dfs_all_active"] = self.dfs_all_active - self.proposed["dfs_peer_ip"] = self.dfs_peer_ip - self.proposed["dfs_peer_vpn"] = self.dfs_peer_vpn - - if self.vpn_instance: - self.proposed["vpn_instance"] = self.vpn_instance - self.proposed["vpn_vni"] = self.vpn_vni - - if self.vbdif_name: - self.proposed["vbdif_name"] = self.vbdif_name - self.proposed["vbdif_mac"] = self.vbdif_mac - self.proposed["vbdif_bind_vpn"] = self.vbdif_bind_vpn - self.proposed[ - "arp_distribute_gateway"] = self.arp_distribute_gateway - self.proposed["arp_direct_route"] = self.arp_direct_route - - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if not self.config: - return - - if is_config_exist(self.config, "dfs-group 1"): - self.existing["dfs_id"] = "1" - self.existing["dfs_source_ip"] = get_dfs_source_ip(self.config) - self.existing["dfs_source_vpn"] = get_dfs_source_vpn(self.config) - self.existing["dfs_udp_port"] = get_dfs_udp_port(self.config) - if is_config_exist(self.config, "active-active-gateway"): - self.existing["dfs_all_active"] = "enable" - self.existing["dfs_peers"] = get_dfs_peers(self.config) - else: - self.existing["dfs_all_active"] = "disable" - - if self.vpn_instance: - self.existing["vpn_instance"] = get_ip_vpn(self.config) - self.existing["vpn_vni"] = get_ip_vpn_vni(self.config) - - if self.vbdif_name: - self.existing["vbdif_name"] = self.vbdif_name - self.existing["vbdif_mac"] = get_vbdif_mac(self.config) - self.existing["vbdif_bind_vpn"] = get_vbdif_vpn(self.config) - if is_config_exist(self.config, "arp distribute-gateway enable"): - self.existing["arp_distribute_gateway"] = "enable" - else: - self.existing["arp_distribute_gateway"] = "disable" - if is_config_exist(self.config, "arp direct-route enable"): - self.existing["arp_direct_route"] = "enable" - else: - self.existing["arp_direct_route"] = "disable" - - def get_end_state(self): - """get end state info""" - - config = self.get_current_config() - if not config: - return - - if is_config_exist(config, "dfs-group 1"): - self.end_state["dfs_id"] = "1" - self.end_state["dfs_source_ip"] = get_dfs_source_ip(config) - self.end_state["dfs_source_vpn"] = get_dfs_source_vpn(config) - self.end_state["dfs_udp_port"] = get_dfs_udp_port(config) - if is_config_exist(config, "active-active-gateway"): - self.end_state["dfs_all_active"] = "enable" - self.end_state["dfs_peers"] = get_dfs_peers(config) - else: - self.end_state["dfs_all_active"] = "disable" - - if self.vpn_instance: - self.end_state["vpn_instance"] = get_ip_vpn(config) - self.end_state["vpn_vni"] = get_ip_vpn_vni(config) - - if self.vbdif_name: - self.end_state["vbdif_name"] = self.vbdif_name - self.end_state["vbdif_mac"] = get_vbdif_mac(config) - self.end_state["vbdif_bind_vpn"] = get_vbdif_vpn(config) - if is_config_exist(config, "arp distribute-gateway enable"): - self.end_state["arp_distribute_gateway"] = "enable" - else: - self.end_state["arp_distribute_gateway"] = "disable" - if is_config_exist(config, "arp direct-route enable"): - self.end_state["arp_direct_route"] = "enable" - else: - self.end_state["arp_direct_route"] = "disable" - - def work(self): - """worker""" - - self.check_params() - self.config = self.get_current_config() - self.get_existing() - self.get_proposed() - - # deal present or absent - if self.dfs_id: - self.config_dfs_group() - - if self.vpn_instance: - self.config_ip_vpn() - - if self.vbdif_name: - self.config_vbdif() - - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - dfs_id=dict(required=False, type='str'), - dfs_source_ip=dict(required=False, type='str'), - dfs_source_vpn=dict(required=False, type='str'), - dfs_udp_port=dict(required=False, type='str'), - dfs_all_active=dict(required=False, type='str', - choices=['enable', 'disable']), - dfs_peer_ip=dict(required=False, type='str'), - dfs_peer_vpn=dict(required=False, type='str'), - vpn_instance=dict(required=False, type='str'), - vpn_vni=dict(required=False, type='str'), - vbdif_name=dict(required=False, type='str'), - vbdif_mac=dict(required=False, type='str'), - vbdif_bind_vpn=dict(required=False, type='str'), - arp_distribute_gateway=dict( - required=False, type='str', choices=['enable', 'disable']), - arp_direct_route=dict(required=False, type='str', - choices=['enable', 'disable']), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = VxlanGateway(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vxlan_global.py b/plugins/modules/network/cloudengine/ce_vxlan_global.py deleted file mode 100644 index 954c5bd7e3..0000000000 --- a/plugins/modules/network/cloudengine/ce_vxlan_global.py +++ /dev/null @@ -1,543 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vxlan_global -short_description: Manages global attributes of VXLAN and bridge domain on HUAWEI CloudEngine devices. -description: - - Manages global attributes of VXLAN and bridge domain on HUAWEI CloudEngine devices. -author: QijunPan (@QijunPan) -notes: - - Recommended connection is C(network_cli). - - This module also works with C(local) connections for legacy playbooks. -options: - bridge_domain_id: - description: - - Specifies a bridge domain ID. - The value is an integer ranging from 1 to 16777215. - tunnel_mode_vxlan: - description: - - Set the tunnel mode to VXLAN when configuring the VXLAN feature. - choices: ['enable', 'disable'] - nvo3_prevent_loops: - description: - - Loop prevention of VXLAN traffic in non-enhanced mode. - When the device works in non-enhanced mode, - inter-card forwarding of VXLAN traffic may result in loops. - choices: ['enable', 'disable'] - nvo3_acl_extend: - description: - - Enabling or disabling the VXLAN ACL extension function. - choices: ['enable', 'disable'] - nvo3_gw_enhanced: - description: - - Configuring the Layer 3 VXLAN Gateway to Work in Non-loopback Mode. - choices: ['l2', 'l3'] - nvo3_service_extend: - description: - - Enabling or disabling the VXLAN service extension function. - choices: ['enable', 'disable'] - nvo3_eth_trunk_hash: - description: - - Eth-Trunk from load balancing VXLAN packets in optimized mode. - choices: ['enable','disable'] - nvo3_ecmp_hash: - description: - - Load balancing of VXLAN packets through ECMP in optimized mode. - choices: ['enable', 'disable'] - state: - description: - - Determines whether the config should be present or not - on the device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' -- name: vxlan global module test - hosts: ce128 - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Create bridge domain and set tunnel mode to VXLAN - ce_vxlan_global: - bridge_domain_id: 100 - nvo3_acl_extend: enable - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"bridge_domain_id": "100", "nvo3_acl_extend": "enable", state="present"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"bridge_domain": {"80", "90"}, "nvo3_acl_extend": "disable"} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"bridge_domain_id": {"80", "90", "100"}, "nvo3_acl_extend": "enable"} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["bridge-domain 100", - "ip tunnel mode vxlan"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import load_config -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec -from ansible.module_utils.connection import exec_command - - -def is_config_exist(cmp_cfg, test_cfg): - """is configuration exist?""" - - if not cmp_cfg or not test_cfg: - return False - - return bool(test_cfg in cmp_cfg) - - -def get_nvo3_gw_enhanced(cmp_cfg): - """get the Layer 3 VXLAN Gateway to Work in Non-loopback Mode """ - - get = re.findall( - r"assign forward nvo3-gateway enhanced (l[2|3])", cmp_cfg) - if not get: - return None - else: - return get[0] - - -class VxlanGlobal(object): - """ - Manages global attributes of VXLAN and bridge domain. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.tunnel_mode_vxlan = self.module.params['tunnel_mode_vxlan'] - self.nvo3_prevent_loops = self.module.params['nvo3_prevent_loops'] - self.nvo3_acl_extend = self.module.params['nvo3_acl_extend'] - self.nvo3_gw_enhanced = self.module.params['nvo3_gw_enhanced'] - self.nvo3_service_extend = self.module.params['nvo3_service_extend'] - self.nvo3_eth_trunk_hash = self.module.params['nvo3_eth_trunk_hash'] - self.nvo3_ecmp_hash = self.module.params['nvo3_ecmp_hash'] - self.bridge_domain_id = self.module.params['bridge_domain_id'] - self.state = self.module.params['state'] - - # state - self.config = "" # current config - self.bd_info = list() - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def init_module(self): - """init module""" - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def cli_load_config(self, commands): - """load config by cli""" - - if not self.module.check_mode: - load_config(self.module, commands) - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'display current-configuration ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - rc, out, err = exec_command(self.module, cmd) - if rc != 0: - self.module.fail_json(msg=err) - cfg = str(out).strip() - - return cfg - - def get_current_config(self): - """get current configuration""" - - flags = list() - exp = " include-default | include vxlan|assign | exclude undo" - flags.append(exp) - return self.get_config(flags) - - def cli_add_command(self, command, undo=False): - """add command to self.update_cmd and self.commands""" - - if undo and command.lower() not in ["quit", "return"]: - cmd = "undo " + command - else: - cmd = command - - self.commands.append(cmd) # set to device - if command.lower() not in ["quit", "return"]: - self.updates_cmd.append(cmd) # show updates result - - def get_bd_list(self): - """get bridge domain list""" - flags = list() - bd_info = list() - exp = " include-default | include bridge-domain | exclude undo" - flags.append(exp) - bd_str = self.get_config(flags) - if not bd_str: - return bd_info - bd_num = re.findall(r'bridge-domain\s*([0-9]+)', bd_str) - bd_info.extend(bd_num) - return bd_info - - def config_bridge_domain(self): - """manage bridge domain""" - - if not self.bridge_domain_id: - return - - cmd = "bridge-domain %s" % self.bridge_domain_id - exist = self.bridge_domain_id in self.bd_info - if self.state == "present": - if not exist: - self.cli_add_command(cmd) - self.cli_add_command("quit") - else: - if exist: - self.cli_add_command(cmd, undo=True) - - def config_tunnel_mode(self): - """config tunnel mode vxlan""" - - # ip tunnel mode vxlan - if self.tunnel_mode_vxlan: - cmd = "ip tunnel mode vxlan" - exist = is_config_exist(self.config, cmd) - if self.tunnel_mode_vxlan == "enable": - if not exist: - self.cli_add_command(cmd) - else: - if exist: - self.cli_add_command(cmd, undo=True) - - def config_assign_forward(self): - """config assign forward command""" - - # [undo] assign forward nvo3-gateway enhanced {l2|l3) - if self.nvo3_gw_enhanced: - cmd = "assign forward nvo3-gateway enhanced %s" % self.nvo3_gw_enhanced - exist = is_config_exist(self.config, cmd) - if self.state == "present": - if not exist: - self.cli_add_command(cmd) - else: - if exist: - self.cli_add_command(cmd, undo=True) - - # [undo] assign forward nvo3 f-linecard compatibility enable - if self.nvo3_prevent_loops: - cmd = "assign forward nvo3 f-linecard compatibility enable" - exist = is_config_exist(self.config, cmd) - if self.nvo3_prevent_loops == "enable": - if not exist: - self.cli_add_command(cmd) - else: - if exist: - self.cli_add_command(cmd, undo=True) - - # [undo] assign forward nvo3 acl extend enable - if self.nvo3_acl_extend: - cmd = "assign forward nvo3 acl extend enable" - exist = is_config_exist(self.config, cmd) - if self.nvo3_acl_extend == "enable": - if not exist: - self.cli_add_command(cmd) - else: - if exist: - self.cli_add_command(cmd, undo=True) - - # [undo] assign forward nvo3 service extend enable - if self.nvo3_service_extend: - cmd = "assign forward nvo3 service extend enable" - exist = is_config_exist(self.config, cmd) - if self.nvo3_service_extend == "enable": - if not exist: - self.cli_add_command(cmd) - else: - if exist: - self.cli_add_command(cmd, undo=True) - - # assign forward nvo3 eth-trunk hash {enable|disable} - if self.nvo3_eth_trunk_hash: - cmd = "assign forward nvo3 eth-trunk hash enable" - exist = is_config_exist(self.config, cmd) - if self.nvo3_eth_trunk_hash == "enable": - if not exist: - self.cli_add_command(cmd) - else: - if exist: - self.cli_add_command(cmd, undo=True) - - # [undo] assign forward nvo3 ecmp hash enable - if self.nvo3_ecmp_hash: - cmd = "assign forward nvo3 ecmp hash enable" - exist = is_config_exist(self.config, cmd) - if self.nvo3_ecmp_hash == "enable": - if not exist: - self.cli_add_command(cmd) - else: - if exist: - self.cli_add_command(cmd, undo=True) - - def check_params(self): - """Check all input params""" - - # bridge domain id check - if self.bridge_domain_id: - if not self.bridge_domain_id.isdigit(): - self.module.fail_json( - msg="Error: bridge domain id is not digit.") - if int(self.bridge_domain_id) < 1 or int(self.bridge_domain_id) > 16777215: - self.module.fail_json( - msg="Error: bridge domain id is not in the range from 1 to 16777215.") - - def get_proposed(self): - """get proposed info""" - - if self.tunnel_mode_vxlan: - self.proposed["tunnel_mode_vxlan"] = self.tunnel_mode_vxlan - if self.nvo3_prevent_loops: - self.proposed["nvo3_prevent_loops"] = self.nvo3_prevent_loops - if self.nvo3_acl_extend: - self.proposed["nvo3_acl_extend"] = self.nvo3_acl_extend - if self.nvo3_gw_enhanced: - self.proposed["nvo3_gw_enhanced"] = self.nvo3_gw_enhanced - if self.nvo3_service_extend: - self.proposed["nvo3_service_extend"] = self.nvo3_service_extend - if self.nvo3_eth_trunk_hash: - self.proposed["nvo3_eth_trunk_hash"] = self.nvo3_eth_trunk_hash - if self.nvo3_ecmp_hash: - self.proposed["nvo3_ecmp_hash"] = self.nvo3_ecmp_hash - if self.bridge_domain_id: - self.proposed["bridge_domain_id"] = self.bridge_domain_id - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - self.existing["bridge_domain"] = self.bd_info - - cmd = "ip tunnel mode vxlan" - exist = is_config_exist(self.config, cmd) - if exist: - self.existing["tunnel_mode_vxlan"] = "enable" - else: - self.existing["tunnel_mode_vxlan"] = "disable" - - cmd = "assign forward nvo3 f-linecard compatibility enable" - exist = is_config_exist(self.config, cmd) - if exist: - self.existing["nvo3_prevent_loops"] = "enable" - else: - self.existing["nvo3_prevent_loops"] = "disable" - - cmd = "assign forward nvo3 acl extend enable" - exist = is_config_exist(self.config, cmd) - if exist: - self.existing["nvo3_acl_extend"] = "enable" - else: - self.existing["nvo3_acl_extend"] = "disable" - - self.existing["nvo3_gw_enhanced"] = get_nvo3_gw_enhanced( - self.config) - - cmd = "assign forward nvo3 service extend enable" - exist = is_config_exist(self.config, cmd) - if exist: - self.existing["nvo3_service_extend"] = "enable" - else: - self.existing["nvo3_service_extend"] = "disable" - - cmd = "assign forward nvo3 eth-trunk hash enable" - exist = is_config_exist(self.config, cmd) - if exist: - self.existing["nvo3_eth_trunk_hash"] = "enable" - else: - self.existing["nvo3_eth_trunk_hash"] = "disable" - - cmd = "assign forward nvo3 ecmp hash enable" - exist = is_config_exist(self.config, cmd) - if exist: - self.existing["nvo3_ecmp_hash"] = "enable" - else: - self.existing["nvo3_ecmp_hash"] = "disable" - - def get_end_state(self): - """get end state info""" - - config = self.get_current_config() - - self.end_state["bridge_domain"] = self.get_bd_list() - - cmd = "ip tunnel mode vxlan" - exist = is_config_exist(config, cmd) - if exist: - self.end_state["tunnel_mode_vxlan"] = "enable" - else: - self.end_state["tunnel_mode_vxlan"] = "disable" - - cmd = "assign forward nvo3 f-linecard compatibility enable" - exist = is_config_exist(config, cmd) - if exist: - self.end_state["nvo3_prevent_loops"] = "enable" - else: - self.end_state["nvo3_prevent_loops"] = "disable" - - cmd = "assign forward nvo3 acl extend enable" - exist = is_config_exist(config, cmd) - if exist: - self.end_state["nvo3_acl_extend"] = "enable" - else: - self.end_state["nvo3_acl_extend"] = "disable" - - self.end_state["nvo3_gw_enhanced"] = get_nvo3_gw_enhanced(config) - - cmd = "assign forward nvo3 service extend enable" - exist = is_config_exist(config, cmd) - if exist: - self.end_state["nvo3_service_extend"] = "enable" - else: - self.end_state["nvo3_service_extend"] = "disable" - - cmd = "assign forward nvo3 eth-trunk hash enable" - exist = is_config_exist(config, cmd) - if exist: - self.end_state["nvo3_eth_trunk_hash"] = "enable" - else: - self.end_state["nvo3_eth_trunk_hash"] = "disable" - - cmd = "assign forward nvo3 ecmp hash enable" - exist = is_config_exist(config, cmd) - if exist: - self.end_state["nvo3_ecmp_hash"] = "enable" - else: - self.end_state["nvo3_ecmp_hash"] = "disable" - if self.existing == self.end_state: - self.changed = True - - def work(self): - """worker""" - - self.check_params() - self.config = self.get_current_config() - self.bd_info = self.get_bd_list() - self.get_existing() - self.get_proposed() - - # deal present or absent - self.config_bridge_domain() - self.config_tunnel_mode() - self.config_assign_forward() - if self.commands: - self.cli_load_config(self.commands) - self.changed = True - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - tunnel_mode_vxlan=dict(required=False, type='str', - choices=['enable', 'disable']), - nvo3_prevent_loops=dict(required=False, type='str', - choices=['enable', 'disable']), - nvo3_acl_extend=dict(required=False, type='str', - choices=['enable', 'disable']), - nvo3_gw_enhanced=dict(required=False, type='str', - choices=['l2', 'l3']), - nvo3_service_extend=dict(required=False, type='str', - choices=['enable', 'disable']), - nvo3_eth_trunk_hash=dict(required=False, type='str', - choices=['enable', 'disable']), - nvo3_ecmp_hash=dict(required=False, type='str', - choices=['enable', 'disable']), - bridge_domain_id=dict(required=False, type='str'), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = VxlanGlobal(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vxlan_tunnel.py b/plugins/modules/network/cloudengine/ce_vxlan_tunnel.py deleted file mode 100644 index 0ca5c15656..0000000000 --- a/plugins/modules/network/cloudengine/ce_vxlan_tunnel.py +++ /dev/null @@ -1,944 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vxlan_tunnel -short_description: Manages VXLAN tunnel configuration on HUAWEI CloudEngine devices. -description: - - This module offers the ability to set the VNI and mapped to the BD, - and configure an ingress replication list on HUAWEI CloudEngine devices. -author: - - Li Yanfeng (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - bridge_domain_id: - description: - - Specifies a bridge domain ID. The value is an integer ranging from 1 to 16777215. - vni_id: - description: - - Specifies a VXLAN network identifier (VNI) ID. The value is an integer ranging from 1 to 16000000. - nve_name: - description: - - Specifies the number of an NVE interface. The value ranges from 1 to 2. - nve_mode: - description: - - Specifies the working mode of an NVE interface. - choices: ['mode-l2','mode-l3'] - peer_list_ip: - description: - - Specifies the IP address of a remote VXLAN tunnel endpoints (VTEP). - The value is in dotted decimal notation. - protocol_type: - description: - - The operation type of routing protocol. - choices: ['bgp','null'] - source_ip: - description: - - Specifies an IP address for a source VTEP. The value is in dotted decimal notation. - state: - description: - - Manage the state of the resource. - default: present - choices: ['present','absent'] -''' -EXAMPLES = ''' -- name: vxlan tunnel module test - hosts: ce128 - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Make sure nve_name is exist, ensure vni_id and protocol_type is configured on Nve1 interface. - ce_vxlan_tunnel: - nve_name: Nve1 - vni_id: 100 - protocol_type: bgp - state: present - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {nve_interface_name": "Nve1", nve_mode": "mode-l2", "source_ip": "0.0.0.0"} -existing: - description: - - k/v pairs of existing rollback - returned: always - type: dict - sample: {nve_interface_name": "Nve1", nve_mode": "mode-l3", "source_ip": "0.0.0.0"} - -updates: - description: command sent to the device - returned: always - type: list - sample: ["interface Nve1", - "mode l3"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -end_state: - description: k/v pairs of configuration after module execution - returned: always - type: dict - sample: {nve_interface_name": "Nve1", nve_mode": "mode-l3", "source_ip": "0.0.0.0"} -''' -import re -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_VNI_BD_INFO = """ - - - - - - - - - - -""" - -CE_NC_GET_NVE_INFO = """ - - - - - %s - - - - -""" - -CE_NC_MERGE_VNI_BD_ID = """ - - - - - %s - %s - - - - -""" - -CE_NC_DELETE_VNI_BD_ID = """ - - - - - %s - %s - - - - -""" - -CE_NC_MERGE_NVE_MODE = """ - - - - - %s - %s - - - - -""" - -CE_NC_MERGE_NVE_SOURCE_IP_PROTOCOL = """ - - - - - %s - %s - - - - -""" - -CE_NC_MERGE_VNI_PEER_ADDRESS_IP_HEAD = """ - - - - - %s - - - %s -""" - -CE_NC_MERGE_VNI_PEER_ADDRESS_IP_END = """ - - - - - - -""" -CE_NC_MERGE_VNI_PEER_ADDRESS_IP_MERGE = """ - - - %s - - -""" - -CE_NC_DELETE_VNI_PEER_ADDRESS_IP_HEAD = """ - - - - - %s - - - %s -""" -CE_NC_DELETE_VNI_PEER_ADDRESS_IP_END = """ - - - - - - -""" -CE_NC_DELETE_VNI_PEER_ADDRESS_IP_DELETE = """ - - - %s - - -""" - -CE_NC_DELETE_PEER_ADDRESS_IP_HEAD = """ - - - - - %s - - - %s -""" -CE_NC_DELETE_PEER_ADDRESS_IP_END = """ - - - - - - -""" -CE_NC_MERGE_VNI_PROTOCOL = """ - - - - - %s - - - %s - %s - - - - - - -""" - -CE_NC_DELETE_VNI_PROTOCOL = """ - - - - - %s - - - %s - %s - - - - - - -""" - - -def is_valid_address(address): - """check ip-address is valid""" - - if address.find('.') != -1: - addr_list = address.split('.') - if len(addr_list) != 4: - return False - for each_num in addr_list: - if not each_num.isdigit(): - return False - if int(each_num) > 255: - return False - return True - - return False - - -class VxlanTunnel(object): - """ - Manages vxlan tunnel configuration. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.init_module() - - # module input info - self.bridge_domain_id = self.module.params['bridge_domain_id'] - self.vni_id = self.module.params['vni_id'] - self.nve_name = self.module.params['nve_name'] - self.nve_mode = self.module.params['nve_mode'] - self.peer_list_ip = self.module.params['peer_list_ip'] - self.protocol_type = self.module.params['protocol_type'] - self.source_ip = self.module.params['source_ip'] - self.state = self.module.params['state'] - - # state - self.changed = False - self.updates_cmd = list() - self.results = dict() - self.existing = dict() - self.proposed = dict() - self.end_state = dict() - - # configuration nve info - self.vni2bd_info = None - self.nve_info = None - - def init_module(self): - """ init module """ - - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_vni2bd_dict(self): - """ get vni2bd attributes dict.""" - - vni2bd_info = dict() - # get vni bd info - conf_str = CE_NC_GET_VNI_BD_INFO - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return vni2bd_info - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - # get vni to bridge domain id info - root = ElementTree.fromstring(xml_str) - vni2bd_info["vni2BdInfos"] = list() - vni2bds = root.findall("nvo3/nvo3Vni2Bds/nvo3Vni2Bd") - - if vni2bds: - for vni2bd in vni2bds: - vni_dict = dict() - for ele in vni2bd: - if ele.tag in ["vniId", "bdId"]: - vni_dict[ele.tag] = ele.text - vni2bd_info["vni2BdInfos"].append(vni_dict) - - return vni2bd_info - - def check_nve_interface(self, nve_name): - """is nve interface exist""" - - if not self.nve_info: - return False - - if self.nve_info["ifName"] == nve_name: - return True - return False - - def get_nve_dict(self, nve_name): - """ get nve interface attributes dict.""" - - nve_info = dict() - # get nve info - conf_str = CE_NC_GET_NVE_INFO % nve_name - xml_str = get_nc_config(self.module, conf_str) - if "" in xml_str: - return nve_info - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get nve info - root = ElementTree.fromstring(xml_str) - - nvo3 = root.find("nvo3/nvo3Nves/nvo3Nve") - if nvo3: - for nve in nvo3: - if nve.tag in ["srcAddr", "ifName", "nveType"]: - nve_info[nve.tag] = nve.text - - # get nve vni info - nve_info["vni_peer_protocols"] = list() - - vni_members = root.findall( - "nvo3/nvo3Nves/nvo3Nve/vniMembers/vniMember") - if vni_members: - for member in vni_members: - vni_dict = dict() - for ele in member: - if ele.tag in ["vniId", "protocol"]: - vni_dict[ele.tag] = ele.text - nve_info["vni_peer_protocols"].append(vni_dict) - - # get vni peer address ip info - nve_info["vni_peer_ips"] = list() - - re_find = re.findall(r'(.*?)\s*' - r'(.*?)\s*' - r'(.*?)', xml_str) - - if re_find: - for vni_peers in re_find: - vni_info = dict() - vni_peer = re.findall(r'(.*?)', vni_peers[2]) - if vni_peer: - vni_info["vniId"] = vni_peers[0] - vni_peer_list = list() - for peer in vni_peer: - vni_peer_list.append(peer) - vni_info["peerAddr"] = vni_peer_list - nve_info["vni_peer_ips"].append(vni_info) - - return nve_info - - def check_nve_name(self): - """Gets Nve interface name""" - - if self.nve_name is None: - return False - if self.nve_name in ["Nve1", "Nve2"]: - return True - return False - - def is_vni_bd_exist(self, vni_id, bd_id): - """is vni to bridge-domain-id exist""" - - if not self.vni2bd_info: - return False - - for vni2bd in self.vni2bd_info["vni2BdInfos"]: - if vni2bd["vniId"] == vni_id and vni2bd["bdId"] == bd_id: - return True - return False - - def is_vni_bd_change(self, vni_id, bd_id): - """is vni to bridge-domain-id change""" - - if not self.vni2bd_info: - return True - - for vni2bd in self.vni2bd_info["vni2BdInfos"]: - if vni2bd["vniId"] == vni_id and vni2bd["bdId"] == bd_id: - return False - return True - - def is_nve_mode_exist(self, nve_name, mode): - """is nve interface mode exist""" - - if not self.nve_info: - return False - - if self.nve_info["ifName"] == nve_name and self.nve_info["nveType"] == mode: - return True - return False - - def is_nve_mode_change(self, nve_name, mode): - """is nve interface mode change""" - - if not self.nve_info: - return True - - if self.nve_info["ifName"] == nve_name and self.nve_info["nveType"] == mode: - return False - return True - - def is_nve_source_ip_exist(self, nve_name, source_ip): - """is vni to bridge-domain-id exist""" - - if not self.nve_info: - return False - - if self.nve_info["ifName"] == nve_name and self.nve_info["srcAddr"] == source_ip: - return True - return False - - def is_nve_source_ip_change(self, nve_name, source_ip): - """is vni to bridge-domain-id change""" - - if not self.nve_info: - return True - - if self.nve_info["ifName"] == nve_name and self.nve_info["srcAddr"] == source_ip: - return False - return True - - def is_vni_protocol_exist(self, nve_name, vni_id, protocol_type): - """is vni protocol exist""" - - if not self.nve_info: - return False - if self.nve_info["ifName"] == nve_name: - for member in self.nve_info["vni_peer_protocols"]: - if member["vniId"] == vni_id and member["protocol"] == protocol_type: - return True - return False - - def is_vni_protocol_change(self, nve_name, vni_id, protocol_type): - """is vni protocol change""" - - if not self.nve_info: - return True - if self.nve_info["ifName"] == nve_name: - for member in self.nve_info["vni_peer_protocols"]: - if member["vniId"] == vni_id and member["protocol"] == protocol_type: - return False - return True - - def is_vni_peer_list_exist(self, nve_name, vni_id, peer_ip): - """is vni peer list exist""" - - if not self.nve_info: - return False - if self.nve_info["ifName"] == nve_name: - for member in self.nve_info["vni_peer_ips"]: - if member["vniId"] == vni_id and peer_ip in member["peerAddr"]: - return True - return False - - def is_vni_peer_list_change(self, nve_name, vni_id, peer_ip_list): - """is vni peer list change""" - - if not self.nve_info: - return True - - if self.nve_info["ifName"] == nve_name: - if not self.nve_info["vni_peer_ips"]: - return True - - nve_peer_info = list() - for nve_peer in self.nve_info["vni_peer_ips"]: - if nve_peer["vniId"] == vni_id: - nve_peer_info.append(nve_peer) - - if not nve_peer_info: - return True - - nve_peer_list = nve_peer_info[0]["peerAddr"] - for peer in peer_ip_list: - if peer not in nve_peer_list: - return True - - return False - - def config_merge_vni2bd(self, bd_id, vni_id): - """config vni to bd id""" - - if self.is_vni_bd_change(vni_id, bd_id): - cfg_xml = CE_NC_MERGE_VNI_BD_ID % (vni_id, bd_id) - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "MERGE_VNI_BD") - self.updates_cmd.append("bridge-domain %s" % bd_id) - self.updates_cmd.append("vxlan vni %s" % vni_id) - self.changed = True - - def config_merge_mode(self, nve_name, mode): - """config nve mode""" - - if self.is_nve_mode_change(nve_name, mode): - cfg_xml = CE_NC_MERGE_NVE_MODE % (nve_name, mode) - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "MERGE_MODE") - self.updates_cmd.append("interface %s" % nve_name) - if mode == "mode-l3": - self.updates_cmd.append("mode l3") - else: - self.updates_cmd.append("undo mode l3") - self.changed = True - - def config_merge_source_ip(self, nve_name, source_ip): - """config nve source ip""" - - if self.is_nve_source_ip_change(nve_name, source_ip): - cfg_xml = CE_NC_MERGE_NVE_SOURCE_IP_PROTOCOL % ( - nve_name, source_ip) - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "MERGE_SOURCE_IP") - self.updates_cmd.append("interface %s" % nve_name) - self.updates_cmd.append("source %s" % source_ip) - self.changed = True - - def config_merge_vni_peer_ip(self, nve_name, vni_id, peer_ip_list): - """config vni peer ip""" - - if self.is_vni_peer_list_change(nve_name, vni_id, peer_ip_list): - cfg_xml = CE_NC_MERGE_VNI_PEER_ADDRESS_IP_HEAD % ( - nve_name, vni_id) - for peer_ip in peer_ip_list: - cfg_xml += CE_NC_MERGE_VNI_PEER_ADDRESS_IP_MERGE % peer_ip - cfg_xml += CE_NC_MERGE_VNI_PEER_ADDRESS_IP_END - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "MERGE_VNI_PEER_IP") - self.updates_cmd.append("interface %s" % nve_name) - - for peer_ip in peer_ip_list: - cmd_output = "vni %s head-end peer-list %s" % (vni_id, peer_ip) - self.updates_cmd.append(cmd_output) - self.changed = True - - def config_merge_vni_protocol_type(self, nve_name, vni_id, protocol_type): - """config vni protocol type""" - - if self.is_vni_protocol_change(nve_name, vni_id, protocol_type): - cfg_xml = CE_NC_MERGE_VNI_PROTOCOL % ( - nve_name, vni_id, protocol_type) - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "MERGE_VNI_PEER_PROTOCOL") - self.updates_cmd.append("interface %s" % nve_name) - - if protocol_type == "bgp": - self.updates_cmd.append( - "vni %s head-end peer-list protocol %s" % (vni_id, protocol_type)) - else: - self.updates_cmd.append( - "undo vni %s head-end peer-list protocol bgp" % vni_id) - self.changed = True - - def config_delete_vni2bd(self, bd_id, vni_id): - """remove vni to bd id""" - - if not self.is_vni_bd_exist(vni_id, bd_id): - return - cfg_xml = CE_NC_DELETE_VNI_BD_ID % (vni_id, bd_id) - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "DELETE_VNI_BD") - self.updates_cmd.append( - "bridge-domain %s" % bd_id) - self.updates_cmd.append( - "undo vxlan vni %s" % vni_id) - - self.changed = True - - def config_delete_mode(self, nve_name, mode): - """nve mode""" - - if mode == "mode-l3": - if not self.is_nve_mode_exist(nve_name, mode): - return - cfg_xml = CE_NC_MERGE_NVE_MODE % (nve_name, "mode-l2") - - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "DELETE_MODE") - self.updates_cmd.append("interface %s" % nve_name) - self.updates_cmd.append("undo mode l3") - self.changed = True - else: - self.module.fail_json( - msg='Error: Can not configure undo mode l2.') - - def config_delete_source_ip(self, nve_name, source_ip): - """nve source ip""" - - if not self.is_nve_source_ip_exist(nve_name, source_ip): - return - ipaddr = "0.0.0.0" - cfg_xml = CE_NC_MERGE_NVE_SOURCE_IP_PROTOCOL % ( - nve_name, ipaddr) - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "DELETE_SOURCE_IP") - self.updates_cmd.append("interface %s" % nve_name) - self.updates_cmd.append("undo source %s" % source_ip) - self.changed = True - - def config_delete_vni_peer_ip(self, nve_name, vni_id, peer_ip_list): - """remove vni peer ip""" - - for peer_ip in peer_ip_list: - if not self.is_vni_peer_list_exist(nve_name, vni_id, peer_ip): - self.module.fail_json(msg='Error: The %s does not exist' % peer_ip) - - config = False - - nve_peer_info = list() - for nve_peer in self.nve_info["vni_peer_ips"]: - if nve_peer["vniId"] == vni_id: - nve_peer_info = nve_peer.get("peerAddr") - for peer in nve_peer_info: - if peer not in peer_ip_list: - config = True - - if not config: - cfg_xml = CE_NC_DELETE_VNI_PEER_ADDRESS_IP_HEAD % ( - nve_name, vni_id) - for peer_ip in peer_ip_list: - cfg_xml += CE_NC_DELETE_VNI_PEER_ADDRESS_IP_DELETE % peer_ip - cfg_xml += CE_NC_DELETE_VNI_PEER_ADDRESS_IP_END - else: - cfg_xml = CE_NC_DELETE_PEER_ADDRESS_IP_HEAD % ( - nve_name, vni_id) - for peer_ip in peer_ip_list: - cfg_xml += CE_NC_DELETE_VNI_PEER_ADDRESS_IP_DELETE % peer_ip - cfg_xml += CE_NC_DELETE_PEER_ADDRESS_IP_END - - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "DELETE_VNI_PEER_IP") - self.updates_cmd.append("interface %s" % nve_name) - - for peer_ip in peer_ip_list: - cmd_output = "undo vni %s head-end peer-list %s" % (vni_id, peer_ip) - self.updates_cmd.append(cmd_output) - - self.changed = True - - def config_delete_vni_protocol_type(self, nve_name, vni_id, protocol_type): - """remove vni protocol type""" - - if not self.is_vni_protocol_exist(nve_name, vni_id, protocol_type): - return - - cfg_xml = CE_NC_DELETE_VNI_PROTOCOL % (nve_name, vni_id, protocol_type) - recv_xml = set_nc_config(self.module, cfg_xml) - self.check_response(recv_xml, "DELETE_VNI_PEER_PROTOCOL") - self.updates_cmd.append("interface %s" % nve_name) - self.updates_cmd.append( - "undo vni %s head-end peer-list protocol bgp " % vni_id) - self.changed = True - - def check_params(self): - """Check all input params""" - - # bridge_domain_id check - if self.bridge_domain_id: - if not self.bridge_domain_id.isdigit(): - self.module.fail_json( - msg='Error: The parameter of bridge domain id is invalid.') - if int(self.bridge_domain_id) > 16777215 or int(self.bridge_domain_id) < 1: - self.module.fail_json( - msg='Error: The bridge domain id must be an integer between 1 and 16777215.') - # vni_id check - if self.vni_id: - if not self.vni_id.isdigit(): - self.module.fail_json( - msg='Error: The parameter of vni id is invalid.') - if int(self.vni_id) > 16000000 or int(self.vni_id) < 1: - self.module.fail_json( - msg='Error: The vni id must be an integer between 1 and 16000000.') - - # nve_name check - if self.nve_name: - if not self.check_nve_name(): - self.module.fail_json( - msg='Error: Error: NVE interface %s is invalid.' % self.nve_name) - - # peer_list_ip check - if self.peer_list_ip: - for peer_ip in self.peer_list_ip: - if not is_valid_address(peer_ip): - self.module.fail_json( - msg='Error: The ip address %s is invalid.' % self.peer_list_ip) - # source_ip check - if self.source_ip: - if not is_valid_address(self.source_ip): - self.module.fail_json( - msg='Error: The ip address %s is invalid.' % self.source_ip) - - def get_proposed(self): - """get proposed info""" - - if self.bridge_domain_id: - self.proposed["bridge_domain_id"] = self.bridge_domain_id - if self.vni_id: - self.proposed["vni_id"] = self.vni_id - if self.nve_name: - self.proposed["nve_name"] = self.nve_name - if self.nve_mode: - self.proposed["nve_mode"] = self.nve_mode - if self.peer_list_ip: - self.proposed["peer_list_ip"] = self.peer_list_ip - if self.source_ip: - self.proposed["source_ip"] = self.source_ip - if self.state: - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if self.vni2bd_info: - self.existing["vni_to_bridge_domain"] = self.vni2bd_info[ - "vni2BdInfos"] - - if self.nve_info: - self.existing["nve_interface_name"] = self.nve_info["ifName"] - self.existing["source_ip"] = self.nve_info["srcAddr"] - self.existing["nve_mode"] = self.nve_info["nveType"] - self.existing["vni_peer_list_ip"] = self.nve_info[ - "vni_peer_ips"] - self.existing["vni_peer_list_protocol"] = self.nve_info[ - "vni_peer_protocols"] - - def get_end_state(self): - """get end state info""" - - vni2bd_info = self.get_vni2bd_dict() - if vni2bd_info: - self.end_state["vni_to_bridge_domain"] = vni2bd_info["vni2BdInfos"] - - nve_info = self.get_nve_dict(self.nve_name) - if nve_info: - self.end_state["nve_interface_name"] = nve_info["ifName"] - self.end_state["source_ip"] = nve_info["srcAddr"] - self.end_state["nve_mode"] = nve_info["nveType"] - self.end_state["vni_peer_list_ip"] = nve_info[ - "vni_peer_ips"] - self.end_state["vni_peer_list_protocol"] = nve_info[ - "vni_peer_protocols"] - - def work(self): - """worker""" - - self.check_params() - self.vni2bd_info = self.get_vni2bd_dict() - if self.nve_name: - self.nve_info = self.get_nve_dict(self.nve_name) - self.get_existing() - self.get_proposed() - # deal present or absent - if self.state == "present": - if self.bridge_domain_id and self.vni_id: - self.config_merge_vni2bd(self.bridge_domain_id, self.vni_id) - if self.nve_name: - if self.check_nve_interface(self.nve_name): - if self.nve_mode: - self.config_merge_mode(self.nve_name, self.nve_mode) - if self.source_ip: - self.config_merge_source_ip( - self.nve_name, self.source_ip) - if self.vni_id and self.peer_list_ip: - self.config_merge_vni_peer_ip( - self.nve_name, self.vni_id, self.peer_list_ip) - if self.vni_id and self.protocol_type: - self.config_merge_vni_protocol_type( - self.nve_name, self.vni_id, self.protocol_type) - else: - self.module.fail_json( - msg='Error: Nve interface %s does not exist.' % self.nve_name) - - else: - if self.bridge_domain_id and self.vni_id: - self.config_delete_vni2bd(self.bridge_domain_id, self.vni_id) - if self.nve_name: - if self.check_nve_interface(self.nve_name): - if self.nve_mode: - self.config_delete_mode(self.nve_name, self.nve_mode) - if self.source_ip: - self.config_delete_source_ip( - self.nve_name, self.source_ip) - if self.vni_id and self.peer_list_ip: - self.config_delete_vni_peer_ip( - self.nve_name, self.vni_id, self.peer_list_ip) - if self.vni_id and self.protocol_type: - self.config_delete_vni_protocol_type( - self.nve_name, self.vni_id, self.protocol_type) - else: - self.module.fail_json( - msg='Error: Nve interface %s does not exist.' % self.nve_name) - - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - bridge_domain_id=dict(required=False), - vni_id=dict(required=False, type='str'), - nve_name=dict(required=False, type='str'), - nve_mode=dict(required=False, choices=['mode-l2', 'mode-l3']), - peer_list_ip=dict(required=False, type='list'), - protocol_type=dict(required=False, type='str', choices=[ - 'bgp', 'null']), - - source_ip=dict(required=False), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = VxlanTunnel(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudengine/ce_vxlan_vap.py b/plugins/modules/network/cloudengine/ce_vxlan_vap.py deleted file mode 100644 index a25ee3d8c5..0000000000 --- a/plugins/modules/network/cloudengine/ce_vxlan_vap.py +++ /dev/null @@ -1,937 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ce_vxlan_vap -short_description: Manages VXLAN virtual access point on HUAWEI CloudEngine Devices. -description: - - Manages VXLAN Virtual access point on HUAWEI CloudEngine Devices. -author: QijunPan (@QijunPan) -notes: - - This module requires the netconf system service be enabled on the remote device being managed. - - Recommended connection is C(netconf). - - This module also works with C(local) connections for legacy playbooks. -options: - bridge_domain_id: - description: - - Specifies a bridge domain ID. - The value is an integer ranging from 1 to 16777215. - bind_vlan_id: - description: - - Specifies the VLAN binding to a BD(Bridge Domain). - The value is an integer ranging ranging from 1 to 4094. - l2_sub_interface: - description: - - Specifies an Sub-Interface full name, i.e. "10GE1/0/41.1". - The value is a string of 1 to 63 case-insensitive characters, spaces supported. - encapsulation: - description: - - Specifies an encapsulation type of packets allowed to pass through a Layer 2 sub-interface. - choices: ['dot1q', 'default', 'untag', 'qinq', 'none'] - ce_vid: - description: - - When I(encapsulation) is 'dot1q', specifies a VLAN ID in the outer VLAN tag. - When I(encapsulation) is 'qinq', specifies an outer VLAN ID for - double-tagged packets to be received by a Layer 2 sub-interface. - The value is an integer ranging from 1 to 4094. - pe_vid: - description: - - When I(encapsulation) is 'qinq', specifies an inner VLAN ID for - double-tagged packets to be received by a Layer 2 sub-interface. - The value is an integer ranging from 1 to 4094. - state: - description: - - Determines whether the config should be present or not - on the device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = ''' -- name: vxlan vap module test - hosts: ce128 - connection: local - gather_facts: no - vars: - cli: - host: "{{ inventory_hostname }}" - port: "{{ ansible_ssh_port }}" - username: "{{ username }}" - password: "{{ password }}" - transport: cli - - tasks: - - - name: Create a mapping between a VLAN and a BD - ce_vxlan_vap: - bridge_domain_id: 100 - bind_vlan_id: 99 - provider: "{{ cli }}" - - - name: Bind a Layer 2 sub-interface to a BD - ce_vxlan_vap: - bridge_domain_id: 100 - l2_sub_interface: 10GE2/0/20.1 - provider: "{{ cli }}" - - - name: Configure an encapsulation type on a Layer 2 sub-interface - ce_vxlan_vap: - l2_sub_interface: 10GE2/0/20.1 - encapsulation: dot1q - provider: "{{ cli }}" -''' - -RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"bridge_domain_id": "100", "bind_vlan_id": "99", state="present"} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"bridge_domain_id": "100", "bind_intf_list": ["10GE2/0/20.1", "10GE2/0/20.2"], - "bind_vlan_list": []} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"bridge_domain_id": "100", "bind_intf_list": ["110GE2/0/20.1", "10GE2/0/20.2"], - "bind_vlan_list": ["99"]} -updates: - description: commands sent to the device - returned: always - type: list - sample: ["bridge-domain 100", - "l2 binding vlan 99"] -changed: - description: check to see if a change was made on the device - returned: always - type: bool - sample: true -''' - -from xml.etree import ElementTree -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec - -CE_NC_GET_BD_VAP = """ - - - - - %s - - - - - - - - - - - - -""" - -CE_NC_MERGE_BD_VLAN = """ - - - - - %s - - %s:%s - - - - - -""" - -CE_NC_MERGE_BD_INTF = """ - - - - - %s - - - %s - - - - - - -""" - -CE_NC_DELETE_BD_INTF = """ - - - - - %s - - - %s - - - - - - -""" - -CE_NC_GET_ENCAP = """ - - - - - %s - - - - - - - - - - - - - - -""" - -CE_NC_SET_ENCAP = """ - - - - - %s - %s - - - - -""" - -CE_NC_UNSET_ENCAP = """ - - - - - %s - none - - - - -""" - -CE_NC_SET_ENCAP_DOT1Q = """ - - - - - %s - dot1q - - %s:%s - - - - - -""" - -CE_NC_SET_ENCAP_QINQ = """ - - - - - %s - qinq - - - %s - %s:%s - - - - - - -""" - - -def vlan_vid_to_bitmap(vid): - """convert VLAN list to VLAN bitmap""" - - vlan_bit = ['0'] * 1024 - int_vid = int(vid) - j = int_vid // 4 - bit_int = 0x8 >> (int_vid % 4) - vlan_bit[j] = str(hex(bit_int))[2] - - return ''.join(vlan_bit) - - -def bitmap_to_vlan_list(bitmap): - """convert VLAN bitmap to VLAN list""" - - tmp = list() - if not bitmap: - return tmp - - bit_len = len(bitmap) - for i in range(bit_len): - if bitmap[i] == "0": - continue - bit = int(bitmap[i]) - if bit & 0x8: - tmp.append(str(i * 4)) - if bit & 0x4: - tmp.append(str(i * 4 + 1)) - if bit & 0x2: - tmp.append(str(i * 4 + 2)) - if bit & 0x1: - tmp.append(str(i * 4 + 3)) - - return tmp - - -def is_vlan_bitmap_empty(bitmap): - """check VLAN bitmap empty""" - - if not bitmap or len(bitmap) == 0: - return True - - for bit in bitmap: - if bit != '0': - return False - - return True - - -def is_vlan_in_bitmap(vid, bitmap): - """check is VLAN id in bitmap""" - - if is_vlan_bitmap_empty(bitmap): - return False - - i = int(vid) // 4 - if i > len(bitmap): - return False - - if int(bitmap[i]) & (0x8 >> (int(vid) % 4)): - return True - - return False - - -def get_interface_type(interface): - """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" - - if interface is None: - return None - - iftype = None - - if interface.upper().startswith('GE'): - iftype = 'ge' - elif interface.upper().startswith('10GE'): - iftype = '10ge' - elif interface.upper().startswith('25GE'): - iftype = '25ge' - elif interface.upper().startswith('4X10GE'): - iftype = '4x10ge' - elif interface.upper().startswith('40GE'): - iftype = '40ge' - elif interface.upper().startswith('100GE'): - iftype = '100ge' - elif interface.upper().startswith('VLANIF'): - iftype = 'vlanif' - elif interface.upper().startswith('LOOPBACK'): - iftype = 'loopback' - elif interface.upper().startswith('METH'): - iftype = 'meth' - elif interface.upper().startswith('ETH-TRUNK'): - iftype = 'eth-trunk' - elif interface.upper().startswith('VBDIF'): - iftype = 'vbdif' - elif interface.upper().startswith('NVE'): - iftype = 'nve' - elif interface.upper().startswith('TUNNEL'): - iftype = 'tunnel' - elif interface.upper().startswith('ETHERNET'): - iftype = 'ethernet' - elif interface.upper().startswith('FCOE-PORT'): - iftype = 'fcoe-port' - elif interface.upper().startswith('FABRIC-PORT'): - iftype = 'fabric-port' - elif interface.upper().startswith('STACK-PORT'): - iftype = 'stack-Port' - elif interface.upper().startswith('NULL'): - iftype = 'null' - else: - return None - - return iftype.lower() - - -class VxlanVap(object): - """ - Manages VXLAN virtual access point. - """ - - def __init__(self, argument_spec): - self.spec = argument_spec - self.module = None - self.__init_module__() - - # module input info - self.bridge_domain_id = self.module.params['bridge_domain_id'] - self.bind_vlan_id = self.module.params['bind_vlan_id'] - self.l2_sub_interface = self.module.params['l2_sub_interface'] - self.ce_vid = self.module.params['ce_vid'] - self.pe_vid = self.module.params['pe_vid'] - self.encapsulation = self.module.params['encapsulation'] - self.state = self.module.params['state'] - - # state - self.vap_info = dict() - self.l2sub_info = dict() - self.changed = False - self.updates_cmd = list() - self.commands = list() - self.results = dict() - self.proposed = dict() - self.existing = dict() - self.end_state = dict() - - def __init_module__(self): - """init module""" - - required_together = [()] - self.module = AnsibleModule( - argument_spec=self.spec, supports_check_mode=True) - - def check_response(self, xml_str, xml_name): - """Check if response message is already succeed.""" - - if "" not in xml_str: - self.module.fail_json(msg='Error: %s failed.' % xml_name) - - def get_bd_vap_dict(self): - """get virtual access point info""" - - vap_info = dict() - conf_str = CE_NC_GET_BD_VAP % self.bridge_domain_id - xml_str = get_nc_config(self.module, conf_str) - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get vap: VLAN - vap_info["bdId"] = self.bridge_domain_id - root = ElementTree.fromstring(xml_str) - vap_info["vlanList"] = "" - vap_vlan = root.find("evc/bds/bd/bdBindVlan") - if vap_vlan: - for ele in vap_vlan: - if ele.tag == "vlanList": - vap_info["vlanList"] = ele.text - - # get vap: l2 su-interface - vap_ifs = root.findall( - "evc/bds/bd/servicePoints/servicePoint/ifName") - if_list = list() - if vap_ifs: - for vap_if in vap_ifs: - if vap_if.tag == "ifName": - if_list.append(vap_if.text) - vap_info["intfList"] = if_list - - return vap_info - - def get_l2_sub_intf_dict(self, ifname): - """get l2 sub-interface info""" - - intf_info = dict() - if not ifname: - return intf_info - - conf_str = CE_NC_GET_ENCAP % ifname - xml_str = get_nc_config(self.module, conf_str) - - if "" in xml_str: - return intf_info - - xml_str = xml_str.replace('\r', '').replace('\n', '').\ - replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\ - replace('xmlns="http://www.huawei.com/netconf/vrp"', "") - - # get l2 sub interface encapsulation info - root = ElementTree.fromstring(xml_str) - bds = root.find("ethernet/servicePoints/servicePoint") - if not bds: - return intf_info - - for ele in bds: - if ele.tag in ["ifName", "flowType"]: - intf_info[ele.tag] = ele.text.lower() - - if intf_info.get("flowType") == "dot1q": - ce_vid = root.find( - "ethernet/servicePoints/servicePoint/flowDot1qs") - intf_info["dot1qVids"] = "" - if ce_vid: - for ele in ce_vid: - if ele.tag == "dot1qVids": - intf_info["dot1qVids"] = ele.text - elif intf_info.get("flowType") == "qinq": - vids = root.find( - "ethernet/servicePoints/servicePoint/flowQinqs/flowQinq") - if vids: - for ele in vids: - if ele.tag in ["peVlanId", "ceVids"]: - intf_info[ele.tag] = ele.text - - return intf_info - - def config_traffic_encap_dot1q(self): - """configure traffic encapsulation type dot1q""" - - xml_str = "" - self.updates_cmd.append("interface %s" % self.l2_sub_interface) - if self.state == "present": - if self.encapsulation != self.l2sub_info.get("flowType"): - if self.ce_vid: - vlan_bitmap = vlan_vid_to_bitmap(self.ce_vid) - xml_str = CE_NC_SET_ENCAP_DOT1Q % ( - self.l2_sub_interface, vlan_bitmap, vlan_bitmap) - self.updates_cmd.append("encapsulation %s vid %s" % ( - self.encapsulation, self.ce_vid)) - else: - xml_str = CE_NC_SET_ENCAP % ( - self.l2_sub_interface, self.encapsulation) - self.updates_cmd.append( - "encapsulation %s" % self.encapsulation) - else: - if self.ce_vid and not is_vlan_in_bitmap( - self.ce_vid, self.l2sub_info.get("dot1qVids")): - vlan_bitmap = vlan_vid_to_bitmap(self.ce_vid) - xml_str = CE_NC_SET_ENCAP_DOT1Q % ( - self.l2_sub_interface, vlan_bitmap, vlan_bitmap) - self.updates_cmd.append("encapsulation %s vid %s" % ( - self.encapsulation, self.ce_vid)) - else: - if self.encapsulation == self.l2sub_info.get("flowType"): - if self.ce_vid: - if is_vlan_in_bitmap(self.ce_vid, self.l2sub_info.get("dot1qVids")): - xml_str = CE_NC_UNSET_ENCAP % self.l2_sub_interface - self.updates_cmd.append("undo encapsulation %s vid %s" % ( - self.encapsulation, self.ce_vid)) - else: - xml_str = CE_NC_UNSET_ENCAP % self.l2_sub_interface - self.updates_cmd.append( - "undo encapsulation %s" % self.encapsulation) - - if not xml_str: - self.updates_cmd.pop() - return - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "CONFIG_INTF_ENCAP_DOT1Q") - self.changed = True - - def config_traffic_encap_qinq(self): - """configure traffic encapsulation type qinq""" - - xml_str = "" - self.updates_cmd.append("interface %s" % self.l2_sub_interface) - if self.state == "present": - if self.encapsulation != self.l2sub_info.get("flowType"): - if self.ce_vid: - vlan_bitmap = vlan_vid_to_bitmap(self.ce_vid) - xml_str = CE_NC_SET_ENCAP_QINQ % (self.l2_sub_interface, - self.pe_vid, - vlan_bitmap, - vlan_bitmap) - self.updates_cmd.append( - "encapsulation %s vid %s ce-vid %s" % (self.encapsulation, - self.pe_vid, - self.ce_vid)) - else: - xml_str = CE_NC_SET_ENCAP % ( - self.l2_sub_interface, self.encapsulation) - self.updates_cmd.append( - "encapsulation %s" % self.encapsulation) - else: - if self.ce_vid: - if not is_vlan_in_bitmap(self.ce_vid, self.l2sub_info.get("ceVids")) \ - or self.pe_vid != self.l2sub_info.get("peVlanId"): - vlan_bitmap = vlan_vid_to_bitmap(self.ce_vid) - xml_str = CE_NC_SET_ENCAP_QINQ % (self.l2_sub_interface, - self.pe_vid, - vlan_bitmap, - vlan_bitmap) - self.updates_cmd.append( - "encapsulation %s vid %s ce-vid %s" % (self.encapsulation, - self.pe_vid, - self.ce_vid)) - else: - if self.encapsulation == self.l2sub_info.get("flowType"): - if self.ce_vid: - if is_vlan_in_bitmap(self.ce_vid, self.l2sub_info.get("ceVids")) \ - and self.pe_vid == self.l2sub_info.get("peVlanId"): - xml_str = CE_NC_UNSET_ENCAP % self.l2_sub_interface - self.updates_cmd.append( - "undo encapsulation %s vid %s ce-vid %s" % (self.encapsulation, - self.pe_vid, - self.ce_vid)) - else: - xml_str = CE_NC_UNSET_ENCAP % self.l2_sub_interface - self.updates_cmd.append( - "undo encapsulation %s" % self.encapsulation) - - if not xml_str: - self.updates_cmd.pop() - return - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "CONFIG_INTF_ENCAP_QINQ") - self.changed = True - - def config_traffic_encap(self): - """configure traffic encapsulation types""" - - if not self.l2sub_info: - self.module.fail_json(msg="Error: Interface %s does not exist." % self.l2_sub_interface) - - if not self.encapsulation: - return - - xml_str = "" - if self.encapsulation in ["default", "untag"]: - if self.state == "present": - if self.encapsulation != self.l2sub_info.get("flowType"): - xml_str = CE_NC_SET_ENCAP % ( - self.l2_sub_interface, self.encapsulation) - self.updates_cmd.append( - "interface %s" % self.l2_sub_interface) - self.updates_cmd.append( - "encapsulation %s" % self.encapsulation) - else: - if self.encapsulation == self.l2sub_info.get("flowType"): - xml_str = CE_NC_UNSET_ENCAP % self.l2_sub_interface - self.updates_cmd.append( - "interface %s" % self.l2_sub_interface) - self.updates_cmd.append( - "undo encapsulation %s" % self.encapsulation) - elif self.encapsulation == "none": - if self.state == "present": - if self.encapsulation != self.l2sub_info.get("flowType"): - xml_str = CE_NC_UNSET_ENCAP % self.l2_sub_interface - self.updates_cmd.append( - "interface %s" % self.l2_sub_interface) - self.updates_cmd.append( - "undo encapsulation %s" % self.l2sub_info.get("flowType")) - elif self.encapsulation == "dot1q": - self.config_traffic_encap_dot1q() - return - elif self.encapsulation == "qinq": - self.config_traffic_encap_qinq() - return - else: - pass - - if not xml_str: - return - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "CONFIG_INTF_ENCAP") - self.changed = True - - def config_vap_sub_intf(self): - """configure a Layer 2 sub-interface as a service access point""" - - if not self.vap_info: - self.module.fail_json(msg="Error: Bridge domain %s does not exist." % self.bridge_domain_id) - - xml_str = "" - if self.state == "present": - if self.l2_sub_interface not in self.vap_info["intfList"]: - self.updates_cmd.append("interface %s" % self.l2_sub_interface) - self.updates_cmd.append("bridge-domain %s" % - self.bridge_domain_id) - xml_str = CE_NC_MERGE_BD_INTF % ( - self.bridge_domain_id, self.l2_sub_interface) - else: - if self.l2_sub_interface in self.vap_info["intfList"]: - self.updates_cmd.append("interface %s" % self.l2_sub_interface) - self.updates_cmd.append( - "undo bridge-domain %s" % self.bridge_domain_id) - xml_str = CE_NC_DELETE_BD_INTF % ( - self.bridge_domain_id, self.l2_sub_interface) - - if not xml_str: - return - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "CONFIG_VAP_SUB_INTERFACE") - self.changed = True - - def config_vap_vlan(self): - """configure a VLAN as a service access point""" - - xml_str = "" - if self.state == "present": - if not is_vlan_in_bitmap(self.bind_vlan_id, self.vap_info["vlanList"]): - self.updates_cmd.append("bridge-domain %s" % - self.bridge_domain_id) - self.updates_cmd.append( - "l2 binding vlan %s" % self.bind_vlan_id) - vlan_bitmap = vlan_vid_to_bitmap(self.bind_vlan_id) - xml_str = CE_NC_MERGE_BD_VLAN % ( - self.bridge_domain_id, vlan_bitmap, vlan_bitmap) - else: - if is_vlan_in_bitmap(self.bind_vlan_id, self.vap_info["vlanList"]): - self.updates_cmd.append("bridge-domain %s" % - self.bridge_domain_id) - self.updates_cmd.append( - "undo l2 binding vlan %s" % self.bind_vlan_id) - vlan_bitmap = vlan_vid_to_bitmap(self.bind_vlan_id) - xml_str = CE_NC_MERGE_BD_VLAN % ( - self.bridge_domain_id, "0" * 1024, vlan_bitmap) - - if not xml_str: - return - recv_xml = set_nc_config(self.module, xml_str) - self.check_response(recv_xml, "CONFIG_VAP_VLAN") - self.changed = True - - def is_vlan_valid(self, vid, name): - """check VLAN id""" - - if not vid: - return - - if not vid.isdigit(): - self.module.fail_json(msg="Error: %s is not digit." % name) - return - - if int(vid) < 1 or int(vid) > 4094: - self.module.fail_json( - msg="Error: %s is not in the range from 1 to 4094." % name) - - def is_l2_sub_intf_valid(self, ifname): - """check l2 sub interface valid""" - - if ifname.count('.') != 1: - return False - - if_num = ifname.split('.')[1] - if not if_num.isdigit(): - return False - - if int(if_num) < 1 or int(if_num) > 4096: - self.module.fail_json( - msg="Error: Sub-interface number is not in the range from 1 to 4096.") - return False - - if not get_interface_type(ifname): - return False - - return True - - def check_params(self): - """Check all input params""" - - # bridge domain id check - if self.bridge_domain_id: - if not self.bridge_domain_id.isdigit(): - self.module.fail_json( - msg="Error: Bridge domain id is not digit.") - if int(self.bridge_domain_id) < 1 or int(self.bridge_domain_id) > 16777215: - self.module.fail_json( - msg="Error: Bridge domain id is not in the range from 1 to 16777215.") - - # check bind_vlan_id - if self.bind_vlan_id: - self.is_vlan_valid(self.bind_vlan_id, "bind_vlan_id") - - # check l2_sub_interface - if self.l2_sub_interface and not self.is_l2_sub_intf_valid(self.l2_sub_interface): - self.module.fail_json(msg="Error: l2_sub_interface is invalid.") - - # check ce_vid - if self.ce_vid: - self.is_vlan_valid(self.ce_vid, "ce_vid") - if not self.encapsulation or self.encapsulation not in ["dot1q", "qinq"]: - self.module.fail_json(msg="Error: ce_vid can not be set " - "when encapsulation is '%s'." % self.encapsulation) - if self.encapsulation == "qinq" and not self.pe_vid: - self.module.fail_json(msg="Error: ce_vid and pe_vid must be set at the same time " - "when encapsulation is '%s'." % self.encapsulation) - # check pe_vid - if self.pe_vid: - self.is_vlan_valid(self.pe_vid, "pe_vid") - if not self.encapsulation or self.encapsulation != "qinq": - self.module.fail_json(msg="Error: pe_vid can not be set " - "when encapsulation is '%s'." % self.encapsulation) - if not self.ce_vid: - self.module.fail_json(msg="Error: ce_vid and pe_vid must be set at the same time " - "when encapsulation is '%s'." % self.encapsulation) - - def get_proposed(self): - """get proposed info""" - - if self.bridge_domain_id: - self.proposed["bridge_domain_id"] = self.bridge_domain_id - if self.bind_vlan_id: - self.proposed["bind_vlan_id"] = self.bind_vlan_id - if self.l2_sub_interface: - self.proposed["l2_sub_interface"] = self.l2_sub_interface - if self.encapsulation: - self.proposed["encapsulation"] = self.encapsulation - if self.ce_vid: - self.proposed["ce_vid"] = self.ce_vid - if self.pe_vid: - self.proposed["pe_vid"] = self.pe_vid - self.proposed["state"] = self.state - - def get_existing(self): - """get existing info""" - - if self.bridge_domain_id: - if self.bind_vlan_id or self.l2_sub_interface: - self.existing["bridge_domain_id"] = self.bridge_domain_id - self.existing["bind_vlan_list"] = bitmap_to_vlan_list( - self.vap_info.get("vlanList")) - self.existing["bind_intf_list"] = self.vap_info.get("intfList") - - if self.encapsulation and self.l2_sub_interface: - self.existing["l2_sub_interface"] = self.l2_sub_interface - self.existing["encapsulation"] = self.l2sub_info.get("flowType") - if self.existing["encapsulation"] == "dot1q": - self.existing["ce_vid"] = bitmap_to_vlan_list( - self.l2sub_info.get("dot1qVids")) - if self.existing["encapsulation"] == "qinq": - self.existing["ce_vid"] = bitmap_to_vlan_list( - self.l2sub_info.get("ceVids")) - self.existing["pe_vid"] = self.l2sub_info.get("peVlanId") - - def get_end_state(self): - """get end state info""" - - if self.bridge_domain_id: - if self.bind_vlan_id or self.l2_sub_interface: - vap_info = self.get_bd_vap_dict() - self.end_state["bridge_domain_id"] = self.bridge_domain_id - self.end_state["bind_vlan_list"] = bitmap_to_vlan_list( - vap_info.get("vlanList")) - self.end_state["bind_intf_list"] = vap_info.get("intfList") - - if self.encapsulation and self.l2_sub_interface: - l2sub_info = self.get_l2_sub_intf_dict(self.l2_sub_interface) - self.end_state["l2_sub_interface"] = self.l2_sub_interface - self.end_state["encapsulation"] = l2sub_info.get("flowType") - if self.end_state["encapsulation"] == "dot1q": - self.end_state["ce_vid"] = bitmap_to_vlan_list( - l2sub_info.get("dot1qVids")) - if self.end_state["encapsulation"] == "qinq": - self.end_state["ce_vid"] = bitmap_to_vlan_list( - l2sub_info.get("ceVids")) - self.end_state["pe_vid"] = l2sub_info.get("peVlanId") - - def data_init(self): - """data init""" - if self.l2_sub_interface: - self.l2_sub_interface = self.l2_sub_interface.replace( - " ", "").upper() - if self.encapsulation and self.l2_sub_interface: - self.l2sub_info = self.get_l2_sub_intf_dict(self.l2_sub_interface) - if self.bridge_domain_id: - if self.bind_vlan_id or self.l2_sub_interface: - self.vap_info = self.get_bd_vap_dict() - - def work(self): - """worker""" - - self.check_params() - self.data_init() - self.get_existing() - self.get_proposed() - - # Traffic encapsulation types - if self.encapsulation and self.l2_sub_interface: - self.config_traffic_encap() - - # A VXLAN service access point can be a Layer 2 sub-interface or VLAN - if self.bridge_domain_id: - if self.l2_sub_interface: - # configure a Layer 2 sub-interface as a service access point - self.config_vap_sub_intf() - - if self.bind_vlan_id: - # configure a VLAN as a service access point - self.config_vap_vlan() - self.get_end_state() - self.results['changed'] = self.changed - self.results['proposed'] = self.proposed - self.results['existing'] = self.existing - self.results['end_state'] = self.end_state - if self.changed: - self.results['updates'] = self.updates_cmd - else: - self.results['updates'] = list() - self.module.exit_json(**self.results) - - -def main(): - """Module main""" - - argument_spec = dict( - bridge_domain_id=dict(required=False, type='str'), - bind_vlan_id=dict(required=False, type='str'), - l2_sub_interface=dict(required=False, type='str'), - encapsulation=dict(required=False, type='str', - choices=['dot1q', 'default', 'untag', 'qinq', 'none']), - ce_vid=dict(required=False, type='str'), - pe_vid=dict(required=False, type='str'), - state=dict(required=False, default='present', - choices=['present', 'absent']) - ) - argument_spec.update(ce_argument_spec) - module = VxlanVap(argument_spec) - module.work() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cloudvision/cv_server_provision.py b/plugins/modules/network/cloudvision/cv_server_provision.py deleted file mode 100644 index 24f4a46f91..0000000000 --- a/plugins/modules/network/cloudvision/cv_server_provision.py +++ /dev/null @@ -1,642 +0,0 @@ -#!/usr/bin/python -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cv_server_provision -author: "EOS+ CS (ansible-dev@arista.com) (@mharista)" -short_description: - Provision server port by applying or removing template configuration to an - Arista CloudVision Portal configlet that is applied to a switch. -description: - - This module allows a server team to provision server network ports for - new servers without having to access Arista CVP or asking the network team - to do it for them. Provide the information for connecting to CVP, switch - rack, port the new server is connected to, optional vlan, and an action - and the module will apply the configuration to the switch port via CVP. - Actions are add (applies template config to port), - remove (defaults the interface config) and - show (returns the current port config). -options: - host: - description: - - The hostname or IP address of the CVP node being connected to. - required: true - port: - description: - - The port number to use when making API calls to the CVP node. This - will default to the default port for the specified protocol. Port 80 - for http and port 443 for https. - protocol: - description: - - The protocol to use when making API calls to CVP. CVP defaults to https - and newer versions of CVP no longer support http. - default: https - choices: [https, http] - username: - description: - - The user that will be used to connect to CVP for making API calls. - required: true - password: - description: - - The password of the user that will be used to connect to CVP for API - calls. - required: true - server_name: - description: - - The hostname or identifier for the server that is having it's switch - port provisioned. - required: true - switch_name: - description: - - The hostname of the switch is being configured for the server being - provisioned. - required: true - switch_port: - description: - - The physical port number on the switch that the new server is - connected to. - required: true - port_vlan: - description: - - The vlan that should be applied to the port for this server. - This parameter is dependent on a proper template that supports single - vlan provisioning with it. If a port vlan is specified by the template - specified does not support this the module will exit out with no - changes. If a template is specified that requires a port vlan but no - port vlan is specified the module will exit out with no changes. - template: - description: - - A path to a Jinja formatted template file that contains the - configuration block that will be applied to the specified switch port. - This template will have variable fields replaced by the module before - being applied to the switch configuration. - required: true - action: - description: - - The action for the module to take. The actions are add, which applies - the specified template config to port, remove, which defaults the - specified interface configuration, and show, which will return the - current port configuration with no changes. - default: show - choices: [show, add, remove] - auto_run: - description: - - Flag that determines whether or not the module will execute the CVP - task spawned as a result of changes to a switch configlet. When an - add or remove action is taken which results in a change to a switch - configlet, CVP will spawn a task that needs to be executed for the - configuration to be applied to the switch. If this option is True then - the module will determined the task number created by the configuration - change, execute it and wait for the task to complete. If the option - is False then the task will remain in the Pending state in CVP for - a network administrator to review and execute. - type: bool - default: 'no' -requirements: [Jinja2, cvprac >= 0.7.0] -''' - -EXAMPLES = ''' -- name: Get current configuration for interface Ethernet2 - cv_server_provision: - host: cvp_node - username: cvp_user - password: cvp_pass - protocol: https - server_name: new_server - switch_name: eos_switch_1 - switch_port: 2 - template: template_file.j2 - action: show - -- name: Remove existing configuration from interface Ethernet2. Run task. - cv_server_provision: - host: cvp_node - username: cvp_user - password: cvp_pass - protocol: https - server_name: new_server - switch_name: eos_switch_1 - switch_port: 2 - template: template_file.j2 - action: remove - auto_run: True - -- name: Add template configuration to interface Ethernet2. No VLAN. Run task. - cv_server_provision: - host: cvp_node - username: cvp_user - password: cvp_pass - protocol: https - server_name: new_server - switch_name: eos_switch_1 - switch_port: 2 - template: single_attached_trunk.j2 - action: add - auto_run: True - -- name: Add template with VLAN configuration to interface Ethernet2. Run task. - cv_server_provision: - host: cvp_node - username: cvp_user - password: cvp_pass - protocol: https - server_name: new_server - switch_name: eos_switch_1 - switch_port: 2 - port_vlan: 22 - template: single_attached_vlan.j2 - action: add - auto_run: True -''' - -RETURN = ''' -changed: - description: Signifies if a change was made to the configlet - returned: success - type: bool - sample: true -currentConfigBlock: - description: The current config block for the user specified interface - returned: when action = show - type: str - sample: | - interface Ethernet4 - ! -newConfigBlock: - description: The new config block for the user specified interface - returned: when action = add or remove - type: str - sample: | - interface Ethernet3 - description example - no switchport - ! -oldConfigBlock: - description: The current config block for the user specified interface - before any changes are made - returned: when action = add or remove - type: str - sample: | - interface Ethernet3 - ! -fullConfig: - description: The full config of the configlet after being updated - returned: when action = add or remove - type: str - sample: | - ! - interface Ethernet3 - ! - interface Ethernet4 - ! -updateConfigletResponse: - description: Response returned from CVP when configlet update is triggered - returned: when action = add or remove and configuration changes - type: str - sample: "Configlet veos1-server successfully updated and task initiated." -portConfigurable: - description: Signifies if the user specified port has an entry in the - configlet that Ansible has access to - returned: success - type: bool - sample: true -switchConfigurable: - description: Signifies if the user specified switch has a configlet - applied to it that CVP is allowed to edit - returned: success - type: bool - sample: true -switchInfo: - description: Information from CVP describing the switch being configured - returned: success - type: dict - sample: {"architecture": "i386", - "bootupTimeStamp": 1491264298.21, - "complianceCode": "0000", - "complianceIndication": "NONE", - "deviceInfo": "Registered", - "deviceStatus": "Registered", - "fqdn": "veos1", - "hardwareRevision": "", - "internalBuildId": "12-12", - "internalVersion": "4.17.1F-11111.4171F", - "ipAddress": "192.168.1.20", - "isDANZEnabled": "no", - "isMLAGEnabled": "no", - "key": "00:50:56:5d:e5:e0", - "lastSyncUp": 1496432895799, - "memFree": 472976, - "memTotal": 1893460, - "modelName": "vEOS", - "parentContainerId": "container_13_5776759195930", - "serialNumber": "", - "systemMacAddress": "00:50:56:5d:e5:e0", - "taskIdList": [], - "tempAction": null, - "type": "netelement", - "unAuthorized": false, - "version": "4.17.1F", - "ztpMode": "false"} -taskCompleted: - description: Signifies if the task created and executed has completed successfully - returned: when action = add or remove, and auto_run = true, - and configuration changes - type: bool - sample: true -taskCreated: - description: Signifies if a task was created due to configlet changes - returned: when action = add or remove, and auto_run = true or false, - and configuration changes - type: bool - sample: true -taskExecuted: - description: Signifies if the automation executed the spawned task - returned: when action = add or remove, and auto_run = true, - and configuration changes - type: bool - sample: true -taskId: - description: The task ID created by CVP because of changes to configlet - returned: when action = add or remove, and auto_run = true or false, - and configuration changes - type: str - sample: "500" -''' - -import re -import time -from ansible.module_utils.basic import AnsibleModule -try: - import jinja2 - from jinja2 import meta - HAS_JINJA2 = True -except ImportError: - HAS_JINJA2 = False -try: - from cvprac.cvp_client import CvpClient - from cvprac.cvp_client_errors import CvpLoginError, CvpApiError - HAS_CVPRAC = True -except ImportError: - HAS_CVPRAC = False - - -def connect(module): - ''' Connects to CVP device using user provided credentials from playbook. - - :param module: Ansible module with parameters and client connection. - :return: CvpClient object with connection instantiated. - ''' - client = CvpClient() - try: - client.connect([module.params['host']], - module.params['username'], - module.params['password'], - protocol=module.params['protocol'], - port=module.params['port']) - except CvpLoginError as e: - module.fail_json(msg=str(e)) - return client - - -def switch_info(module): - ''' Get dictionary of switch info from CVP. - - :param module: Ansible module with parameters and client connection. - :return: Dict of switch info from CVP or exit with failure if no - info for device is found. - ''' - switch_name = module.params['switch_name'] - switch_info = module.client.api.get_device_by_name(switch_name) - if not switch_info: - module.fail_json(msg=str("Device with name '%s' does not exist." - % switch_name)) - return switch_info - - -def switch_in_compliance(module, sw_info): - ''' Check if switch is currently in compliance. - - :param module: Ansible module with parameters and client connection. - :param sw_info: Dict of switch info. - :return: Nothing or exit with failure if device is not in compliance. - ''' - compliance = module.client.api.check_compliance(sw_info['key'], - sw_info['type']) - if compliance['complianceCode'] != '0000': - module.fail_json(msg=str('Switch %s is not in compliance. Returned' - ' compliance code %s.' - % (sw_info['fqdn'], - compliance['complianceCode']))) - - -def server_configurable_configlet(module, sw_info): - ''' Check CVP that the user specified switch has a configlet assigned to - it that Ansible is allowed to edit. - - :param module: Ansible module with parameters and client connection. - :param sw_info: Dict of switch info. - :return: Dict of configlet information or None. - ''' - configurable_configlet = None - configlet_name = module.params['switch_name'] + '-server' - switch_configlets = module.client.api.get_configlets_by_device_id( - sw_info['key']) - for configlet in switch_configlets: - if configlet['name'] == configlet_name: - configurable_configlet = configlet - return configurable_configlet - - -def port_configurable(module, configlet): - ''' Check configlet if the user specified port has a configuration entry - in the configlet to determine if Ansible is allowed to configure the - port on this switch. - - :param module: Ansible module with parameters and client connection. - :param configlet: Dict of configlet info. - :return: True or False. - ''' - configurable = False - regex = r'^interface Ethernet%s' % module.params['switch_port'] - for config_line in configlet['config'].split('\n'): - if re.match(regex, config_line): - configurable = True - return configurable - - -def configlet_action(module, configlet): - ''' Take appropriate action based on current state of device and user - requested action. - - Return current config block for specified port if action is show. - - If action is add or remove make the appropriate changes to the - configlet and return the associated information. - - :param module: Ansible module with parameters and client connection. - :param configlet: Dict of configlet info. - :return: Dict of information to updated results with. - ''' - result = dict() - existing_config = current_config(module, configlet['config']) - if module.params['action'] == 'show': - result['currentConfigBlock'] = existing_config - return result - elif module.params['action'] == 'add': - result['newConfigBlock'] = config_from_template(module) - elif module.params['action'] == 'remove': - result['newConfigBlock'] = ('interface Ethernet%s\n!' - % module.params['switch_port']) - result['oldConfigBlock'] = existing_config - result['fullConfig'] = updated_configlet_content(module, - configlet['config'], - result['newConfigBlock']) - resp = module.client.api.update_configlet(result['fullConfig'], - configlet['key'], - configlet['name']) - if 'data' in resp: - result['updateConfigletResponse'] = resp['data'] - if 'task' in resp['data']: - result['changed'] = True - result['taskCreated'] = True - return result - - -def current_config(module, config): - ''' Parse the full port configuration for the user specified port out of - the full configlet configuration and return as a string. - - :param module: Ansible module with parameters and client connection. - :param config: Full config to parse specific port config from. - :return: String of current config block for user specified port. - ''' - regex = r'^interface Ethernet%s' % module.params['switch_port'] - match = re.search(regex, config, re.M) - if not match: - module.fail_json(msg=str('interface section not found - %s' - % config)) - block_start, line_end = match.regs[0] - - match = re.search(r'!', config[line_end:], re.M) - if not match: - return config[block_start:] - _, block_end = match.regs[0] - - block_end = line_end + block_end - return config[block_start:block_end] - - -def valid_template(port, template): - ''' Test if the user provided Jinja template is valid. - - :param port: User specified port. - :param template: Contents of Jinja template. - :return: True or False - ''' - valid = True - regex = r'^interface Ethernet%s' % port - match = re.match(regex, template, re.M) - if not match: - valid = False - return valid - - -def config_from_template(module): - ''' Load the Jinja template and apply user provided parameters in necessary - places. Fail if template is not found. Fail if rendered template does - not reference the correct port. Fail if the template requires a VLAN - but the user did not provide one with the port_vlan parameter. - - :param module: Ansible module with parameters and client connection. - :return: String of Jinja template rendered with parameters or exit with - failure. - ''' - template_loader = jinja2.FileSystemLoader('./templates') - env = jinja2.Environment(loader=template_loader, - undefined=jinja2.DebugUndefined) - template = env.get_template(module.params['template']) - if not template: - module.fail_json(msg=str('Could not find template - %s' - % module.params['template'])) - - data = {'switch_port': module.params['switch_port'], - 'server_name': module.params['server_name']} - - temp_source = env.loader.get_source(env, module.params['template'])[0] - parsed_content = env.parse(temp_source) - temp_vars = list(meta.find_undeclared_variables(parsed_content)) - if 'port_vlan' in temp_vars: - if module.params['port_vlan']: - data['port_vlan'] = module.params['port_vlan'] - else: - module.fail_json(msg=str('Template %s requires a vlan. Please' - ' re-run with vlan number provided.' - % module.params['template'])) - - template = template.render(data) - if not valid_template(module.params['switch_port'], template): - module.fail_json(msg=str('Template content does not configure proper' - ' interface - %s' % template)) - return template - - -def updated_configlet_content(module, existing_config, new_config): - ''' Update the configlet configuration with the new section for the port - specified by the user. - - :param module: Ansible module with parameters and client connection. - :param existing_config: String of current configlet configuration. - :param new_config: String of configuration for user specified port to - replace in the existing config. - :return: String of the full updated configuration. - ''' - regex = r'^interface Ethernet%s' % module.params['switch_port'] - match = re.search(regex, existing_config, re.M) - if not match: - module.fail_json(msg=str('interface section not found - %s' - % existing_config)) - block_start, line_end = match.regs[0] - - updated_config = existing_config[:block_start] + new_config - match = re.search(r'!\n', existing_config[line_end:], re.M) - if match: - _, block_end = match.regs[0] - block_end = line_end + block_end - updated_config += '\n%s' % existing_config[block_end:] - return updated_config - - -def configlet_update_task(module): - ''' Poll device info of switch from CVP up to three times to see if the - configlet updates have spawned a task. It sometimes takes a second for - the task to be spawned after configlet updates. If a task is found - return the task ID. Otherwise return None. - - :param module: Ansible module with parameters and client connection. - :return: Task ID or None. - ''' - for num in range(3): - device_info = switch_info(module) - if (('taskIdList' in device_info) and - (len(device_info['taskIdList']) > 0)): - for task in device_info['taskIdList']: - if ('Configlet Assign' in task['description'] and - task['data']['WORKFLOW_ACTION'] == 'Configlet Push'): - return task['workOrderId'] - time.sleep(1) - return None - - -def wait_for_task_completion(module, task): - ''' Poll CVP for the executed task to complete. There is currently no - timeout. Exits with failure if task status is Failed or Cancelled. - - :param module: Ansible module with parameters and client connection. - :param task: Task ID to poll for completion. - :return: True or exit with failure if task is cancelled or fails. - ''' - task_complete = False - while not task_complete: - task_info = module.client.api.get_task_by_id(task) - task_status = task_info['workOrderUserDefinedStatus'] - if task_status == 'Completed': - return True - elif task_status in ['Failed', 'Cancelled']: - module.fail_json(msg=str('Task %s has reported status %s. Please' - ' consult the CVP admins for more' - ' information.' % (task, task_status))) - time.sleep(2) - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - host=dict(required=True), - port=dict(required=False, default=None), - protocol=dict(default='https', choices=['http', 'https']), - username=dict(required=True), - password=dict(required=True, no_log=True), - server_name=dict(required=True), - switch_name=dict(required=True), - switch_port=dict(required=True), - port_vlan=dict(required=False, default=None), - template=dict(require=True), - action=dict(default='show', choices=['show', 'add', 'remove']), - auto_run=dict(type='bool', default=False)) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=False) - if not HAS_JINJA2: - module.fail_json(msg='The Jinja2 python module is required.') - if not HAS_CVPRAC: - module.fail_json(msg='The cvprac python module is required.') - result = dict(changed=False) - module.client = connect(module) - - try: - result['switchInfo'] = switch_info(module) - if module.params['action'] in ['add', 'remove']: - switch_in_compliance(module, result['switchInfo']) - switch_configlet = server_configurable_configlet(module, - result['switchInfo']) - if not switch_configlet: - module.fail_json(msg=str('Switch %s has no configurable server' - ' ports.' % module.params['switch_name'])) - result['switchConfigurable'] = True - if not port_configurable(module, switch_configlet): - module.fail_json(msg=str('Port %s is not configurable as a server' - ' port on switch %s.' - % (module.params['switch_port'], - module.params['switch_name']))) - result['portConfigurable'] = True - result['taskCreated'] = False - result['taskExecuted'] = False - result['taskCompleted'] = False - result.update(configlet_action(module, switch_configlet)) - if module.params['auto_run'] and module.params['action'] != 'show': - task_id = configlet_update_task(module) - if task_id: - result['taskId'] = task_id - note = ('Update config on %s with %s action from Ansible.' - % (module.params['switch_name'], - module.params['action'])) - module.client.api.add_note_to_task(task_id, note) - module.client.api.execute_task(task_id) - result['taskExecuted'] = True - task_completed = wait_for_task_completion(module, task_id) - if task_completed: - result['taskCompleted'] = True - else: - result['taskCreated'] = False - except CvpApiError as e: - module.fail_json(msg=str(e)) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_backup.py b/plugins/modules/network/cnos/cnos_backup.py deleted file mode 100644 index 366906f902..0000000000 --- a/plugins/modules/network/cnos/cnos_backup.py +++ /dev/null @@ -1,276 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to Backup Config to Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_backup -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Backup the current running or startup configuration to a - remote server on devices running Lenovo CNOS -description: - - This module allows you to work with switch configurations. It provides a - way to back up the running or startup configurations of a switch to a - remote server. This is achieved by periodically saving a copy of the - startup or running configuration of the network device to a remote server - using FTP, SFTP, TFTP, or SCP. The first step is to create a directory from - where the remote server can be reached. The next step is to provide the - full file path of the location where the configuration will be backed up. - Authentication details required by the remote server must be provided as - well. This module uses SSH to manage network device configuration. - The results of the operation will be placed in a directory named 'results' - that must be created by the user in their local directory to where the - playbook is run. -extends_documentation_fragment: -- community.general.cnos - -options: - configType: - description: - - This specifies what type of configuration will be backed up. The - choices are the running or startup configurations. There is no - default value, so it will result in an error if the input is - incorrect. - required: Yes - default: Null - choices: [running-config, startup-config] - protocol: - description: - - This refers to the protocol used by the network device to - interact with the remote server to where to upload the backup - configuration. The choices are FTP, SFTP, TFTP, or SCP. Any other - protocols will result in error. If this parameter is - not specified, there is no default value to be used. - required: Yes - default: Null - choices: [SFTP, SCP, FTP, TFTP] - rcserverip: - description: - -This specifies the IP Address of the remote server to where the - configuration will be backed up. - required: Yes - default: Null - rcpath: - description: - - This specifies the full file path where the configuration file - will be copied on the remote server. In case the relative path is - used as the variable value, the root folder for the user of the - server needs to be specified. - required: Yes - default: Null - serverusername: - description: - - Specify the username for the server relating to the protocol - used. - required: Yes - default: Null - serverpassword: - description: - - Specify the password for the server relating to the protocol - used. - required: Yes - default: Null -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module cnos_backup. - These are written in the main.yml file of the tasks directory. ---- -- name: Test Running Config Backup - cnos_backup: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_backup_{{ inventory_hostname }}_output.txt" - configType: running-config - protocol: "sftp" - serverip: "10.241.106.118" - rcpath: "/root/cnos/G8272-running-config.txt" - serverusername: "root" - serverpassword: "root123" - -- name: Test Startup Config Backup - cnos_backup: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_backup_{{ inventory_hostname }}_output.txt" - configType: startup-config - protocol: "sftp" - serverip: "10.241.106.118" - rcpath: "/root/cnos/G8272-startup-config.txt" - serverusername: "root" - serverpassword: "root123" - -- name: Test Running Config Backup -TFTP - cnos_backup: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_backup_{{ inventory_hostname }}_output.txt" - configType: running-config - protocol: "tftp" - serverip: "10.241.106.118" - rcpath: "/anil/G8272-running-config.txt" - serverusername: "root" - serverpassword: "root123" - -- name: Test Startup Config Backup - TFTP - cnos_backup: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_backup_{{ inventory_hostname }}_output.txt" - configType: startup-config - protocol: "tftp" - serverip: "10.241.106.118" - rcpath: "/anil/G8272-startup-config.txt" - serverusername: "root" - serverpassword: "root123" - -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Config file transferred to server" -''' - -import sys -import time -import socket -import array -import json -import time -import re -import os -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -# Utility Method to back up the running config or start up config -# This method supports only SCP or SFTP or FTP or TFTP -# Tuning of timeout parameter is pending -def doConfigBackUp(module, prompt, answer): - host = module.params['host'] - server = module.params['serverip'] - username = module.params['serverusername'] - password = module.params['serverpassword'] - protocol = module.params['protocol'].lower() - rcPath = module.params['rcpath'] - configType = module.params['configType'] - confPath = rcPath + host + '_' + configType + '.txt' - - retVal = '' - - # config backup command happens here - command = "copy " + configType + " " + protocol + " " + protocol + "://" - command = command + username + "@" + server + "/" + confPath - command = command + " vrf management\n" - cnos.debugOutput(command + "\n") - # cnos.checkForFirstTimeAccess(module, command, 'yes/no', 'yes') - cmd = [] - if(protocol == "scp"): - scp_cmd1 = [{'command': command, 'prompt': 'timeout:', 'answer': '0'}] - scp_cmd2 = [{'command': '\n', 'prompt': 'Password:', - 'answer': password}] - cmd.extend(scp_cmd1) - cmd.extend(scp_cmd2) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "sftp"): - sftp_cmd = [{'command': command, 'prompt': 'Password:', - 'answer': password}] - cmd.extend(sftp_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "ftp"): - ftp_cmd = [{'command': command, 'prompt': 'Password:', - 'answer': password}] - cmd.extend(ftp_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "tftp"): - command = "copy " + configType + " " + protocol + " " + protocol - command = command + "://" + server + "/" + confPath - command = command + " vrf management\n" - # cnos.debugOutput(command) - tftp_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(tftp_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - else: - return "Error-110" - - return retVal -# EOM - - -def main(): - - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True), - deviceType=dict(required=True), - configType=dict(required=True), - protocol=dict(required=True), - serverip=dict(required=True), - rcpath=dict(required=True), - serverusername=dict(required=False), - serverpassword=dict(required=False, no_log=True),), - supports_check_mode=False) - - outputfile = module.params['outputfile'] - protocol = module.params['protocol'].lower() - output = '' - if(protocol == "tftp" or protocol == "ftp" or - protocol == "sftp" or protocol == "scp"): - transfer_status = doConfigBackUp(module, None, None) - else: - transfer_status = "Invalid Protocol option" - - output = output + "\n Config Back Up status \n" + transfer_status - - # Save it into the file - path = outputfile.rsplit('/', 1) - # cnos.debugOutput(path[0]) - if not os.path.exists(path[0]): - os.makedirs(path[0]) - file = open(outputfile, "a") - file.write(output) - file.close() - - # Logic to check when changes occur or not - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, msg="Config file transferred to server") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_banner.py b/plugins/modules/network/cnos/cnos_banner.py deleted file mode 100644 index c78b37ce6f..0000000000 --- a/plugins/modules/network/cnos/cnos_banner.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2017 Lenovo, Inc. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to send banner commands to Lenovo Switches -# Two types of banners are supported login and motd -# Lenovo Networking -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_banner -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage multiline banners on Lenovo CNOS devices -description: - - This will configure both login and motd banners on remote devices - running Lenovo CNOS. It allows playbooks to add or remote - banner text from the active running configuration. -notes: - - Tested against CNOS 10.8.1 -options: - banner: - description: - - Specifies which banner should be configured on the remote device. - In Ansible 2.8 and earlier only I(login) and I(motd) were supported. - required: true - choices: ['login', 'motd'] - text: - description: - - The banner text that should be - present in the remote device running configuration. This argument - accepts a multiline string, with no empty lines. Requires - I(state=present). - state: - description: - - Specifies whether or not the configuration is - present in the current devices active running configuration. - default: present - choices: ['present', 'absent'] - provider: - description: - - B(Deprecated) - - "Starting with Ansible 2.5 we recommend using - C(connection: network_cli)." - - For more information please see the - L(CNOS Platform Options guide, ../network/user_guide/platform_cnos.html). - - HORIZONTALLINE - - A dict object containing connection details. - 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. - required: true - port: - description: - - Specifies the port to use when building the connection to the - remote device. - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_USERNAME) will be used - instead. - password: - description: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used - instead. - timeout: - description: - - Specifies the timeout in seconds for communicating with the network - device for either connecting or sending commands. If the timeout - is exceeded before the operation is completed, the module will - error. - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. If the value is not - specified in the task, the value of environment variable - C(ANSIBLE_NET_SSH_KEYFILE)will be used instead. - 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: 'no' - 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. -''' - -EXAMPLES = """ -- name: configure the login banner - cnos_banner: - banner: login - text: | - this is my login banner - that contains a multiline - string - state: present - -- name: remove the motd banner - cnos_banner: - banner: motd - state: absent - -- name: Configure banner from file - cnos_banner: - banner: motd - text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}" - state: present - -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - banner login - - this is my login banner - - that contains a multiline - - string -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import exec_command -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import load_config, run_commands -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import check_args -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec -from ansible.module_utils._text import to_text -import re - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - state = module.params['state'] - - if state == 'absent' and 'text' in have.keys() and have['text']: - commands.append('no banner %s' % module.params['banner']) - - elif state == 'present': - if want['text'] and (want['text'] != have.get('text')): - banner_cmd = 'banner %s ' % module.params['banner'] - for bline in want['text'].strip().splitlines(): - final_cmd = banner_cmd + bline.strip() - commands.append(final_cmd) - - return commands - - -def map_config_to_obj(module): - rc, out, err = exec_command(module, - 'show banner %s' % module.params['banner']) - if rc == 0: - output = out - else: - rc, out, err = exec_command(module, - 'show running-config | include banner %s' - % module.params['banner']) - if out: - output = re.search(r'\^C(.*)\^C', out, re.S).group(1).strip() - else: - output = None - obj = {'banner': module.params['banner'], 'state': 'absent'} - if output: - obj['text'] = output - obj['state'] = 'present' - return obj - - -def map_params_to_obj(module): - text = module.params['text'] - if text: - text = to_text(text).strip() - - return { - 'banner': module.params['banner'], - 'text': text, - 'state': module.params['state'] - } - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - banner=dict(required=True, choices=['login', 'motd']), - text=dict(), - state=dict(default='present', choices=['present', 'absent']) - ) - - argument_spec.update(cnos_argument_spec) - - required_if = [('state', 'present', ('text',))] - - module = AnsibleModule(argument_spec=argument_spec, - required_if=required_if, - supports_check_mode=True) - - warnings = list() - check_args(module, warnings) - - result = {'changed': False} - if warnings: - result['warnings'] = warnings - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - response = load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_bgp.py b/plugins/modules/network/cnos/cnos_bgp.py deleted file mode 100644 index a6ce89e3e7..0000000000 --- a/plugins/modules/network/cnos/cnos_bgp.py +++ /dev/null @@ -1,1180 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to send BGP commands to Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_bgp -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage BGP resources and attributes on devices running CNOS -description: - - This module allows you to work with Border Gateway Protocol (BGP) related - configurations. The operators used are overloaded to ensure control over - switch BGP configurations. This module is invoked using method with - asNumber as one of its arguments. The first level of the BGP configuration - allows to set up an AS number, with the following attributes going - into various configuration operations under the context of BGP. - After passing this level, there are eight BGP arguments that will perform - further configurations. They are bgpArg1, bgpArg2, bgpArg3, bgpArg4, - bgpArg5, bgpArg6, bgpArg7, and bgpArg8. For more details on how to use - these arguments, see [Overloaded Variables]. - This module uses SSH to manage network device configuration. - The results of the operation will be placed in a directory named 'results' - that must be created by the user in their local directory to where the - playbook is run. -extends_documentation_fragment: -- community.general.cnos - -options: - asNum: - description: - - AS number - required: Yes - default: Null - bgpArg1: - description: - - This is an overloaded bgp first argument. Usage of this argument - can be found is the User Guide referenced above. - required: Yes - default: Null - choices: [address-family,bestpath,bgp,cluster-id,confederation, - enforce-first-as,fast-external-failover,graceful-restart, - graceful-restart-helper,log-neighbor-changes, - maxas-limit,neighbor,router-id,shutdown,synchronization, - timers,vrf] - bgpArg2: - description: - - This is an overloaded bgp second argument. Usage of this argument - can be found is the User Guide referenced above. - required: No - default: Null - choices: [ipv4 or ipv6, always-compare-med,compare-confed-aspath, - compare-routerid,dont-compare-originator-id,tie-break-on-age, - as-path,med,identifier,peers] - bgpArg3: - description: - - This is an overloaded bgp third argument. Usage of this argument - can be found is the User Guide referenced above. - required: No - default: Null - choices: [aggregate-address,client-to-client,dampening,distance, - maximum-paths,network,nexthop,redistribute,save, - synchronization,ignore or multipath-relax, - confed or missing-as-worst or non-deterministic or - remove-recv-med or remove-send-med] - bgpArg4: - description: - - This is an overloaded bgp fourth argument. Usage of this argument - can be found is the User Guide referenced above. - required: No - default: Null - choices: [Aggregate prefix, Reachability Half-life time,route-map, - Distance for routes ext,ebgp or ibgp,IP prefix , - IP prefix /, synchronization, - Delay value, direct, ospf, static, memory] - bgpArg5: - description: - - This is an overloaded bgp fifth argument. Usage of this argument - can be found is the User Guide referenced above. - required: No - default: Null - choices: [as-set, summary-only, Value to start reusing a route, - Distance for routes internal, Supported multipath numbers, - backdoor, map, route-map ] - bgpArg6: - description: - - This is an overloaded bgp sixth argument. Usage of this argument - can be found is the User Guide referenced above. - required: No - default: Null - choices: [summary-only,as-set, route-map name, - Value to start suppressing a route, Distance local routes, - Network mask, Pointer to route-map entries] - bgpArg7: - description: - - This is an overloaded bgp seventh argument. Use of this argument - can be found is the User Guide referenced above. - required: No - default: Null - choices: [Maximum duration to suppress a stable route(minutes), - backdoor,route-map, Name of the route map ] - bgpArg8: - description: - - This is an overloaded bgp eight argument. Usage of this argument - can be found is the User Guide referenced above. - required: No - default: Null - choices: [Un-reachability Half-life time for the penalty(minutes), - backdoor] -''' -EXAMPLES = ''' -Tasks: The following are examples of using the module cnos_bgp. These are - written in the main.yml file of the tasks directory. ---- -- name: Test BGP - neighbor - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "neighbor" - bgpArg2: "10.241.107.40" - bgpArg3: 13 - bgpArg4: "address-family" - bgpArg5: "ipv4" - bgpArg6: "next-hop-self" - -- name: Test BGP - BFD - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "neighbor" - bgpArg2: "10.241.107.40" - bgpArg3: 13 - bgpArg4: "bfd" - -- name: Test BGP - address-family - dampening - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "address-family" - bgpArg2: "ipv4" - bgpArg3: "dampening" - bgpArg4: 13 - bgpArg5: 233 - bgpArg6: 333 - bgpArg7: 15 - bgpArg8: 33 - -- name: Test BGP - address-family - network - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "address-family" - bgpArg2: "ipv4" - bgpArg3: "network" - bgpArg4: "1.2.3.4/5" - bgpArg5: "backdoor" - -- name: Test BGP - bestpath - always-compare-med - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "bestpath" - bgpArg2: "always-compare-med" - -- name: Test BGP - bestpath-compare-confed-aspat - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "bestpath" - bgpArg2: "compare-confed-aspath" - -- name: Test BGP - bgp - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "bgp" - bgpArg2: 33 - -- name: Test BGP - cluster-id - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "cluster-id" - bgpArg2: "1.2.3.4" - -- name: Test BGP - confederation-identifier - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "confederation" - bgpArg2: "identifier" - bgpArg3: 333 - -- name: Test BGP - enforce-first-as - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "enforce-first-as" - -- name: Test BGP - fast-external-failover - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "fast-external-failover" - -- name: Test BGP - graceful-restart - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "graceful-restart" - bgpArg2: 333 - -- name: Test BGP - graceful-restart-helper - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "graceful-restart-helper" - -- name: Test BGP - maxas-limit - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "maxas-limit" - bgpArg2: 333 - -- name: Test BGP - neighbor - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "neighbor" - bgpArg2: "10.241.107.40" - bgpArg3: 13 - bgpArg4: "address-family" - bgpArg5: "ipv4" - bgpArg6: "next-hop-self" - -- name: Test BGP - router-id - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "router-id" - bgpArg2: "1.2.3.4" - -- name: Test BGP - synchronization - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "synchronization" - -- name: Test BGP - timers - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "timers" - bgpArg2: 333 - bgpArg3: 3333 - -- name: Test BGP - vrf - cnos_bgp: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_bgp_{{ inventory_hostname }}_output.txt" - asNum: 33 - bgpArg1: "vrf" - -''' -RETURN = ''' -msg: - description: Success or failure message. Upon any failure, the method returns - an error display string. - returned: always - type: str -''' - -import sys -import time -import socket -import array -import json -import time -import re -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def bgpNeighborConfig(module, cmd, prompt, answer): - retVal = '' - command = '' - bgpNeighborArg1 = module.params['bgpArg4'] - bgpNeighborArg2 = module.params['bgpArg5'] - bgpNeighborArg3 = module.params['bgpArg6'] - bgpNeighborArg4 = module.params['bgpArg7'] - bgpNeighborArg5 = module.params['bgpArg8'] - deviceType = module.params['deviceType'] - - if(bgpNeighborArg1 == "address-family"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_address_family", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 + " unicast" - # debugOutput(command) - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - retVal = retVal + bgpNeighborAFConfig(module, cmd, '(config-router-neighbor-af)#', answer) - return retVal - else: - retVal = "Error-316" - return retVal - - elif(bgpNeighborArg1 == "advertisement-interval"): - command = command + bgpNeighborArg1 - - elif(bgpNeighborArg1 == "bfd"): - command = command + bgpNeighborArg1 + " " - if(bgpNeighborArg2 is not None and bgpNeighborArg2 == "mutihop"): - command = command + bgpNeighborArg2 - - elif(bgpNeighborArg1 == "connection-retry-time"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_connection_retrytime", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 - else: - retVal = "Error-315" - return retVal - - elif(bgpNeighborArg1 == "description"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_description", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 - else: - retVal = "Error-314" - return retVal - - elif(bgpNeighborArg1 == "disallow-infinite-holdtime"): - command = command + bgpNeighborArg1 - - elif(bgpNeighborArg1 == "dont-capability-negotiate"): - command = command + bgpNeighborArg1 - - elif(bgpNeighborArg1 == "dynamic-capability"): - command = command + bgpNeighborArg1 - - elif(bgpNeighborArg1 == "ebgp-multihop"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_maxhopcount", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 - else: - retVal = "Error-313" - return retVal - - elif(bgpNeighborArg1 == "interface"): - command = command + bgpNeighborArg1 + " " - # TBD - - elif(bgpNeighborArg1 == "local-as"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_local_as", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 + " " - if(bgpNeighborArg3 is not None and - bgpNeighborArg3 == "no-prepend"): - command = command + bgpNeighborArg3 + " " - if(bgpNeighborArg4 is not None and - bgpNeighborArg4 == "replace-as"): - command = command + bgpNeighborArg4 + " " - if(bgpNeighborArg5 is not None and - bgpNeighborArg5 == "dual-as"): - command = command + bgpNeighborArg5 - else: - command = command.strip() - else: - command = command.strip() - else: - command = command.strip() - else: - retVal = "Error-312" - return retVal - - elif(bgpNeighborArg1 == "maximum-peers"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_maxpeers", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 - else: - retVal = "Error-311" - return retVal - - elif(bgpNeighborArg1 == "password"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_password", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 - else: - retVal = "Error-310" - return retVal - - elif(bgpNeighborArg1 == "remove-private-AS"): - command = command + bgpNeighborArg1 - - elif(bgpNeighborArg1 == "timers"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_timers_Keepalive", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_timers_holdtime", bgpNeighborArg3) - if(value == "ok"): - command = command + bgpNeighborArg3 - else: - retVal = "Error-309" - return retVal - else: - retVal = "Error-308" - return retVal - - elif(bgpNeighborArg1 == "transport"): - command = command + bgpNeighborArg1 + " connection-mode passive " - - elif(bgpNeighborArg1 == "ttl-security"): - command = command + bgpNeighborArg1 + " hops " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_ttl_hops", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 - else: - retVal = "Error-307" - return retVal - - elif(bgpNeighborArg1 == "update-source"): - command = command + bgpNeighborArg1 + " " - if(bgpNeighborArg2 is not None): - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_update_options", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 + " " - if(bgpNeighborArg2 == "ethernet"): - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_update_ethernet", - bgpNeighborArg3) - if(value == "ok"): - command = command + bgpNeighborArg3 - else: - retVal = "Error-304" - return retVal - elif(bgpNeighborArg2 == "loopback"): - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_update_loopback", - bgpNeighborArg3) - if(value == "ok"): - command = command + bgpNeighborArg3 - else: - retVal = "Error-305" - return retVal - else: - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_update_vlan", - bgpNeighborArg3) - if(value == "ok"): - command = command + bgpNeighborArg3 - else: - retVal = "Error-306" - return retVal - else: - command = command + bgpNeighborArg2 - else: - retVal = "Error-303" - return retVal - - elif(bgpNeighborArg1 == "weight"): - command = command + bgpNeighborArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_weight", bgpNeighborArg2) - if(value == "ok"): - command = command + bgpNeighborArg2 - else: - retVal = "Error-302" - return retVal - - else: - retVal = "Error-301" - return retVal - - # debugOutput(command) - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - command = "exit \n" - return retVal -# EOM - - -def bgpNeighborAFConfig(module, cmd, prompt, answer): - retVal = '' - command = '' - bgpNeighborAFArg1 = module.params['bgpArg6'] - bgpNeighborAFArg2 = module.params['bgpArg7'] - bgpNeighborAFArg3 = module.params['bgpArg8'] - deviceType = module.params['deviceType'] - if(bgpNeighborAFArg1 == "allowas-in"): - command = command + bgpNeighborAFArg1 + " " - if(bgpNeighborAFArg2 is not None): - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_af_occurances", bgpNeighborAFArg2) - if(value == "ok"): - command = command + bgpNeighborAFArg2 - else: - retVal = "Error-325" - return retVal - - elif(bgpNeighborAFArg1 == "default-originate"): - command = command + bgpNeighborAFArg1 + " " - if(bgpNeighborAFArg2 is not None and bgpNeighborAFArg2 == "route-map"): - command = command + bgpNeighborAFArg2 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_af_routemap", bgpNeighborAFArg2) - if(value == "ok"): - command = command + bgpNeighborAFArg3 - else: - retVal = "Error-324" - return retVal - - elif(bgpNeighborAFArg1 == "filter-list"): - command = command + bgpNeighborAFArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_af_filtername", bgpNeighborAFArg2) - if(value == "ok"): - command = command + bgpNeighborAFArg2 + " " - if(bgpNeighborAFArg3 == "in" or bgpNeighborAFArg3 == "out"): - command = command + bgpNeighborAFArg3 - else: - retVal = "Error-323" - return retVal - else: - retVal = "Error-322" - return retVal - - elif(bgpNeighborAFArg1 == "maximum-prefix"): - command = command + bgpNeighborAFArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_af_maxprefix", bgpNeighborAFArg2) - if(value == "ok"): - command = command + bgpNeighborAFArg2 + " " - if(bgpNeighborAFArg3 is not None): - command = command + bgpNeighborAFArg3 - else: - command = command.strip() - else: - retVal = "Error-326" - return retVal - - elif(bgpNeighborAFArg1 == "next-hop-self"): - command = command + bgpNeighborAFArg1 - - elif(bgpNeighborAFArg1 == "prefix-list"): - command = command + bgpNeighborAFArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_af_prefixname", bgpNeighborAFArg2) - if(value == "ok"): - command = command + bgpNeighborAFArg2 + " " - if(bgpNeighborAFArg3 == "in" or bgpNeighborAFArg3 == "out"): - command = command + bgpNeighborAFArg3 - else: - retVal = "Error-321" - return retVal - else: - retVal = "Error-320" - return retVal - - elif(bgpNeighborAFArg1 == "route-map"): - command = command + bgpNeighborAFArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_af_routemap", bgpNeighborAFArg2) - if(value == "ok"): - command = command + bgpNeighborAFArg2 - else: - retVal = "Error-319" - return retVal - elif(bgpNeighborAFArg1 == "route-reflector-client"): - command = command + bgpNeighborAFArg1 - - elif(bgpNeighborAFArg1 == "send-community"): - command = command + bgpNeighborAFArg1 + " " - if(bgpNeighborAFArg2 is not None and bgpNeighborAFArg2 == "extended"): - command = command + bgpNeighborAFArg2 - - elif(bgpNeighborAFArg1 == "soft-reconfiguration"): - command = command + bgpNeighborAFArg1 + " inbound" - - elif(bgpNeighborAFArg1 == "unsuppress-map"): - command = command + bgpNeighborAFArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_neighbor_af_routemap", bgpNeighborAFArg2) - if(value == "ok"): - command = command + bgpNeighborAFArg2 - else: - retVal = "Error-318" - return retVal - - else: - retVal = "Error-317" - return retVal - - # debugOutput(command) - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - return retVal -# EOM - - -def bgpAFConfig(module, cmd, prompt, answer): - retVal = '' - command = '' - bgpAFArg1 = module.params['bgpArg3'] - bgpAFArg2 = module.params['bgpArg4'] - bgpAFArg3 = module.params['bgpArg5'] - bgpAFArg4 = module.params['bgpArg6'] - bgpAFArg5 = module.params['bgpArg7'] - bgpAFArg6 = module.params['bgpArg8'] - deviceType = module.params['deviceType'] - if(bgpAFArg1 == "aggregate-address"): - command = command + bgpAFArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_aggregate_prefix", bgpAFArg2) - if(value == "ok"): - if(bgpAFArg2 is None): - command = command.strip() - elif(bgpAFArg2 == "as-set" or bgpAFArg2 == "summary-only"): - command = command + bgpAFArg2 + " " - if((bgpAFArg3 is not None) and (bgpAFArg2 == "as-set")): - command = command + "summary-only" - else: - retVal = "Error-297" - return retVal - else: - retVal = "Error-296" - return retVal - - elif(bgpAFArg1 == "client-to-client"): - command = command + bgpAFArg1 + " reflection " - - elif(bgpAFArg1 == "dampening"): - command = command + bgpAFArg1 + " " - if(bgpAFArg2 == "route-map"): - command = command + bgpAFArg2 + " " - value = cnos.checkSanityofVariable( - deviceType, "addrfamily_routemap_name", bgpAFArg3) - if(value == "ok"): - command = command + bgpAFArg3 - else: - retVal = "Error-196" - return retVal - elif(bgpAFArg2 is not None): - value = cnos.checkSanityofVariable( - deviceType, "reachability_half_life", bgpAFArg2) - if(value == "ok"): - command = command + bgpAFArg2 + " " - if(bgpAFArg3 is not None): - value1 = cnos.checkSanityofVariable( - deviceType, "start_reuse_route_value", bgpAFArg3) - value2 = cnos.checkSanityofVariable( - deviceType, "start_suppress_route_value", bgpAFArg4) - value3 = cnos.checkSanityofVariable( - deviceType, "max_duration_to_suppress_route", - bgpAFArg5) - if(value1 == "ok" and value2 == "ok" and value3 == "ok"): - command = command + bgpAFArg3 + " " + bgpAFArg4 + \ - " " + bgpAFArg5 + " " - if(bgpAFArg6 is not None): - value = cnos.checkSanityofVariable( - deviceType, - "unreachability_halftime_for_penalty", - bgpAFArg6) - if(value == "ok"): - command = command + bgpAFArg6 - else: - retVal = "Error-295" - return retVal - else: - command = command.strip() - else: - retVal = "Error-294" - return retVal - - elif(bgpAFArg1 == "distance"): - command = command + bgpAFArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "distance_external_AS", bgpAFArg2) - if(value == "ok"): - command = command + bgpAFArg2 + " " - value = cnos.checkSanityofVariable( - deviceType, "distance_internal_AS", bgpAFArg3) - if(value == "ok"): - command = command + bgpAFArg3 + " " - value = cnos.checkSanityofVariable( - deviceType, "distance_local_routes", bgpAFArg4) - if(value == "ok"): - command = command + bgpAFArg4 - else: - retVal = "Error-291" - return retVal - else: - retVal = "Error-292" - return retVal - else: - retVal = "Error-293" - return retVal - - elif(bgpAFArg1 == "maximum-paths"): - command = command + bgpAFArg1 + " " - value = cnos.checkSanityofVariable(deviceType, "maxpath_option", bgpAFArg2) - if(value == "ok"): - command = command + bgpAFArg2 + " " - value = cnos.checkSanityofVariable( - deviceType, "maxpath_numbers", bgpAFArg3) - if(value == "ok"): - command = command + bgpAFArg3 - else: - retVal = "Error-199" - return retVal - else: - retVal = "Error-290" - return retVal - - elif(bgpAFArg1 == "network"): - command = command + bgpAFArg1 + " " - if(bgpAFArg2 == "synchronization"): - command = command + bgpAFArg2 - else: - value = cnos.checkSanityofVariable( - deviceType, "network_ip_prefix_with_mask", bgpAFArg2) - if(value == "ok"): - command = command + bgpAFArg2 + " " - if(bgpAFArg3 is not None and bgpAFArg3 == "backdoor"): - command = command + bgpAFArg3 - elif(bgpAFArg3 is not None and bgpAFArg3 == "route-map"): - command = command + bgpAFArg3 - value = cnos.checkSanityofVariable( - deviceType, "addrfamily_routemap_name", bgpAFArg4) - if(value == "ok"): - command = command + bgpAFArg4 + " " - if(bgpAFArg5 is not None and bgpAFArg5 == "backdoor"): - command = command + bgpAFArg5 - else: - retVal = "Error-298" - return retVal - else: - retVal = "Error-196" - return retVal - else: - command = command.strip() - else: - value = cnos.checkSanityofVariable( - deviceType, "network_ip_prefix_value", bgpAFArg2) - if(value == "ok"): - command = command + bgpAFArg2 + " " - if(bgpAFArg3 is not None and bgpAFArg3 == "backdoor"): - command = command + bgpAFArg3 - elif(bgpAFArg3 is not None and bgpAFArg3 == "route-map"): - command = command + bgpAFArg3 - value = cnos.checkSanityofVariable( - deviceType, "addrfamily_routemap_name", bgpAFArg4) - if(value == "ok"): - command = command + bgpAFArg4 + " " - if(bgpAFArg5 is not None and - bgpAFArg5 == "backdoor"): - command = command + bgpAFArg5 - else: - retVal = "Error-298" - return retVal - else: - retVal = "Error-196" - return retVal - elif(bgpAFArg3 is not None and bgpAFArg3 == "mask"): - command = command + bgpAFArg3 - value = cnos.checkSanityofVariable( - deviceType, "network_ip_prefix_mask", bgpAFArg4) - if(value == "ok"): - command = command + bgpAFArg4 + " " - else: - retVal = "Error-299" - return retVal - else: - command = command.strip() - else: - retVal = "Error-300" - return retVal - - elif(bgpAFArg1 == "nexthop"): - command = command + bgpAFArg1 + " trigger-delay critical " - value = cnos.checkSanityofVariable( - deviceType, "nexthop_crtitical_delay", bgpAFArg2) - if(value == "ok"): - command = command + bgpAFArg2 + " " - value = cnos.checkSanityofVariable( - deviceType, "nexthop_noncrtitical_delay", bgpAFArg3) - if(value == "ok"): - command = command + bgpAFArg3 + " " - else: - retVal = "Error-198" - return retVal - else: - retVal = "Error-197" - return retVal - - elif(bgpAFArg1 == "redistribute"): - command = command + bgpAFArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "addrfamily_redistribute_option", bgpAFArg2) - if(value == "ok"): - if(bgpAFArg2 is not None): - command = command + bgpAFArg2 + " " + "route-map " - value = cnos.checkSanityofVariable( - deviceType, "addrfamily_routemap_name", bgpAFArg3) - if(value == "ok"): - command = command + bgpAFArg3 - else: - retVal = "Error-196" - return retVal - else: - retVal = "Error-195" - return retVal - - elif(bgpAFArg1 == "save" or bgpAFArg1 == "synchronization"): - command = command + bgpAFArg1 - - else: - retVal = "Error-194" - return retVal - # debugOutput(command) - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - command = "exit \n" - return retVal -# EOM - - -def bgpConfig(module, cmd, prompt, answer): - retVal = '' - command = '' - bgpArg1 = module.params['bgpArg1'] - bgpArg2 = module.params['bgpArg2'] - bgpArg3 = module.params['bgpArg3'] - bgpArg4 = module.params['bgpArg4'] - bgpArg5 = module.params['bgpArg5'] - bgpArg6 = module.params['bgpArg6'] - bgpArg7 = module.params['bgpArg7'] - bgpArg8 = module.params['bgpArg8'] - asNum = module.params['asNum'] - deviceType = module.params['deviceType'] - # cnos.debugOutput(bgpArg1) - if(bgpArg1 == "address-family"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "bgp_address_family", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 + " " + "unicast \n" - # debugOutput(command) - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - retVal = retVal + bgpAFConfig(module, cmd, prompt, answer) - return retVal - else: - retVal = "Error-178" - return retVal - - elif(bgpArg1 == "bestpath"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " " - if(bgpArg2 == "always-compare-med"): - # debugOutput(bgpArg2) - command = command + bgpArg2 - elif(bgpArg2 == "compare-confed-aspath"): - # debugOutput(bgpArg2) - command = command + bgpArg2 - elif(bgpArg2 == "compare-routerid"): - # debugOutput(bgpArg2) - command = command + bgpArg2 - elif(bgpArg2 == "dont-compare-originator-id"): - # debugOutput(bgpArg2) - command = command + bgpArg2 - elif(bgpArg2 == "tie-break-on-age"): - # debugOutput(bgpArg2) - command = command + bgpArg2 - elif(bgpArg2 == "as-path"): - # debugOutput(bgpArg2) - command = command + bgpArg2 + " " - if(bgpArg3 == "ignore" or bgpArg3 == "multipath-relax"): - command = command + bgpArg3 - else: - retVal = "Error-179" - return retVal - elif(bgpArg2 == "med"): - # debugOutput(bgpArg2) - command = command + bgpArg2 + " " - if(bgpArg3 == "confed" or - bgpArg3 == "missing-as-worst" or - bgpArg3 == "non-deterministic" or - bgpArg3 == "remove-recv-med" or - bgpArg3 == "remove-send-med"): - command = command + bgpArg3 - else: - retVal = "Error-180" - return retVal - else: - retVal = "Error-181" - return retVal - - elif(bgpArg1 == "bgp"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " as-local-count " - value = cnos.checkSanityofVariable( - deviceType, "bgp_bgp_local_count", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 - else: - retVal = "Error-182" - return retVal - - elif(bgpArg1 == "cluster-id"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " " - value = cnos.checkSanityofVariable(deviceType, "cluster_id_as_ip", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 - else: - value = cnos.checkSanityofVariable( - deviceType, "cluster_id_as_number", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 - else: - retVal = "Error-183" - return retVal - - elif(bgpArg1 == "confederation"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " " - if(bgpArg2 == "identifier"): - value = cnos.checkSanityofVariable( - deviceType, "confederation_identifier", bgpArg3) - if(value == "ok"): - command = command + bgpArg2 + " " + bgpArg3 + "\n" - else: - retVal = "Error-184" - return retVal - elif(bgpArg2 == "peers"): - value = cnos.checkSanityofVariable( - deviceType, "confederation_peers_as", bgpArg3) - if(value == "ok"): - command = command + bgpArg2 + " " + bgpArg3 - else: - retVal = "Error-185" - return retVal - else: - retVal = "Error-186" - return retVal - - elif(bgpArg1 == "enforce-first-as"): - # debugOutput(bgpArg1) - command = command + bgpArg1 - - elif(bgpArg1 == "fast-external-failover"): - # debugOutput(bgpArg1) - command = command + bgpArg1 - - elif(bgpArg1 == "graceful-restart"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " stalepath-time " - value = cnos.checkSanityofVariable( - deviceType, "stalepath_delay_value", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 - else: - retVal = "Error-187" - return retVal - - elif(bgpArg1 == "graceful-restart-helper"): - # debugOutput(bgpArg1) - command = command + bgpArg1 - - elif(bgpArg1 == "log-neighbor-changes"): - # debugOutput(bgpArg1) - command = command + bgpArg1 - - elif(bgpArg1 == "maxas-limit"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " " - value = cnos.checkSanityofVariable(deviceType, "maxas_limit_as", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 - else: - retVal = "Error-188" - return retVal - - elif(bgpArg1 == "neighbor"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "neighbor_ipaddress", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 - if(bgpArg3 is not None): - command = command + " remote-as " - value = cnos.checkSanityofVariable( - deviceType, "neighbor_as", bgpArg3) - if(value == "ok"): - # debugOutput(command) - command = command + bgpArg3 - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - retVal = retVal + bgpNeighborConfig(module, cmd, prompt, answer) - return retVal - else: - retVal = "Error-189" - return retVal - - elif(bgpArg1 == "router-id"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " " - value = cnos.checkSanityofVariable(deviceType, "router_id", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 - else: - retVal = "Error-190" - return retVal - - elif(bgpArg1 == "shutdown"): - # debugOutput(bgpArg1) - command = command + bgpArg1 - - elif(bgpArg1 == "synchronization"): - # debugOutput(bgpArg1) - command = command + bgpArg1 - - elif(bgpArg1 == "timers"): - # cnos.debugOutput(bgpArg3) - command = command + bgpArg1 + " bgp " - value = cnos.checkSanityofVariable( - deviceType, "bgp_keepalive_interval", bgpArg2) - if(value == "ok"): - command = command + bgpArg2 - else: - retVal = "Error-191" - return retVal - if(bgpArg3 is not None): - value = cnos.checkSanityofVariable(deviceType, "bgp_holdtime", bgpArg3) - if(value == "ok"): - command = command + " " + bgpArg3 - else: - retVal = "Error-192" - return retVal - else: - retVal = "Error-192" - return retVal - - elif(bgpArg1 == "vrf"): - # debugOutput(bgpArg1) - command = command + bgpArg1 + " default" - else: - # debugOutput(bgpArg1) - retVal = "Error-192" - return retVal - # debugOutput(command) - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - command = "exit \n" - # debugOutput(command) - return retVal -# EOM - - -def main(): - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True), - deviceType=dict(required=True), - bgpArg1=dict(required=True), - bgpArg2=dict(required=False), - bgpArg3=dict(required=False), - bgpArg4=dict(required=False), - bgpArg5=dict(required=False), - bgpArg6=dict(required=False), - bgpArg7=dict(required=False), - bgpArg8=dict(required=False), - asNum=dict(required=True),), - supports_check_mode=False) - - asNum = module.params['asNum'] - outputfile = module.params['outputfile'] - deviceType = module.params['deviceType'] - output = '' - command = 'router bgp ' - value = cnos.checkSanityofVariable(deviceType, "bgp_as_number", asNum) - if(value == "ok"): - # BGP command happens here. It creates if not present - command = command + asNum - cmd = [{'command': command, 'prompt': None, 'answer': None}] - output = output + bgpConfig(module, cmd, '(config)#', None) - else: - output = "Error-176" - # Save it into the file - file = open(outputfile, "a") - file.write(output) - file.close() - - # Logic to check when changes occur or not - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, msg="BGP configurations accomplished") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_command.py b/plugins/modules/network/cnos/cnos_command.py deleted file mode 100644 index b81812b415..0000000000 --- a/plugins/modules/network/cnos/cnos_command.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Module to execute CNOS Commands on Lenovo Switches. -# Lenovo Networking -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_command -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Run arbitrary commands on Lenovo CNOS devices -description: - - Sends arbitrary commands to an CNOS node and returns the results - read from the device. The C(cnos_command) 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. -options: - commands: - description: - - List of commands to send to the remote device. - 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 - retires is expired. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - 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. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ ---- -- name: test contains operator - cnos_command: - commands: - - show version - - show system memory - wait_for: - - "result[0] contains 'Lenovo'" - - "result[1] contains 'MemFree'" - register: result - -- assert: - that: - - "result.changed == false" - - "result.stdout is defined" - -- name: get output for single command - cnos_command: - commands: ['show version'] - register: result - -- assert: - that: - - "result.changed == false" - - "result.stdout is defined" - -- name: get output for multiple commands - cnos_command: - commands: - - show version - - show interface information - register: result - -- assert: - that: - - "result.changed == false" - - "result.stdout is defined" - - "result.stdout | length == 2" -""" - -RETURN = """ -stdout: - description: the set of responses from the commands - returned: always - type: list - sample: ['...', '...'] -stdout_lines: - description: The value of stdout split into a list - returned: always - type: list - sample: [['...', '...'], ['...'], ['...']] -failed_conditions: - description: the conditionals that failed - returned: failed - type: list - sample: ['...', '...'] -""" - -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import run_commands, check_args -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 main(): - spec = dict( - # { command: , prompt: , response: } - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=spec, supports_check_mode=True) - result = {'changed': False} - - wait_for = module.params['wait_for'] or list() - conditionals = [Conditional(c) for c in wait_for] - - commands = module.params['commands'] - 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/plugins/modules/network/cnos/cnos_conditional_command.py b/plugins/modules/network/cnos/cnos_conditional_command.py deleted file mode 100644 index 4caee7a95f..0000000000 --- a/plugins/modules/network/cnos/cnos_conditional_command.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to send Conditional CLI commands to Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_conditional_command -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Execute a single command based on condition on devices - running Lenovo CNOS -description: - - This module allows you to modify the running configuration of a switch. It - provides a way to execute a single CNOS command on a network device by - evaluating the current running configuration and executing the command only - if the specific settings have not been already configured. - The CNOS command is passed as an argument of the method. - This module functions the same as the cnos_command module. - The only exception is that following inventory variable can be specified - ["condition = "] - When this inventory variable is specified as the variable of a task, the - command is executed for the network element that matches the flag string. - Usually, commands are executed across a group of network devices. When - there is a requirement to skip the execution of the command on one or - more devices, it is recommended to use this module. This module uses SSH to - manage network device configuration. -extends_documentation_fragment: -- community.general.cnos - -options: - clicommand: - description: - - This specifies the CLI command as an attribute to this method. - The command is passed using double quotes. The variables can be - placed directly on to the CLI commands or can be invoked - from the vars directory. - required: true - default: Null - condition: - description: - - If you specify condition=false in the inventory file against any - device, the command execution is skipped for that device. - required: true - default: Null - flag: - description: - - If a task needs to be executed, you have to set the flag the same - as it is specified in the inventory for that device. - required: true - default: Null - -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module - cnos_conditional_command. These are written in the main.yml file of the tasks - directory. ---- -- name: Applying CLI template on VLAG Tier1 Leaf Switch1 - cnos_conditional_command: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_conditional_command_ - {{ inventory_hostname }}_output.txt" - condition: "{{ hostvars[inventory_hostname]['condition']}}" - flag: leaf_switch2 - command: "spanning-tree mode enable" - -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Command Applied" -''' - -import sys -import time -import socket -import array -import json -import time -import re -import os -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def main(): - module = AnsibleModule( - argument_spec=dict( - clicommand=dict(required=True), - outputfile=dict(required=True), - condition=dict(required=True), - flag=dict(required=True), - host=dict(required=False), - deviceType=dict(required=True), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, - no_log=True), ), supports_check_mode=False) - - condition = module.params['condition'] - flag = module.params['flag'] - cliCommand = module.params['clicommand'] - outputfile = module.params['outputfile'] - output = '' - if (condition is None or condition != flag): - module.exit_json(changed=True, msg="Command Skipped for this switch") - return '' - # Send the CLi command - cmd = [{'command': cliCommand, 'prompt': None, 'answer': None}] - output = output + str(cnos.run_cnos_commands(module, cmd)) - # Write to memory - save_cmd = [{'command': 'save', 'prompt': None, 'answer': None}] - cmd.extend(save_cmd) - output = output + str(cnos.run_cnos_commands(module, cmd)) - - # Save it into the file - path = outputfile.rsplit('/', 1) - # cnos.debugOutput(path[0]) - if not os.path.exists(path[0]): - os.makedirs(path[0]) - file = open(outputfile, "a") - file.write(output) - file.close() - - # Logic to check when changes occur or not - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, - msg="CLI Command executed and results saved in file ") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_conditional_template.py b/plugins/modules/network/cnos/cnos_conditional_template.py deleted file mode 100644 index 244c4fe8de..0000000000 --- a/plugins/modules/network/cnos/cnos_conditional_template.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to send conditional template to Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_conditional_template -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage switch configuration using templates based on - condition on devices running Lenovo CNOS -description: - - This module allows you to work with the running configuration of a - switch. It provides a way to execute a set of CNOS commands on a switch by - evaluating the current running configuration and executing the commands - only if the specific settings have not been already configured. - The configuration source can be a set of commands or a template written in - the Jinja2 templating language. This module functions the same as the - cnos_template module. The only exception is that the following inventory - variable can be specified. - ["condition = "] - When this inventory variable is specified as the variable of a task, the - template is executed for the network element that matches the flag string. - Usually, templates are used when commands are the same across a group of - network devices. When there is a requirement to skip the execution of the - template on one or more devices, it is recommended to use this module. - This module uses SSH to manage network device configuration. -extends_documentation_fragment: -- community.general.cnos - -options: - commandfile: - description: - - This specifies the path to the CNOS command file which needs to - be applied. This usually comes from the commands folder. Generally - this file is the output of the variables applied on a template - file. So this command is preceded by a template module. The - command file must contain the Ansible keyword - {{ inventory_hostname }} and the condition flag in its filename to - ensure that the command file is unique for each switch and - condition. If this is omitted, the command file will be - overwritten during iteration. For example, - commandfile=./commands/clos_leaf_bgp_ - {{ inventory_hostname }}_LP21_commands.txt - required: true - default: Null - condition: - description: - - If you specify condition= in the inventory file - against any device, the template execution is done for that device - in case it matches the flag setting for that task. - required: true - default: Null - flag: - description: - - If a task needs to be executed, you have to set the flag the same - as it is specified in the inventory for that device. - required: true - default: Null -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module - cnos_conditional_template. These are written in the main.yml file of the - tasks directory. ---- -- name: Applying CLI template on VLAG Tier1 Leaf Switch1 - cnos_conditional_template: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/vlag_1tier_leaf_switch1_ - {{ inventory_hostname }}_output.txt" - condition: "{{ hostvars[inventory_hostname]['condition']}}" - flag: "leaf_switch1" - commandfile: "./commands/vlag_1tier_leaf_switch1_ - {{ inventory_hostname }}_commands.txt" - stp_mode1: "disable" - port_range1: "17,18,29,30" - portchannel_interface_number1: 1001 - portchannel_mode1: active - slot_chassis_number1: 1/48 - switchport_mode1: trunk -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Template Applied." -''' - -import sys -import time -import socket -import array -import json -import time -import re -import os -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def main(): - module = AnsibleModule( - argument_spec=dict( - commandfile=dict(required=True), - outputfile=dict(required=True), - condition=dict(required=True), - flag=dict(required=True), - host=dict(required=False), - deviceType=dict(required=True), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True),), - supports_check_mode=False) - - condition = module.params['condition'] - flag = module.params['flag'] - commandfile = module.params['commandfile'] - outputfile = module.params['outputfile'] - - output = '' - if (condition is None or condition != flag): - module.exit_json(changed=True, msg="Template Skipped for this switch") - return " " - # Send commands one by one - f = open(commandfile, "r") - cmd = [] - for line in f: - # Omit the comment lines in template file - if not line.startswith("#"): - # cnos.debugOutput(line) - command = line.strip() - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - # Write to memory - save_cmd = [{'command': 'save', 'prompt': None, 'answer': None}] - cmd.extend(save_cmd) - output = output + str(cnos.run_cnos_commands(module, cmd)) - # Write output to file - path = outputfile.rsplit('/', 1) - # cnos.debugOutput(path[0]) - if not os.path.exists(path[0]): - os.makedirs(path[0]) - file = open(outputfile, "a") - file.write(output) - file.close() - - # Logic to check when changes occur or not - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, msg="Template Applied") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_config.py b/plugins/modules/network/cnos/cnos_config.py deleted file mode 100644 index abbf95c04d..0000000000 --- a/plugins/modules/network/cnos/cnos_config.py +++ /dev/null @@ -1,307 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Module to configure Lenovo Switches. -# Lenovo Networking -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_config -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage Lenovo CNOS configuration sections -description: - - Lenovo CNOS configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with CNOS configuration sections in - a deterministic way. -notes: - - Tested against CNOS 10.9.1 -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. - aliases: ['commands'] - parents: - description: - - The ordered set of parents that uniquely identify the section - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is - mutually exclusive with I(lines), I(parents). - 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. - 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. - 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. - 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. - default: line - choices: ['line', 'block', 'config'] - 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. - 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' - comment: - description: - - Allows a commit description to be specified to be included - when the configuration is committed. If the configuration is - not changed or committed, this argument is ignored. - default: 'configured by cnos_config' - admin: - description: - - Enters into administration configuration mode for making config - changes to the device. - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -Tasks: The following are examples of using the module cnos_config. ---- -- name: configure top level configuration - cnos_config: - "lines: hostname {{ inventory_hostname }}" - -- name: configure interface settings - cnos_config: - lines: - - enable - - ip ospf enable - parents: interface ip 13 - -- name: load a config from disk and replace the current config - cnos_config: - src: config.cfg - backup: yes - -- name: configurable backup path - cnos_config: - src: config.cfg - 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: Only when lines is specified. - type: list - sample: ['...', '...'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/cnos01.2016-07-16@22:28:34 -""" -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import load_config, get_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import check_args -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - - -DEFAULT_COMMIT_COMMENT = 'configured by cnos_config' - - -def get_running_config(module): - contents = module.params['config'] - if not contents: - contents = get_config(module) - return NetworkConfig(indent=1, contents=contents) - - -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() - candidate.add(module.params['lines'], parents=parents) - return candidate - - -def run(module, result): - match = module.params['match'] - replace = module.params['replace'] - replace_config = replace == 'config' - path = module.params['parents'] - comment = module.params['comment'] - admin = module.params['admin'] - check_mode = module.check_mode - - candidate = get_candidate(module) - - if match != 'none' and replace != 'config': - contents = get_running_config(module) - configobj = NetworkConfig(contents=contents, indent=1) - commands = candidate.difference(configobj, path=path, match=match, - replace=replace) - else: - commands = candidate.items - - if commands: - commands = dumps(commands, 'commands').split('\n') - - if any((module.params['lines'], module.params['src'])): - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - - diff = load_config(module, commands) - if diff: - result['diff'] = dict(prepared=diff) - result['changed'] = True - - -def main(): - """main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', - 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block', 'config']), - - config=dict(), - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - comment=dict(default=DEFAULT_COMMIT_COMMENT), - admin=dict(type='bool', default=False) - ) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('replace', 'config', ['src'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - warnings = list() - check_args(module, warnings) - - result = dict(changed=False, warnings=warnings) - - if module.params['backup']: - result['__backup__'] = get_config(module) - - run(module, result) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_factory.py b/plugins/modules/network/cnos/cnos_factory.py deleted file mode 100644 index 7aa0ab712a..0000000000 --- a/plugins/modules/network/cnos/cnos_factory.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to Reset to factory settings of Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_factory -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Reset the switch startup configuration to default (factory) - on devices running Lenovo CNOS. -description: - - This module allows you to reset a switch's startup configuration. The - method provides a way to reset the startup configuration to its factory - settings. This is helpful when you want to move the switch to another - topology as a new network device. This module uses SSH to manage network - device configuration. The result of the operation can be viewed in results - directory. -extends_documentation_fragment: -- community.general.cnos - -options: {} - -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module cnos_reload. These are - written in the main.yml file of the tasks directory. ---- -- name: Test Reset to factory - cnos_factory: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_factory_{{ inventory_hostname }}_output.txt" - -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Switch Startup Config is Reset to factory settings" -''' - -import sys -import time -import socket -import array -import json -import time -import re -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def main(): - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True), - deviceType=dict(required=True),), - supports_check_mode=False) - - command = 'write erase' - outputfile = module.params['outputfile'] - output = '' - cmd = [{'command': command, 'prompt': '[n]', 'answer': 'y'}] - output = output + str(cnos.run_cnos_commands(module, cmd)) - - # Save it into the file - file = open(outputfile, "a") - file.write(output) - file.close() - - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, - msg="Switch Startup Config is Reset to Factory settings") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_facts.py b/plugins/modules/network/cnos/cnos_facts.py deleted file mode 100644 index a3efab537f..0000000000 --- a/plugins/modules/network/cnos/cnos_facts.py +++ /dev/null @@ -1,539 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# (C) 2019 Red Hat Inc. -# Copyright (C) 2019 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Module to Collect facts from Lenovo Switches running Lenovo CNOS commands -# Lenovo Networking -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_facts -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Collect facts from remote devices running Lenovo CNOS -description: - - Collects a base set of device facts from a remote Lenovo device - running on CNOS. This module prepends all of the - base network fact keys with C(ansible_net_). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -notes: - - Tested against CNOS 10.8.1 -options: - 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: 'no' - 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. - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: '!config' -''' -EXAMPLES = ''' -Tasks: The following are examples of using the module cnos_facts. ---- -- name: Test cnos Facts - cnos_facts: - ---- -# Collect all facts from the device -- cnos_facts: - gather_subset: all - -# Collect only the config and default facts -- cnos_facts: - gather_subset: - - config - -# Do not collect hardware facts -- cnos_facts: - gather_subset: - - "!hardware" -''' -RETURN = ''' - ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list -# default - ansible_net_model: - description: The model name returned from the Lenovo CNOS device - returned: always - type: str - ansible_net_serialnum: - description: The serial number of the Lenovo CNOS device - returned: always - type: str - ansible_net_version: - description: The CNOS operating system version running on the remote device - returned: always - type: str - ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - ansible_net_image: - description: Indicates the active image for the device - returned: always - type: str -# hardware - ansible_net_memfree_mb: - description: The available free 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. - This gives information on description, mac address, mtu, speed, - duplex and operstatus - returned: when interfaces is configured - type: dict - ansible_net_neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -''' - -import re - -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import run_commands -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import check_args -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible.module_utils.six.moves import zip - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - self.PERSISTENT_COMMAND_TIMEOUT = 60 - - 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 sys-info', 'show running-config'] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - data_run = self.responses[1] - if data: - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - self.facts['model'] = self.parse_model(data) - self.facts['image'] = self.parse_image(data) - if data_run: - self.facts['hostname'] = self.parse_hostname(data_run) - - def parse_version(self, data): - for line in data.split('\n'): - line = line.strip() - match = re.match(r'System Software Revision (.*?)', - line, re.M | re.I) - if match: - vers = line.split(':') - ver = vers[1].strip() - return ver - return "NA" - - def parse_hostname(self, data_run): - for line in data_run.split('\n'): - line = line.strip() - match = re.match(r'hostname (.*?)', line, re.M | re.I) - if match: - hosts = line.split() - hostname = hosts[1].strip('\"') - return hostname - return "NA" - - def parse_model(self, data): - for line in data.split('\n'): - line = line.strip() - match = re.match(r'System Model (.*?)', line, re.M | re.I) - if match: - mdls = line.split(':') - mdl = mdls[1].strip() - return mdl - return "NA" - - def parse_image(self, data): - match = re.search(r'(.*) image(.*)', data, re.M | re.I) - if match: - return "Image1" - else: - return "Image2" - - def parse_serialnum(self, data): - for line in data.split('\n'): - line = line.strip() - match = re.match(r'System Serial Number (.*?)', line, re.M | re.I) - if match: - serNums = line.split(':') - ser = serNums[1].strip() - return ser - return "NA" - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show running-config' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.run(['show process memory']) - data = to_text(data, errors='surrogate_or_strict').strip() - data = data.replace(r"\n", "\n") - if data: - for line in data.split('\n'): - line = line.strip() - match = re.match(r'Mem: (.*?)', line, re.M | re.I) - if match: - memline = line.split(':') - mems = memline[1].strip().split() - self.facts['memtotal_mb'] = int(mems[0]) / 1024 - self.facts['memused_mb'] = int(mems[1]) / 1024 - self.facts['memfree_mb'] = int(mems[2]) / 1024 - self.facts['memshared_mb'] = int(mems[3]) / 1024 - self.facts['memavailable_mb'] = int(mems[5]) / 1024 - - def parse_memtotal(self, data): - match = re.search(r'^MemTotal:\s*(.*) kB', data, re.M | re.I) - if match: - return int(match.group(1)) / 1024 - - def parse_memfree(self, data): - match = re.search(r'^MemFree:\s*(.*) kB', data, re.M | re.I) - if match: - return int(match.group(1)) / 1024 - - -class Config(FactsBase): - - COMMANDS = ['show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = ['show interface brief'] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - data1 = self.run(['show interface status']) - data1 = to_text(data1, errors='surrogate_or_strict').strip() - data1 = data1.replace(r"\n", "\n") - data2 = self.run(['show interface mac-address']) - data2 = to_text(data2, errors='surrogate_or_strict').strip() - data2 = data2.replace(r"\n", "\n") - lines1 = None - lines2 = None - if data1: - lines1 = self.parse_interfaces(data1) - if data2: - lines2 = self.parse_interfaces(data2) - if lines1 is not None and lines2 is not None: - self.facts['interfaces'] = self.populate_interfaces(lines1, lines2) - data3 = self.run(['show lldp neighbors']) - data3 = to_text(data3, errors='surrogate_or_strict').strip() - data3 = data3.replace(r"\n", "\n") - if data3: - lines3 = self.parse_neighbors(data3) - if lines3 is not None: - self.facts['neighbors'] = self.populate_neighbors(lines3) - - data4 = self.run(['show ip interface brief vrf all']) - data5 = self.run(['show ipv6 interface brief vrf all']) - data4 = to_text(data4, errors='surrogate_or_strict').strip() - data4 = data4.replace(r"\n", "\n") - data5 = to_text(data5, errors='surrogate_or_strict').strip() - data5 = data5.replace(r"\n", "\n") - lines4 = None - lines5 = None - if data4: - lines4 = self.parse_ipaddresses(data4) - ipv4_interfaces = self.set_ip_interfaces(lines4) - self.facts['all_ipv4_addresses'] = ipv4_interfaces - if data5: - lines5 = self.parse_ipaddresses(data5) - ipv6_interfaces = self.set_ipv6_interfaces(lines5) - self.facts['all_ipv6_addresses'] = ipv6_interfaces - - def parse_ipaddresses(self, data): - parsed = list() - for line in data.split('\n'): - if len(line) == 0: - continue - else: - line = line.strip() - match = re.match(r'^(Ethernet+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(po+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(mgmt+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(loopback+)', line) - if match: - key = match.group(1) - parsed.append(line) - return parsed - - def populate_interfaces(self, lines1, lines2): - interfaces = dict() - for line1, line2 in zip(lines1, lines2): - line = line1 + " " + line2 - intfSplit = line.split() - innerData = dict() - innerData['description'] = intfSplit[1].strip() - innerData['macaddress'] = intfSplit[8].strip() - innerData['type'] = intfSplit[6].strip() - innerData['speed'] = intfSplit[5].strip() - innerData['duplex'] = intfSplit[4].strip() - innerData['operstatus'] = intfSplit[2].strip() - interfaces[intfSplit[0].strip()] = innerData - return interfaces - - def parse_interfaces(self, data): - parsed = list() - for line in data.split('\n'): - if len(line) == 0: - continue - else: - line = line.strip() - match = re.match(r'^(Ethernet+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(po+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(mgmt+)', line) - if match: - key = match.group(1) - parsed.append(line) - return parsed - - def set_ip_interfaces(self, line4): - ipv4_addresses = list() - for line in line4: - ipv4Split = line.split() - if 'Ethernet' in ipv4Split[0]: - ipv4_addresses.append(ipv4Split[1]) - if 'mgmt' in ipv4Split[0]: - ipv4_addresses.append(ipv4Split[1]) - if 'po' in ipv4Split[0]: - ipv4_addresses.append(ipv4Split[1]) - if 'loopback' in ipv4Split[0]: - ipv4_addresses.append(ipv4Split[1]) - return ipv4_addresses - - def set_ipv6_interfaces(self, line4): - ipv6_addresses = list() - for line in line4: - ipv6Split = line.split() - if 'Ethernet' in ipv6Split[0]: - ipv6_addresses.append(ipv6Split[1]) - if 'mgmt' in ipv6Split[0]: - ipv6_addresses.append(ipv6Split[1]) - if 'po' in ipv6Split[0]: - ipv6_addresses.append(ipv6Split[1]) - if 'loopback' in ipv6Split[0]: - ipv6_addresses.append(ipv6Split[1]) - return ipv6_addresses - - def populate_neighbors(self, lines3): - neighbors = dict() - device_name = '' - for line in lines3: - neighborSplit = line.split() - innerData = dict() - count = len(neighborSplit) - if count == 5: - local_interface = neighborSplit[1].strip() - innerData['Device Name'] = neighborSplit[0].strip() - innerData['Hold Time'] = neighborSplit[2].strip() - innerData['Capability'] = neighborSplit[3].strip() - innerData['Remote Port'] = neighborSplit[4].strip() - neighbors[local_interface] = innerData - elif count == 4: - local_interface = neighborSplit[0].strip() - innerData['Hold Time'] = neighborSplit[1].strip() - innerData['Capability'] = neighborSplit[2].strip() - innerData['Remote Port'] = neighborSplit[3].strip() - neighbors[local_interface] = innerData - return neighbors - - def parse_neighbors(self, neighbors): - parsed = list() - for line in neighbors.split('\n'): - if len(line) == 0: - continue - else: - line = line.strip() - if 'Ethernet' in line: - parsed.append(line) - if 'mgmt' in line: - parsed.append(line) - if 'po' in line: - parsed.append(line) - if 'loopback' in line: - parsed.append(line) - return parsed - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - -PERSISTENT_COMMAND_TIMEOUT = 60 - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - gather_subset = module.params['gather_subset'] - - runable_subsets = set() - exclude_subsets = set() - - for subset in gather_subset: - if subset == 'all': - runable_subsets.update(VALID_SUBSETS) - continue - - if subset.startswith('!'): - subset = subset[1:] - if subset == 'all': - exclude_subsets.update(VALID_SUBSETS) - continue - exclude = True - else: - exclude = False - - if subset not in VALID_SUBSETS: - module.fail_json(msg='Bad subset') - - if exclude: - exclude_subsets.add(subset) - else: - runable_subsets.add(subset) - - if not runable_subsets: - runable_subsets.update(VALID_SUBSETS) - - runable_subsets.difference_update(exclude_subsets) - runable_subsets.add('default') - - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - instances.append(FACT_SUBSETS[key](module)) - - for inst in instances: - inst.populate() - facts.update(inst.facts) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - warnings = list() - check_args(module, warnings) - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_image.py b/plugins/modules/network/cnos/cnos_image.py deleted file mode 100644 index 0ab3d809a5..0000000000 --- a/plugins/modules/network/cnos/cnos_image.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to download new image to Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_image -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Perform firmware upgrade/download from a remote server on - devices running Lenovo CNOS -description: - - This module allows you to work with switch firmware images. It provides a - way to download a firmware image to a network device from a remote server - using FTP, SFTP, TFTP, or SCP. The first step is to create a directory - from where the remote server can be reached. The next step is to provide - the full file path of the image's location. Authentication details - required by the remote server must be provided as well. By default, this - method makes the newly downloaded firmware image the active image, which - will be used by the switch during the next restart. - This module uses SSH to manage network device configuration. - The results of the operation will be placed in a directory named 'results' - that must be created by the user in their local directory to where the - playbook is run. -extends_documentation_fragment: -- community.general.cnos - -options: - protocol: - description: - - This refers to the protocol used by the network device to - interact with the remote server from where to download the - firmware image. The choices are FTP, SFTP, TFTP, or SCP. Any other - protocols will result in error. If this parameter is not specified - there is no default value to be used. - required: true - choices: [SFTP, SCP, FTP, TFTP] - serverip: - description: - - This specifies the IP Address of the remote server from where the - software image will be downloaded. - required: true - imgpath: - description: - - This specifies the full file path of the image located on the - remote server. In case the relative path is used as the variable - value, the root folder for the user of the server needs to be - specified. - required: true - imgtype: - description: - - This specifies the firmware image type to be downloaded - required: true - choices: [all, boot, os, onie] - serverusername: - description: - - Specify the username for the server relating to the protocol used - required: true - serverpassword: - description: - - Specify the password for the server relating to the protocol used -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module cnos_image. These are - written in the main.yml file of the tasks directory. ---- -- name: Test Image transfer - cnos_image: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_image_{{ inventory_hostname }}_output.txt" - protocol: "sftp" - serverip: "10.241.106.118" - imgpath: "/root/cnos_images/G8272-10.1.0.112.img" - imgtype: "os" - serverusername: "root" - serverpassword: "root123" - -- name: Test Image tftp - cnos_image: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_image_{{ inventory_hostname }}_output.txt" - protocol: "tftp" - serverip: "10.241.106.118" - imgpath: "/anil/G8272-10.2.0.34.img" - imgtype: "os" - serverusername: "root" - serverpassword: "root123" -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Image file transferred to device" -''' - -import sys -import time -import socket -import array -import json -import time -import re -import os -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def doImageDownload(module, prompt, answer): - protocol = module.params['protocol'].lower() - server = module.params['serverip'] - imgPath = module.params['imgpath'] - imgType = module.params['imgtype'] - username = module.params['serverusername'] - password = module.params['serverpassword'] - retVal = '' - command = "copy " + protocol + " " + protocol + "://" + username + "@" - command = command + server + "/" + imgPath + " system-image " - command = command + imgType + " vrf management" - cmd = [] - if(protocol == "scp"): - prompt = ['timeout', 'Confirm download operation', 'Password', - 'Do you want to change that to the standby image'] - answer = ['240', 'y', password, 'y'] - scp_cmd = [{'command': command, 'prompt': prompt, 'answer': answer, - 'check_all': True}] - cmd.extend(scp_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "sftp"): - prompt = ['Confirm download operation', 'Password', - 'Do you want to change that to the standby image'] - answer = ['y', password, 'y'] - sftp_cmd = [{'command': command, 'prompt': prompt, 'answer': answer, - 'check_all': True}] - cmd.extend(sftp_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "ftp"): - prompt = ['Confirm download operation', 'Password', - 'Do you want to change that to the standby image'] - answer = ['y', password, 'y'] - ftp_cmd = [{'command': command, 'prompt': prompt, 'answer': answer, - 'check_all': True}] - cmd.extend(ftp_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "tftp"): - command = "copy " + protocol + " " + protocol + "://" + server - command = command + "/" + imgPath + " system-image " + imgType - command = command + " vrf management" - prompt = ['Confirm download operation', - 'Do you want to change that to the standby image'] - answer = ['y', 'y'] - tftp_cmd = [{'command': command, 'prompt': prompt, 'answer': answer, - 'check_all': True}] - cmd.extend(tftp_cmd) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - else: - return "Error-110" - - return retVal -# EOM - - -def main(): - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True), - deviceType=dict(required=True), - protocol=dict(required=True), - serverip=dict(required=True), - imgpath=dict(required=True), - imgtype=dict(required=True), - serverusername=dict(required=False), - serverpassword=dict(required=False, no_log=True),), - supports_check_mode=False) - - outputfile = module.params['outputfile'] - protocol = module.params['protocol'].lower() - output = '' - - # Invoke method for image transfer from server - if(protocol == "tftp" or protocol == "ftp" or protocol == "sftp" or - protocol == "scp"): - transfer_status = doImageDownload(module, None, None) - else: - transfer_status = "Invalid Protocol option" - - output = output + "\n Image Transfer status \n" + transfer_status - - # Save it into the file - path = outputfile.rsplit('/', 1) - if not os.path.exists(path[0]): - os.makedirs(path[0]) - file = open(outputfile, "a") - file.write(output) - file.close() - - # Logic to check when changes occur or not - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, msg="Image file transferred to device") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_interface.py b/plugins/modules/network/cnos/cnos_interface.py deleted file mode 100644 index 7d32044cb6..0000000000 --- a/plugins/modules/network/cnos/cnos_interface.py +++ /dev/null @@ -1,555 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2017 Lenovo, Inc. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to work on Interfaces with Lenovo Switches -# Lenovo Networking -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_interface -author: "Anil Kumar Muraleedharan(@amuraleedhar)" -short_description: Manage Interface on Lenovo CNOS network devices -description: - - This module provides declarative management of Interfaces - on Lenovo CNOS network devices. -notes: - - Tested against CNOS 10.8.1 -options: - name: - description: - - Name of the Interface. - required: true - description: - description: - - Description of Interface. - enabled: - description: - - Interface link status. - type: bool - default: True - speed: - description: - - Interface link speed. - mtu: - description: - - Maximum size of transmit packet. - duplex: - description: - - Interface link status - default: auto - choices: ['full', 'half', 'auto'] - tx_rate: - description: - - Transmit rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules, - ../network/user_guide/network_working_with_command_output.html) - rx_rate: - description: - - Receiver rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules, - ../network/user_guide/network_working_with_command_output.html) - neighbors: - description: - - Check operational state of given interface C(name) for LLDP neighbor. - - The following suboptions are available. - suboptions: - host: - description: - - "LLDP neighbor host for given interface C(name)." - port: - description: - - "LLDP neighbor port to which interface C(name) is connected." - aggregate: - description: List of Interfaces definitions. - delay: - description: - - Time in seconds to wait before checking for the operational state on - remote device. This wait is applicable for operational state argument - which are I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate) - default: 20 - state: - description: - - State of the Interface configuration, C(up) means present and - operationally up and C(down) means present and operationally C(down) - default: present - choices: ['present', 'absent', 'up', 'down'] - provider: - description: - - B(Deprecated) - - "Starting with Ansible 2.5 we recommend using C(connection: network_cli)." - - For more information please see the L(CNOS Platform Options guide, ../network/user_guide/platform_cnos.html). - - HORIZONTALLINE - - A dict object containing connection details. - 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. - required: true - port: - description: - - Specifies the port to use when building the connection to the remote device. - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_USERNAME) will be used instead. - password: - description: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead. - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. If the timeout is - exceeded before the operation is completed, the module will error. - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. If the value is not specified - in the task, the value of environment variable C(ANSIBLE_NET_SSH_KEYFILE) - will be used instead. - 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: 'no' - 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. -''' - -EXAMPLES = """ -- name: configure interface - cnos_interface: - name: Ethernet1/33 - description: test-interface - speed: 100 - duplex: half - mtu: 999 - -- name: remove interface - cnos_interface: - name: loopback3 - state: absent - -- name: make interface up - cnos_interface: - name: Ethernet1/33 - enabled: True - -- name: make interface down - cnos_interface: - name: Ethernet1/33 - enabled: False - -- name: Check intent arguments - cnos_interface: - name: Ethernet1/33 - state: up - tx_rate: ge(0) - rx_rate: le(0) - -- name: Check neighbors intent arguments - cnos_interface: - name: Ethernet1/33 - neighbors: - - port: eth0 - host: netdev - -- name: Config + intent - cnos_interface: - name: Ethernet1/33 - enabled: False - state: down - -- name: Add interface using aggregate - cnos_interface: - aggregate: - - { name: Ethernet1/33, mtu: 256, description: test-interface-1 } - - { name: Ethernet1/44, mtu: 516, description: test-interface-2 } - duplex: full - speed: 100 - state: present - -- name: Delete interface using aggregate - cnos_interface: - aggregate: - - name: loopback3 - - name: loopback6 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always, except for the platforms that use Netconf transport to - manage the device. - type: list - sample: - - interface Ethernet1/33 - - description test-interface - - duplex half - - mtu 512 -""" -import re - -from copy import deepcopy -from time import sleep - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import exec_command -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import debugOutput, check_args -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import conditional -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - - -def validate_mtu(value, module): - if value and not 64 <= int(value) <= 9216: - module.fail_json(msg='mtu must be between 64 and 9216') - - -def validate_param_values(module, obj, param=None): - if param is None: - param = module.params - for key in obj: - # validate the param value (if validator func exists) - validator = globals().get('validate_%s' % key) - if callable(validator): - validator(param.get(key), module) - - -def parse_shutdown(configobj, name): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'^shutdown', cfg, re.M) - if match: - return True - else: - return False - - -def parse_config_argument(configobj, name, arg=None): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'%s (.+)$' % arg, cfg, re.M) - if match: - return match.group(1) - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - return None - - -def add_command_to_interface(interface, cmd, commands): - if interface not in commands: - commands.append(interface) - commands.append(cmd) - - -def map_config_to_obj(module): - config = get_config(module) - configobj = NetworkConfig(indent=1, contents=config) - - match = re.findall(r'^interface (\S+)', config, re.M) - if not match: - return list() - - instances = list() - - for item in set(match): - obj = { - 'name': item, - 'description': parse_config_argument(configobj, item, 'description'), - 'speed': parse_config_argument(configobj, item, 'speed'), - 'duplex': parse_config_argument(configobj, item, 'duplex'), - 'mtu': parse_config_argument(configobj, item, 'mtu'), - 'disable': True if parse_shutdown(configobj, item) else False, - 'state': 'present' - } - instances.append(obj) - return instances - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - validate_param_values(module, item, item) - d = item.copy() - - if d['enabled']: - d['disable'] = False - else: - d['disable'] = True - - obj.append(d) - - else: - params = { - 'name': module.params['name'], - 'description': module.params['description'], - 'speed': module.params['speed'], - 'mtu': module.params['mtu'], - 'duplex': module.params['duplex'], - 'state': module.params['state'], - 'delay': module.params['delay'], - 'tx_rate': module.params['tx_rate'], - 'rx_rate': module.params['rx_rate'], - 'neighbors': module.params['neighbors'] - } - - validate_param_values(module, params) - if module.params['enabled']: - params.update({'disable': False}) - else: - params.update({'disable': True}) - - obj.append(params) - return obj - - -def map_obj_to_commands(updates): - commands = list() - want, have = updates - - args = ('speed', 'description', 'duplex', 'mtu') - for w in want: - name = w['name'] - disable = w['disable'] - state = w['state'] - - obj_in_have = search_obj_in_list(name, have) - interface = 'interface ' + name - if state == 'absent' and obj_in_have: - commands.append('no ' + interface) - elif state in ('present', 'up', 'down'): - if obj_in_have: - for item in args: - candidate = w.get(item) - running = obj_in_have.get(item) - if candidate != running: - if candidate: - cmd = item + ' ' + str(candidate) - add_command_to_interface(interface, cmd, commands) - - if disable and not obj_in_have.get('disable', False): - add_command_to_interface(interface, 'shutdown', commands) - elif not disable and obj_in_have.get('disable', False): - add_command_to_interface(interface, 'no shutdown', commands) - else: - commands.append(interface) - for item in args: - value = w.get(item) - if value: - commands.append(item + ' ' + str(value)) - - if disable: - commands.append('no shutdown') - return commands - - -def check_declarative_intent_params(module, want, result): - failed_conditions = [] - have_neighbors_lldp = None - for w in want: - want_state = w.get('state') - want_tx_rate = w.get('tx_rate') - want_rx_rate = w.get('rx_rate') - want_neighbors = w.get('neighbors') - - if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate and not want_neighbors: - continue - - if result['changed']: - sleep(w['delay']) - - command = 'show interface %s brief' % w['name'] - rc, out, err = exec_command(module, command) - if rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc) - if want_state in ('up', 'down'): - state_data = out.strip().lower().split(w['name']) - have_state = None - have_state = state_data[1].split()[3] - if have_state is None or not conditional(want_state, have_state.strip()): - failed_conditions.append('state ' + 'eq(%s)' % want_state) - - command = 'show interface %s' % w['name'] - rc, out, err = exec_command(module, command) - have_tx_rate = None - have_rx_rate = None - rates = out.splitlines() - for s in rates: - s = s.strip() - if 'output rate' in s and 'input rate' in s: - sub = s.split() - if want_tx_rate: - have_tx_rate = sub[8] - if have_tx_rate is None or not conditional(want_tx_rate, have_tx_rate.strip(), cast=int): - failed_conditions.append('tx_rate ' + want_tx_rate) - if want_rx_rate: - have_rx_rate = sub[2] - if have_rx_rate is None or not conditional(want_rx_rate, have_rx_rate.strip(), cast=int): - failed_conditions.append('rx_rate ' + want_rx_rate) - if want_neighbors: - have_host = [] - have_port = [] - - # Process LLDP neighbors - if have_neighbors_lldp is None: - rc, have_neighbors_lldp, err = exec_command(module, 'show lldp neighbors detail') - if rc != 0: - module.fail_json(msg=to_text(err, - errors='surrogate_then_replace'), - command=command, rc=rc) - - if have_neighbors_lldp: - lines = have_neighbors_lldp.strip().split('Local Port ID: ') - for line in lines: - field = line.split('\n') - if field[0].strip() == w['name']: - for item in field: - if item.startswith('System Name:'): - have_host.append(item.split(':')[1].strip()) - if item.startswith('Port Description:'): - have_port.append(item.split(':')[1].strip()) - - for item in want_neighbors: - host = item.get('host') - port = item.get('port') - if host and host not in have_host: - failed_conditions.append('host ' + host) - if port and port not in have_port: - failed_conditions.append('port ' + port) - return failed_conditions - - -def main(): - """ main entry point for module execution - """ - neighbors_spec = dict( - host=dict(), - port=dict() - ) - - element_spec = dict( - name=dict(), - description=dict(), - speed=dict(), - mtu=dict(), - duplex=dict(default='auto', choices=['full', 'half', 'auto']), - enabled=dict(default=True, type='bool'), - tx_rate=dict(), - rx_rate=dict(), - neighbors=dict(type='list', elements='dict', options=neighbors_spec), - delay=dict(default=20, type='int'), - state=dict(default='present', - choices=['present', 'absent', 'up', 'down']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - argument_spec.update(cnos_argument_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - warnings = list() - check_args(module, warnings) - - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have)) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - failed_conditions = check_declarative_intent_params(module, want, result) - - if failed_conditions: - msg = 'One or more conditional statements have not been satisfied' - module.fail_json(msg=msg, failed_conditions=failed_conditions) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_l2_interface.py b/plugins/modules/network/cnos/cnos_l2_interface.py deleted file mode 100644 index a6decc39b9..0000000000 --- a/plugins/modules/network/cnos/cnos_l2_interface.py +++ /dev/null @@ -1,598 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2017 Lenovo, Inc. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to send banner commands to Lenovo Switches -# Two types of banners are supported login and motd -# Lenovo Networking -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_l2_interface -short_description: Manage Layer-2 interface on Lenovo CNOS devices. -description: - - This module provides declarative management of Layer-2 interfaces on - Lenovo CNOS devices. -author: - - Anil Kumar Muraleedharan (@amuraleedhar) -options: - name: - description: - - Full name of the interface excluding any logical - unit number, i.e. Ethernet1/3. - required: true - aliases: ['interface'] - mode: - description: - - Mode in which interface needs to be configured. - default: access - choices: ['access', 'trunk'] - access_vlan: - description: - - Configure given VLAN in access port. - If C(mode=access), used as the access VLAN ID. - trunk_vlans: - description: - - List of VLANs to be configured in trunk port. - If C(mode=trunk), used as the VLAN range to ADD or REMOVE - from the trunk. - native_vlan: - description: - - Native VLAN to be configured in trunk port. - If C(mode=trunk), used as the trunk native VLAN ID. - trunk_allowed_vlans: - description: - - List of allowed VLANs in a given trunk port. - If C(mode=trunk), these are the only VLANs that will be - configured on the trunk, i.e. "2-10,15". - aggregate: - description: - - List of Layer-2 interface definitions. - state: - description: - - Manage the state of the Layer-2 Interface configuration. - default: present - choices: ['present','absent', 'unconfigured'] - provider: - description: - - B(Deprecated) - - "Starting with Ansible 2.5 we recommend using - C(connection: network_cli)." - - For more information please see the - L(CNOS Platform Options guide, ../network/user_guide/platform_cnos.html). - - HORIZONTALLINE - - A dict object containing connection details. - 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. - required: true - port: - description: - - Specifies the port to use when building the connection to the - remote device. - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_USERNAME) will be used - instead. - password: - description: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used - instead. - timeout: - description: - - Specifies the timeout in seconds for communicating with the network - device for either connecting or sending commands. If the timeout - is exceeded before the operation is completed, the module will - error. - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. If the value is not - specified in the task, the value of environment variable - C(ANSIBLE_NET_SSH_KEYFILE)will be used instead. - 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: 'no' - 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. -''' - -EXAMPLES = """ -- name: Ensure Ethernet1/5 is in its default l2 interface state - cnos_l2_interface: - name: Ethernet1/5 - state: unconfigured - -- name: Ensure Ethernet1/5 is configured for access vlan 20 - cnos_l2_interface: - name: Ethernet1/5 - mode: access - access_vlan: 20 - -- name: Ensure Ethernet1/5 only has vlans 5-10 as trunk vlans - cnos_l2_interface: - name: Ethernet1/5 - mode: trunk - native_vlan: 10 - trunk_vlans: 5-10 - -- name: Ensure Ethernet1/5 is a trunk port and ensure 2-50 are being tagged - (doesn't mean others aren't also being tagged) - cnos_l2_interface: - name: Ethernet1/5 - mode: trunk - native_vlan: 10 - trunk_vlans: 2-50 - -- name: Ensure these VLANs are not being tagged on the trunk - cnos_l2_interface: - name: Ethernet1/5 - mode: trunk - trunk_vlans: 51-4094 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to - manage the device. - type: list - sample: - - interface Ethernet1/5 - - switchport access vlan 20 -""" - -import re -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import run_commands - - -def get_interface_type(interface): - intf_type = 'unknown' - if interface.upper()[:2] in ('ET', 'GI', 'FA', 'TE', 'FO', 'HU', 'TWE'): - intf_type = 'ethernet' - elif interface.upper().startswith('VL'): - intf_type = 'svi' - elif interface.upper().startswith('LO'): - intf_type = 'loopback' - elif interface.upper()[:2] in ('MG', 'MA'): - intf_type = 'management' - elif interface.upper().startswith('PO'): - intf_type = 'portchannel' - elif interface.upper().startswith('NV'): - intf_type = 'nve' - - return intf_type - - -def is_switchport(name, module): - intf_type = get_interface_type(name) - - if intf_type in ('ethernet', 'portchannel'): - config = run_commands(module, - ['show interface {0} switchport'.format(name)])[0] - match = re.search(r'Switchport : enabled', config) - return bool(match) - return False - - -def interface_is_portchannel(name, module): - if get_interface_type(name) == 'ethernet': - config = run_commands(module, ['show run interface {0}'.format(name)])[0] - if any(c in config for c in ['channel group', 'channel-group']): - return True - return False - - -def get_switchport(name, module): - config = run_commands(module, - ['show interface {0} switchport'.format(name)])[0] - mode = re.search(r'Switchport mode : (?:.* )?(\w+)$', config, re.M) - access = re.search(r'Configured Vlans : (\d+)', config) - native = re.search(r'Default/Native Vlan : (\d+)', config) - trunk = re.search(r'Enabled Vlans : (.+)$', config, re.M) - if mode: - mode = mode.group(1) - if access: - access = access.group(1) - if native: - native = native.group(1) - if trunk: - trunk = trunk.group(1) - if trunk == 'ALL': - trunk = '1-4094' - - switchport_config = { - "interface": name, - "mode": mode, - "access_vlan": access, - "native_vlan": native, - "trunk_vlans": trunk, - } - - return switchport_config - - -def remove_switchport_config_commands(name, existing, proposed, module): - mode = proposed.get('mode') - commands = [] - command = None - - if mode == 'access': - av_check = existing.get('access_vlan') == proposed.get('access_vlan') - if av_check: - command = 'no switchport access vlan' - commands.append(command) - - elif mode == 'trunk': - # Supported Remove Scenarios for trunk_vlans_list - # 1) Existing: 1,2,3 Proposed: 1,2,3 - Remove all - # 2) Existing: 1,2,3 Proposed: 1,2 - Remove 1,2 Leave 3 - # 3) Existing: 1,2,3 Proposed: 2,3 - Remove 2,3 Leave 1 - # 4) Existing: 1,2,3 Proposed: 4,5,6 - None removed. - # 5) Existing: None Proposed: 1,2,3 - None removed. - existing_vlans = existing.get('trunk_vlans_list') - proposed_vlans = proposed.get('trunk_vlans_list') - vlans_to_remove = set(proposed_vlans).intersection(existing_vlans) - - if vlans_to_remove: - proposed_allowed_vlans = proposed.get('trunk_allowed_vlans') - remove_trunk_allowed_vlans = proposed.get('trunk_vlans', - proposed_allowed_vlans) - command = 'switchport trunk allowed vlan remove {0}' - command = command.format(remove_trunk_allowed_vlans) - commands.append(command) - - native_check = existing.get('native_vlan') == proposed.get('native_vlan') - if native_check and proposed.get('native_vlan'): - command = 'no switchport trunk native vlan' - commands.append(command) - - if commands: - commands.insert(0, 'interface ' + name) - return commands - - -def get_switchport_config_commands(name, existing, proposed, module): - """Gets commands required to config a given switchport interface - """ - - proposed_mode = proposed.get('mode') - existing_mode = existing.get('mode') - commands = [] - command = None - - if proposed_mode != existing_mode: - if proposed_mode == 'trunk': - command = 'switchport mode trunk' - elif proposed_mode == 'access': - command = 'switchport mode access' - - if command: - commands.append(command) - - if proposed_mode == 'access': - av_check = str(existing.get('access_vlan')) == str(proposed.get('access_vlan')) - if not av_check: - command = 'switchport access vlan {0}'.format(proposed.get('access_vlan')) - commands.append(command) - - elif proposed_mode == 'trunk': - tv_check = existing.get('trunk_vlans_list') == proposed.get('trunk_vlans_list') - - if not tv_check: - if proposed.get('allowed'): - command = 'switchport trunk allowed vlan {0}' - command = command.format(proposed.get('trunk_allowed_vlans')) - commands.append(command) - - else: - existing_vlans = existing.get('trunk_vlans_list') - proposed_vlans = proposed.get('trunk_vlans_list') - vlans_to_add = set(proposed_vlans).difference(existing_vlans) - if vlans_to_add: - command = 'switchport trunk allowed vlan add {0}' - command = command.format(proposed.get('trunk_vlans')) - commands.append(command) - - native_check = str(existing.get('native_vlan')) == str(proposed.get('native_vlan')) - if not native_check and proposed.get('native_vlan'): - command = 'switchport trunk native vlan {0}' - command = command.format(proposed.get('native_vlan')) - commands.append(command) - - if commands: - commands.insert(0, 'interface ' + name) - return commands - - -def is_switchport_default(existing): - """Determines if switchport has a default config based on mode - Args: - existing (dict): existing switchport configuration from Ansible mod - Returns: - boolean: True if switchport has OOB Layer 2 config, i.e. - vlan 1 and trunk all and mode is access - """ - - c1 = str(existing['access_vlan']) == '1' - c2 = str(existing['native_vlan']) == '1' - c3 = existing['trunk_vlans'] == '1-4094' - c4 = existing['mode'] == 'access' - - default = c1 and c2 and c3 and c4 - - return default - - -def default_switchport_config(name): - commands = [] - commands.append('interface ' + name) - commands.append('switchport mode access') - commands.append('switch access vlan 1') - commands.append('switchport trunk native vlan 1') - commands.append('switchport trunk allowed vlan all') - return commands - - -def vlan_range_to_list(vlans): - result = [] - if vlans: - for part in vlans.split(','): - if part.lower() == 'none': - break - if part: - if '-' in part: - start, stop = (int(i) for i in part.split('-')) - result.extend(range(start, stop + 1)) - else: - result.append(int(part)) - return sorted(result) - - -def get_list_of_vlans(module): - config = run_commands(module, ['show vlan'])[0] - vlans = set() - - lines = config.strip().splitlines() - for line in lines: - line_parts = line.split() - if line_parts: - try: - int(line_parts[0]) - except ValueError: - continue - vlans.add(line_parts[0]) - - return list(vlans) - - -def flatten_list(commands): - flat_list = [] - for command in commands: - if isinstance(command, list): - flat_list.extend(command) - else: - flat_list.append(command) - return flat_list - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - obj.append(item.copy()) - else: - obj.append({ - 'name': module.params['name'], - 'mode': module.params['mode'], - 'access_vlan': module.params['access_vlan'], - 'native_vlan': module.params['native_vlan'], - 'trunk_vlans': module.params['trunk_vlans'], - 'trunk_allowed_vlans': module.params['trunk_allowed_vlans'], - 'state': module.params['state'] - }) - - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - name=dict(type='str', aliases=['interface']), - mode=dict(choices=['access', 'trunk'], default='access'), - access_vlan=dict(type='str'), - native_vlan=dict(type='str'), - trunk_vlans=dict(type='str'), - trunk_allowed_vlans=dict(type='str'), - state=dict(choices=['absent', 'present', 'unconfigured'], - default='present') - ) - - aggregate_spec = deepcopy(element_spec) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - argument_spec.update(cnos_argument_spec) - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=[['access_vlan', 'trunk_vlans'], - ['access_vlan', 'native_vlan'], - ['access_vlan', 'trunk_allowed_vlans']], - supports_check_mode=True) - - warnings = list() - commands = [] - result = {'changed': False, 'warnings': warnings} - - want = map_params_to_obj(module) - for w in want: - name = w['name'] - mode = w['mode'] - access_vlan = w['access_vlan'] - state = w['state'] - trunk_vlans = w['trunk_vlans'] - native_vlan = w['native_vlan'] - trunk_allowed_vlans = w['trunk_allowed_vlans'] - - args = dict(name=name, mode=mode, access_vlan=access_vlan, - native_vlan=native_vlan, trunk_vlans=trunk_vlans, - trunk_allowed_vlans=trunk_allowed_vlans) - - proposed = dict((k, v) for k, v in args.items() if v is not None) - - name = name.lower() - - if mode == 'access' and state == 'present' and not access_vlan: - msg = 'access_vlan param required for mode=access && state=present' - module.fail_json(msg=msg) - - if mode == 'trunk' and access_vlan: - msg = 'access_vlan param not supported when using mode=trunk' - module.fail_json(msg=msg) - - if not is_switchport(name, module): - module.fail_json(msg='Ensure interface is configured to be a L2' - '\nport first before using this module. You can use' - '\nthe cnos_interface module for this.') - - if interface_is_portchannel(name, module): - module.fail_json(msg='Cannot change L2 config on physical ' - '\nport because it is in a portchannel. ' - '\nYou should update the portchannel config.') - - # existing will never be null for Eth intfs as there is always a default - existing = get_switchport(name, module) - - # Safeguard check - # If there isn't an existing, something is wrong per previous comment - if not existing: - msg = 'Make sure you are using the FULL interface name' - module.fail_json(msg=msg) - - if trunk_vlans or trunk_allowed_vlans: - if trunk_vlans: - trunk_vlans_list = vlan_range_to_list(trunk_vlans) - elif trunk_allowed_vlans: - trunk_vlans_list = vlan_range_to_list(trunk_allowed_vlans) - proposed['allowed'] = True - - existing_trunks_list = vlan_range_to_list((existing['trunk_vlans'])) - - existing['trunk_vlans_list'] = existing_trunks_list - proposed['trunk_vlans_list'] = trunk_vlans_list - - current_vlans = get_list_of_vlans(module) - - if state == 'present': - if access_vlan and access_vlan not in current_vlans: - module.fail_json(msg='You are trying to configure a VLAN' - ' on an interface that\ndoes not exist on the ' - ' switch yet!', vlan=access_vlan) - elif native_vlan and native_vlan not in current_vlans: - module.fail_json(msg='You are trying to configure a VLAN on' - ' an interface that\ndoes not exist on the ' - ' switch yet!', vlan=native_vlan) - else: - command = get_switchport_config_commands(name, existing, - proposed, module) - commands.append(command) - elif state == 'unconfigured': - is_default = is_switchport_default(existing) - if not is_default: - command = default_switchport_config(name) - commands.append(command) - elif state == 'absent': - command = remove_switchport_config_commands(name, existing, - proposed, module) - commands.append(command) - - if trunk_vlans or trunk_allowed_vlans: - existing.pop('trunk_vlans_list') - proposed.pop('trunk_vlans_list') - - cmds = flatten_list(commands) - if cmds: - if module.check_mode: - module.exit_json(changed=True, commands=cmds) - else: - result['changed'] = True - load_config(module, cmds) - if 'configure' in cmds: - cmds.pop(0) - - result['commands'] = cmds - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_l3_interface.py b/plugins/modules/network/cnos/cnos_l3_interface.py deleted file mode 100644 index cf68a237f6..0000000000 --- a/plugins/modules/network/cnos/cnos_l3_interface.py +++ /dev/null @@ -1,461 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2019 Lenovo, Inc. -# (c) 2019, Ansible by Red Hat, 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 . -# -# Module to work on Link Aggregation with Lenovo Switches -# Lenovo Networking -# -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_l3_interface -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage Layer-3 interfaces on Lenovo CNOS network devices. -description: - - This module provides declarative management of Layer-3 interfaces - on CNOS network devices. -notes: - - Tested against CNOS 10.8.1 -options: - name: - description: - - Name of the Layer-3 interface to be configured eg. Ethernet1/2 - ipv4: - description: - - IPv4 address to be set for the Layer-3 interface mentioned in I(name) - option. The address format is /, the mask is number - in range 0-32 eg. 10.241.107.1/24 - ipv6: - description: - - IPv6 address to be set for the Layer-3 interface mentioned in I(name) - option. The address format is /, the mask is number - in range 0-128 eg. fd5d:12c9:2201:1::1/64 - aggregate: - description: - - List of Layer-3 interfaces definitions. Each of the entry in aggregate - list should define name of interface C(name) and a optional C(ipv4) or - C(ipv6) address. - state: - description: - - State of the Layer-3 interface configuration. It indicates if the - configuration should be present or absent on remote device. - default: present - choices: ['present', 'absent'] - provider: - description: - - B(Deprecated) - - "Starting with Ansible 2.5 we recommend using - C(connection: network_cli)." - - For more information please see the - L(CNOS Platform Options guide, ../network/user_guide/platform_cnos.html). - - HORIZONTALLINE - - A dict object containing connection details. - 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. - required: true - port: - description: - - Specifies the port to use when building the connection to the - remote device. - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_USERNAME) will be used - instead. - password: - description: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used - instead. - timeout: - description: - - Specifies the timeout in seconds for communicating with the network - device for either connecting or sending commands. If the timeout - is exceeded before the operation is completed, the module will - error. - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. If the value is not - specified in the task, the value of environment variable - C(ANSIBLE_NET_SSH_KEYFILE)will be used instead. - 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: 'no' - 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. -''' - -EXAMPLES = """ -- name: Remove Ethernet1/33 IPv4 and IPv6 address - cnos_l3_interface: - name: Ethernet1/33 - state: absent - -- name: Set Ethernet1/33 IPv4 address - cnos_l3_interface: - name: Ethernet1/33 - ipv4: 10.241.107.1/24 - -- name: Set Ethernet1/33 IPv6 address - cnos_l3_interface: - name: Ethernet1/33 - ipv6: "fd5d:12c9:2201:1::1/64" - -- name: Set Ethernet1/33 in dhcp - cnos_l3_interface: - name: Ethernet1/33 - ipv4: dhcp - ipv6: dhcp - -- name: Set interface Vlan1 (SVI) IPv4 address - cnos_l3_interface: - name: Vlan1 - ipv4: 192.168.0.5/24 - -- name: Set IP addresses on aggregate - cnos_l3_interface: - aggregate: - - { name: Ethernet1/33, ipv4: 10.241.107.1/24 } - - { name: Ethernet1/44, ipv4: 10.240.106.1/24, - ipv6: "fd5d:12c9:2201:1::1/64" } - -- name: Remove IP addresses on aggregate - cnos_l3_interface: - aggregate: - - { name: Ethernet1/33, ipv4: 10.241.107.1/24 } - - { name: Ethernet1/44, ipv4: 10.240.106.1/24, - ipv6: "fd5d:12c9:2201:1::1/64" } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to - manage the device. - type: list - sample: - - interface Ethernet1/33 - - ip address 10.241.107.1 255.255.255.0 - - ipv6 address fd5d:12c9:2201:1::1/64 -""" -import re - -from copy import deepcopy - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import run_commands -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import is_netmask, is_masklen -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_netmask, to_masklen - - -def validate_ipv4(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json( - msg='address format is /,got invalid format %s' % value) - if not is_masklen(address[1]): - module.fail_json( - msg='invalid value for mask: %s, mask should be in range 0-32' % address[1]) - - -def validate_ipv6(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json( - msg='address format is /, got invalid format %s' % value) - else: - if not 0 <= int(address[1]) <= 128: - module.fail_json( - msg='invalid value for mask: %s, mask should be in range 0-128' % address[1]) - - -def validate_param_values(module, obj, param=None): - if param is None: - param = module.params - for key in obj: - # validate the param value (if validator func exists) - validator = globals().get('validate_%s' % key) - if callable(validator): - validator(param.get(key), module) - - -def parse_config_argument(configobj, name, arg=None): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - - values = [] - matches = re.finditer(r'%s (.+)$' % arg, cfg, re.M) - for match in matches: - match_str = match.group(1).strip() - if arg == 'ipv6 address': - values.append(match_str) - else: - values = match_str - break - - return values or None - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'].lower() == name.lower(): - return o - - return None - - -def get_interface_type(interface): - intf_type = 'unknown' - if interface.upper()[:2] in ('ET', 'GI', 'FA', 'TE', 'FO', 'HU', 'TWE'): - intf_type = 'ethernet' - elif interface.upper().startswith('VL'): - intf_type = 'svi' - elif interface.upper().startswith('LO'): - intf_type = 'loopback' - elif interface.upper()[:2] in ('MG', 'MA'): - intf_type = 'management' - elif interface.upper().startswith('PO'): - intf_type = 'portchannel' - elif interface.upper().startswith('NV'): - intf_type = 'nve' - - return intf_type - - -def is_switchport(name, module): - intf_type = get_interface_type(name) - - if intf_type in ('ethernet', 'portchannel'): - config = run_commands(module, - ['show interface {0} switchport'.format(name)])[0] - match = re.search(r'Switchport : enabled', config) - return bool(match) - return False - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - for w in want: - name = w['name'] - ipv4 = w['ipv4'] - ipv6 = w['ipv6'] - state = w['state'] - - interface = 'interface ' + name - commands.append(interface) - - obj_in_have = search_obj_in_list(name, have) - if state == 'absent' and obj_in_have: - if obj_in_have['ipv4']: - if ipv4: - address = ipv4.split('/') - if len(address) == 2: - ipv4 = '{0} {1}'.format( - address[0], to_netmask(address[1])) - commands.append('no ip address %s' % ipv4) - else: - commands.append('no ip address') - if obj_in_have['ipv6']: - if ipv6: - commands.append('no ipv6 address %s' % ipv6) - else: - commands.append('no ipv6 address') - if 'dhcp' in obj_in_have['ipv6']: - commands.append('no ipv6 address dhcp') - - elif state == 'present': - if ipv4: - if obj_in_have is None or obj_in_have.get('ipv4') is None or ipv4 != obj_in_have['ipv4']: - address = ipv4.split('/') - if len(address) == 2: - ipv4 = '{0} {1}'.format( - address[0], to_netmask(address[1])) - commands.append('ip address %s' % ipv4) - - if ipv6: - if obj_in_have is None or obj_in_have.get('ipv6') is None or ipv6.lower() not in [addr.lower() for addr in obj_in_have['ipv6']]: - commands.append('ipv6 address %s' % ipv6) - if commands[-1] == interface: - commands.pop(-1) - - return commands - - -def map_config_to_obj(module): - config = get_config(module) - configobj = NetworkConfig(indent=1, contents=config) - - match = re.findall(r'^interface (\S+)', config, re.M) - if not match: - return list() - - instances = list() - - for item in set(match): - ipv4 = parse_config_argument(configobj, item, 'ip address') - if ipv4: - # eg. 192.168.2.10 255.255.255.0 -> 192.168.2.10/24 - address = ipv4.strip().split(' ') - if len(address) == 2 and is_netmask(address[1]): - ipv4 = '{0}/{1}'.format(address[0], to_text(to_masklen(address[1]))) - - obj = { - 'name': item, - 'ipv4': ipv4, - 'ipv6': parse_config_argument(configobj, item, 'ipv6 address'), - 'state': 'present' - } - instances.append(obj) - - return instances - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - validate_param_values(module, item, item) - obj.append(item.copy()) - else: - obj.append({ - 'name': module.params['name'], - 'ipv4': module.params['ipv4'], - 'ipv6': module.params['ipv6'], - 'state': module.params['state'] - }) - - validate_param_values(module, obj) - - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - name=dict(), - ipv4=dict(), - ipv6=dict(), - state=dict(default='present', - choices=['present', 'absent']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - argument_spec.update(cnos_argument_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - - result = {'changed': False} - - want = map_params_to_obj(module) - for w in want: - name = w['name'] - name = name.lower() - if is_switchport(name, module): - module.fail_json(msg='Ensure interface is configured to be a L3' - '\nport first before using this module. You can use' - '\nthe cnos_interface module for this.') - - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - resp = load_config(module, commands) - if resp is not None: - warnings.extend((out for out in resp if out)) - - result['changed'] = True - - if warnings: - result['warnings'] = warnings - if 'overlaps with address configured on' in warnings[0]: - result['failed'] = True - result['msg'] = warnings[0] - if 'Cannot set overlapping address' in warnings[0]: - result['failed'] = True - result['msg'] = warnings[0] - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_linkagg.py b/plugins/modules/network/cnos/cnos_linkagg.py deleted file mode 100644 index 1725bafa82..0000000000 --- a/plugins/modules/network/cnos/cnos_linkagg.py +++ /dev/null @@ -1,391 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, Inc. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to work on Link Aggregation with Lenovo Switches -# Lenovo Networking -# -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_linkagg -author: "Anil Kumar Muraleedharan (@auraleedhar)" -short_description: Manage link aggregation groups on Lenovo CNOS devices -description: - - This module provides declarative management of link aggregation groups - on Lenovo CNOS network devices. -notes: - - Tested against CNOS 10.8.1 -options: - group: - description: - - Channel-group number for the port-channel - Link aggregation group. Range 1-255. - mode: - description: - - Mode of the link aggregation group. - choices: ['active', 'on', 'passive'] - members: - description: - - List of members of the link aggregation group. - aggregate: - description: List of link aggregation definitions. - state: - description: - - State of the link aggregation group. - default: present - choices: ['present', 'absent'] - purge: - description: - - Purge links not defined in the I(aggregate) parameter. - type: bool - default: no - provider: - description: - - B(Deprecated) - - "Starting with Ansible 2.5 we recommend using C(connection: network_cli)." - - For more information please see the L(CNOS Platform Options guide, ../network/user_guide/platform_cnos.html). - - HORIZONTALLINE - - A dict object containing connection details. - 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. - required: true - port: - description: - - Specifies the port to use when building the connection to the remote device. - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_USERNAME) will be used instead. - password: - description: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead. - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. If the timeout is - exceeded before the operation is completed, the module will error. - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. If the value is not specified - in the task, the value of environment variable C(ANSIBLE_NET_SSH_KEYFILE) - will be used instead. - 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: 'no' - 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. -''' - -EXAMPLES = """ -- name: create link aggregation group - cnos_linkagg: - group: 10 - state: present - -- name: delete link aggregation group - cnos_linkagg: - group: 10 - state: absent - -- name: set link aggregation group to members - cnos_linkagg: - group: 200 - mode: active - members: - - Ethernet1/33 - - Ethernet1/44 - -- name: remove link aggregation group from GigabitEthernet0/0 - cnos_linkagg: - group: 200 - mode: active - members: - - Ethernet1/33 - -- name: Create aggregate of linkagg definitions - cnos_linkagg: - aggregate: - - { group: 3, mode: on, members: [Ethernet1/33] } - - { group: 100, mode: passive, members: [Ethernet1/44] } -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to - manage the device. - type: list - sample: - - interface port-channel 30 - - interface Ethernet1/33 - - channel-group 30 mode on - - no interface port-channel 30 -""" - -import re -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import CustomNetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec - - -def search_obj_in_list(group, lst): - for o in lst: - if o['group'] == group: - return o - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params['purge'] - - for w in want: - group = w['group'] - mode = w['mode'] - members = w.get('members') or [] - state = w['state'] - del w['state'] - - obj_in_have = search_obj_in_list(group, have) - - if state == 'absent': - if obj_in_have: - commands.append('no interface port-channel {0}'.format(group)) - - elif state == 'present': - cmd = ['interface port-channel {0}'.format(group), - 'exit'] - if not obj_in_have: - if not group: - module.fail_json(msg='group is a required option') - commands.extend(cmd) - - if members: - for m in members: - commands.append('interface {0}'.format(m)) - commands.append('channel-group {0} mode {1}'.format(group, mode)) - - else: - if members: - if 'members' not in obj_in_have.keys(): - for m in members: - commands.extend(cmd) - commands.append('interface {0}'.format(m)) - commands.append('channel-group {0} mode {1}'.format(group, mode)) - - elif set(members) != set(obj_in_have['members']): - missing_members = list(set(members) - set(obj_in_have['members'])) - for m in missing_members: - commands.extend(cmd) - commands.append('interface {0}'.format(m)) - commands.append('channel-group {0} mode {1}'.format(group, mode)) - - superfluous_members = list(set(obj_in_have['members']) - set(members)) - for m in superfluous_members: - commands.extend(cmd) - commands.append('interface {0}'.format(m)) - commands.append('no channel-group') - - if purge: - for h in have: - obj_in_want = search_obj_in_list(h['group'], want) - if not obj_in_want: - commands.append('no interface port-channel {0}'.format(h['group'])) - - return commands - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - d = item.copy() - d['group'] = str(d['group']) - - obj.append(d) - else: - obj.append({ - 'group': str(module.params['group']), - 'mode': module.params['mode'], - 'members': module.params['members'], - 'state': module.params['state'] - }) - - return obj - - -def parse_mode(module, config, group, member): - mode = None - netcfg = CustomNetworkConfig(indent=1, contents=config) - parents = ['interface {0}'.format(member)] - body = netcfg.get_section(parents) - - match_int = re.findall(r'interface {0}\n'.format(member), body, re.M) - if match_int: - match = re.search(r'channel-group {0} mode (\S+)'.format(group), - body, re.M) - if match: - mode = match.group(1) - - return mode - - -def parse_members(module, config, group): - members = [] - - for line in config.strip().split('!'): - l = line.strip() - if l.startswith('interface'): - match_group = re.findall(r'channel-group {0} mode'.format(group), l, re.M) - if match_group: - match = re.search(r'interface (\S+)', l, re.M) - if match: - members.append(match.group(1)) - - return members - - -def get_channel(module, config, group): - match = re.findall(r'^interface (\S+)', config, re.M) - - if not match: - return {} - - channel = {} - for item in set(match): - member = item - channel['mode'] = parse_mode(module, config, group, member) - channel['members'] = parse_members(module, config, group) - - return channel - - -def map_config_to_obj(module): - objs = list() - config = get_config(module) - - for line in config.split('\n'): - l = line.strip() - match = re.search(r'interface port-channel(\S+)', l, re.M) - if match: - obj = {} - group = match.group(1) - obj['group'] = group - obj.update(get_channel(module, config, group)) - objs.append(obj) - - return objs - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - group=dict(type='int'), - mode=dict(choices=['active', 'on', 'passive']), - members=dict(type='list'), - state=dict(default='present', - choices=['present', 'absent']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['group'] = dict(required=True) - - required_one_of = [['group', 'aggregate']] - required_together = [['members', 'mode']] - mutually_exclusive = [['group', 'aggregate']] - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec, - required_together=required_together), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - argument_spec.update(cnos_argument_spec) - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - required_together=required_together, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_lldp.py b/plugins/modules/network/cnos/cnos_lldp.py deleted file mode 100644 index d9853de516..0000000000 --- a/plugins/modules/network/cnos/cnos_lldp.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2019 Lenovo. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to work on Link Aggregation with Lenovo Switches -# Lenovo Networking -# -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_lldp -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage LLDP configuration on Lenovo CNOS network devices. -description: - - This module provides declarative management of LLDP service - on Lenovc CNOS network devices. -notes: - - Tested against CNOS 10.9.1 -options: - state: - description: - - State of the LLDP configuration. If value is I(present) lldp will be - enabled else if it is I(absent) it will be disabled. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: Enable LLDP service - cnos_lldp: - state: present - -- name: Disable LLDP service - cnos_lldp: - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to - manage the device. - type: list - sample: - - lldp timer 1024 - - lldp trap-interval 330 -""" -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import debugOutput, run_commands -from ansible.module_utils.connection import exec_command - - -def get_ethernet_range(module): - output = run_commands(module, ['show interface brief'])[0].split('\n') - maxport = None - last_interface = None - for line in output: - if line.startswith('Ethernet1/'): - last_interface = line.split(' ')[0] - if last_interface is not None: - eths = last_interface.split('/') - maxport = eths[1] - return maxport - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - state=dict(default='present', - choices=['present', 'absent']) - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - warnings = list() - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - maxport = get_ethernet_range(module) - commands = [] - prime_cmd = 'interface ethernet 1/1-' + maxport - - if module.params['state'] == 'absent': - commands.append(prime_cmd) - commands.append('no lldp receive') - commands.append('no lldp transmit') - commands.append('exit') - commands.append('interface mgmt 0') - commands.append('no lldp receive') - commands.append('no lldp transmit') - commands.append('exit') - elif module.params['state'] == 'present': - commands.append(prime_cmd) - commands.append('lldp receive') - commands.append('lldp transmit') - commands.append('exit') - commands.append('interface mgmt 0') - commands.append('lldp receive') - commands.append('lldp transmit') - commands.append('exit') - - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_logging.py b/plugins/modules/network/cnos/cnos_logging.py deleted file mode 100644 index e78174b4e6..0000000000 --- a/plugins/modules/network/cnos/cnos_logging.py +++ /dev/null @@ -1,425 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2019 Lenovo, Inc. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to work on Link Aggregation with Lenovo Switches -# Lenovo Networking -# -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_logging -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage logging on network devices -description: - - This module provides declarative management of logging - on Cisco Cnos devices. -notes: - - Tested against CNOS 10.9.1 -options: - dest: - description: - - Destination of the logs. Lenovo uses the term server instead of host in - its CLI. - choices: ['server', 'console', 'monitor', 'logfile'] - name: - description: - - If value of C(dest) is I(file) it indicates file-name - and for I(server) indicates the server name to be notified. - size: - description: - - Size of buffer. The acceptable value is in range from 4096 to - 4294967295 bytes. - default: 10485760 - facility: - description: - - Set logging facility. This is applicable only for server logging - level: - description: - - Set logging severity levels. 0-emerg;1-alert;2-crit;3-err;4-warn; - 5-notif;6-inform;7-debug - default: 5 - aggregate: - description: List of logging definitions. - state: - description: - - State of the logging configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: configure server logging - cnos_logging: - dest: server - name: 10.241.107.224 - facility: local7 - state: present - -- name: remove server logging configuration - cnos_logging: - dest: server - name: 10.241.107.224 - state: absent - -- name: configure console logging level and facility - cnos_logging: - dest: console - level: 7 - state: present - -- name: configure buffer size - cnos_logging: - dest: logfile - level: 5 - name: testfile - size: 5000 - -- name: Configure logging using aggregate - cnos_logging: - aggregate: - - { dest: console, level: 6 } - - { dest: logfile, size: 9000 } - -- name: remove logging using aggregate - cnos_logging: - aggregate: - - { dest: console, level: 6 } - - { dest: logfile, name: anil, size: 9000 } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - logging console 7 - - logging server 10.241.107.224 -""" - -import re - -from copy import deepcopy -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import validate_ip_address -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_capabilities -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import check_args -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec - - -def validate_size(value, module): - if value: - if not int(4096) <= int(value) <= int(4294967295): - module.fail_json(msg='size must be between 4096 and 4294967295') - else: - return value - - -def map_obj_to_commands(updates, module): - dest_group = ('console', 'monitor', 'logfile', 'server') - commands = list() - want, have = updates - for w in want: - dest = w['dest'] - name = w['name'] - size = w['size'] - facility = w['facility'] - level = w['level'] - state = w['state'] - del w['state'] - - if state == 'absent': - if dest: - - if dest == 'server': - commands.append('no logging server {0}'.format(name)) - - elif dest in dest_group: - commands.append('no logging {0}'.format(dest)) - - else: - module.fail_json(msg='dest must be among console, monitor, logfile, server') - - if state == 'present' and w not in have: - if dest == 'server': - cmd_str = 'logging server {0}'.format(name) - if level is not None and level > 0 and level < 8: - cmd_str = cmd_str + ' ' + level - if facility is not None: - cmd_str = cmd_str + ' facility ' + facility - commands.append(cmd_str) - - elif dest == 'logfile' and size: - present = False - - for entry in have: - if entry['dest'] == 'logfile' and entry['size'] == size and entry['level'] == level: - present = True - - if not present: - cmd_str = 'logging logfile ' - if name is not None: - cmd_str = cmd_str + name - if level and level != '7': - cmd_str = cmd_str + ' ' + level - else: - cmd_str = cmd_str + ' 7' - if size is not None: - cmd_str = cmd_str + ' size ' + size - commands.append(cmd_str) - else: - module.fail_json(msg='Name of the logfile is a mandatory parameter') - - else: - if dest: - dest_cmd = 'logging {0}'.format(dest) - if level: - dest_cmd += ' {0}'.format(level) - commands.append(dest_cmd) - return commands - - -def parse_facility(line, dest): - facility = None - if dest == 'server': - result = line.split() - i = 0 - for x in result: - if x == 'facility': - return result[i + 1] - i = i + 1 - return facility - - -def parse_size(line, dest): - size = None - if dest == 'logfile': - if 'logging logfile' in line: - result = line.split() - i = 0 - for x in result: - if x == 'size': - return result[i + 1] - i = i + 1 - return '10485760' - return size - - -def parse_name(line, dest): - name = None - if dest == 'server': - if 'logging server' in line: - result = line.split() - i = 0 - for x in result: - if x == 'server': - name = result[i + 1] - elif dest == 'logfile': - if 'logging logfile' in line: - result = line.split() - i = 0 - for x in result: - if x == 'logfile': - name = result[i + 1] - else: - name = None - return name - - -def parse_level(line, dest): - level_group = ('0', '1', '2', '3', '4', '5', '6', '7') - level = '7' - if dest == 'server': - if 'logging server' in line: - result = line.split() - if(len(result) > 3): - if result[3].isdigit(): - level = result[3] - else: - if dest == 'logfile': - if 'logging logfile' in line: - result = line.split() - if result[3].isdigit(): - level = result[3] - else: - match = re.search(r'logging {0} (\S+)'.format(dest), line, re.M) - - return level - - -def map_config_to_obj(module): - obj = [] - dest_group = ('console', 'server', 'monitor', 'logfile') - data = get_config(module, flags=['| include logging']) - index = 0 - for line in data.split('\n'): - logs = line.split() - index = len(logs) - if index == 0 or index == 1: - continue - if logs[0] != 'logging': - continue - if logs[1] == 'monitor' or logs[1] == 'console': - obj.append({'dest': logs[1], 'level': logs[2]}) - elif logs[1] == 'logfile': - level = '5' - if index > 3 and logs[3].isdigit(): - level = logs[3] - size = '10485760' - if len(logs) > 4: - size = logs[5] - obj.append({'dest': logs[1], 'name': logs[2], 'size': size, 'level': level}) - elif logs[1] == 'server': - level = '5' - facility = None - - if index > 3 and logs[3].isdigit(): - level = logs[3] - if index > 3 and logs[3] == 'facility': - facility = logs[4] - if index > 4 and logs[4] == 'facility': - facility = logs[5] - obj.append({'dest': logs[1], 'name': logs[2], 'facility': facility, 'level': level}) - else: - continue - return obj - - -def map_params_to_obj(module, required_if=None): - obj = [] - aggregate = module.params.get('aggregate') - - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - module._check_required_if(required_if, item) - - d = item.copy() - if d['dest'] != 'server' and d['dest'] != 'logfile': - d['name'] = None - - if d['dest'] == 'logfile': - if 'size' in d: - d['size'] = str(validate_size(d['size'], module)) - elif 'size' not in d: - d['size'] = str(10485760) - else: - pass - - if d['dest'] != 'logfile': - d['size'] = None - - obj.append(d) - - else: - if module.params['dest'] != 'server' and module.params['dest'] != 'logfile': - module.params['name'] = None - - if module.params['dest'] == 'logfile': - if not module.params['size']: - module.params['size'] = str(10485760) - else: - module.params['size'] = None - - if module.params['size'] is None: - obj.append({ - 'dest': module.params['dest'], - 'name': module.params['name'], - 'size': module.params['size'], - 'facility': module.params['facility'], - 'level': module.params['level'], - 'state': module.params['state'] - }) - - else: - obj.append({ - 'dest': module.params['dest'], - 'name': module.params['name'], - 'size': str(validate_size(module.params['size'], module)), - 'facility': module.params['facility'], - 'level': module.params['level'], - 'state': module.params['state'] - }) - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - dest=dict(type='str', - choices=['server', 'console', 'monitor', 'logfile']), - name=dict(type='str'), - size=dict(type='int', default=10485760), - facility=dict(type='str'), - level=dict(type='str', default='5'), - state=dict(default='present', choices=['present', 'absent']), - ) - - aggregate_spec = deepcopy(element_spec) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - - required_if = [('dest', 'server', ['name'])] - - module = AnsibleModule(argument_spec=argument_spec, - required_if=required_if, - supports_check_mode=True) - warnings = list() - check_args(module, warnings) - - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module, required_if=required_if) - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_reload.py b/plugins/modules/network/cnos/cnos_reload.py deleted file mode 100644 index 0d456e9028..0000000000 --- a/plugins/modules/network/cnos/cnos_reload.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to reload Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_reload -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Perform switch restart on devices running Lenovo CNOS -description: - - This module allows you to restart the switch using the current startup - configuration. The module is usually invoked after the running - configuration has been saved over the startup configuration. - This module uses SSH to manage network device configuration. - The results of the operation can be viewed in results directory. -extends_documentation_fragment: -- community.general.cnos - -options: {} - -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module cnos_reload. These are - written in the main.yml file of the tasks directory. ---- -- name: Test Reload - cnos_reload: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_reload_{{ inventory_hostname }}_output.txt" -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Device is Reloading. Please wait..." -''' - -import sys -import time -import socket -import array -import json -import time -import re -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def main(): - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True), - deviceType=dict(required=True),), - supports_check_mode=False) - - command = 'reload' - outputfile = module.params['outputfile'] - output = '' - cmd = [{'command': command, 'prompt': 'reboot system? (y/n): ', - 'answer': 'y'}] - output = output + str(cnos.run_cnos_commands(module, cmd)) - - # Save it into the file - file = open(outputfile, "a") - file.write(output) - file.close() - - errorMsg = cnos.checkOutputForError(output) - if(errorMsg in "Device Response Timed out"): - module.exit_json(changed=True, - msg="Device is Reloading. Please wait...") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_rollback.py b/plugins/modules/network/cnos/cnos_rollback.py deleted file mode 100644 index f8a2e1179c..0000000000 --- a/plugins/modules/network/cnos/cnos_rollback.py +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to Rollback Config back to Lenovo Switches -# -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_rollback -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Roll back the running or startup configuration from a remote - server on devices running Lenovo CNOS -description: - - This module allows you to work with switch configurations. It provides a - way to roll back configurations of a switch from a remote server. This is - achieved by using startup or running configurations of the target device - that were previously backed up to a remote server using FTP, SFTP, TFTP, - or SCP. The first step is to create a directory from where the remote - server can be reached. The next step is to provide the full file path of - he backup configuration's location. Authentication details required by the - remote server must be provided as well. - By default, this method overwrites the switch's configuration file with - the newly downloaded file. This module uses SSH to manage network device - configuration. The results of the operation will be placed in a directory - named 'results' that must be created by the user in their local directory - to where the playbook is run. -extends_documentation_fragment: -- community.general.cnos - -options: - configType: - description: - - This refers to the type of configuration which will be used for - the rolling back process. The choices are the running or startup - configurations. There is no default value, so it will result - in an error if the input is incorrect. - required: Yes - default: Null - choices: [running-config, startup-config] - protocol: - description: - - This refers to the protocol used by the network device to - interact with the remote server from where to download the backup - configuration. The choices are FTP, SFTP, TFTP, or SCP. Any other - protocols will result in error. If this parameter is not - specified, there is no default value to be used. - required: Yes - default: Null - choices: [SFTP, SCP, FTP, TFTP] - rcserverip: - description: - - This specifies the IP Address of the remote server from where the - backup configuration will be downloaded. - required: Yes - default: Null - rcpath: - description: - - This specifies the full file path of the configuration file - located on the remote server. In case the relative path is used as - the variable value, the root folder for the user of the server - needs to be specified. - required: Yes - default: Null - serverusername: - description: - - Specify username for the server relating to the protocol used. - required: Yes - default: Null - serverpassword: - description: - - Specify password for the server relating to the protocol used. - required: Yes - default: Null -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module cnos_rollback. - These are written in the main.yml file of the tasks directory. ---- - -- name: Test Rollback of config - Running config - cnos_rolback: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_rollback_{{ inventory_hostname }}_output.txt" - configType: running-config - protocol: "sftp" - serverip: "10.241.106.118" - rcpath: "/root/cnos/G8272-running-config.txt" - serverusername: "root" - serverpassword: "root123" - -- name: Test Rollback of config - Startup config - cnos_rolback: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_rollback_{{ inventory_hostname }}_output.txt" - configType: startup-config - protocol: "sftp" - serverip: "10.241.106.118" - rcpath: "/root/cnos/G8272-startup-config.txt" - serverusername: "root" - serverpassword: "root123" - -- name: Test Rollback of config - Running config - TFTP - cnos_rolback: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_rollback_{{ inventory_hostname }}_output.txt" - configType: running-config - protocol: "tftp" - serverip: "10.241.106.118" - rcpath: "/anil/G8272-running-config.txt" - serverusername: "root" - serverpassword: "root123" - -- name: Test Rollback of config - Startup config - TFTP - cnos_rolback: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_rollback_{{ inventory_hostname }}_output.txt" - configType: startup-config - protocol: "tftp" - serverip: "10.241.106.118" - rcpath: "/anil/G8272-startup-config.txt" - serverusername: "root" - serverpassword: "root123" - -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Config file transferred to Device" -''' - -import sys -import time -import socket -import array -import json -import time -import re -import os -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -# Utility Method to rollback the running config or start up config -# This method supports only SCP or SFTP or FTP or TFTP -def doConfigRollBack(module, prompt, answer): - host = module.params['host'] - server = module.params['serverip'] - username = module.params['serverusername'] - password = module.params['serverpassword'] - protocol = module.params['protocol'].lower() - rcPath = module.params['rcpath'] - configType = module.params['configType'] - confPath = rcPath - retVal = '' - - command = "copy " + protocol + " " + protocol + "://" - command = command + username + "@" + server + "/" + confPath - command = command + " " + configType + " vrf management\n" - cnos.debugOutput(command + "\n") - # cnos.checkForFirstTimeAccess(module, command, 'yes/no', 'yes') - cmd = [] - if(protocol == "scp"): - scp_cmd1 = [{'command': command, 'prompt': 'timeout:', 'answer': '0'}] - scp_cmd2 = [{'command': '\n', 'prompt': 'Password:', - 'answer': password}] - cmd.extend(scp_cmd1) - cmd.extend(scp_cmd2) - if(configType == 'startup-config'): - scp_cmd3 = [{'command': 'y', 'prompt': None, 'answer': None}] - cmd.extend(scp_cmd3) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "sftp"): - sftp_cmd = [{'command': command, 'prompt': 'Password:', - 'answer': password}] - cmd.extend(sftp_cmd) - # cnos.debugOutput(configType + "\n") - if(configType == 'startup-config'): - sftp_cmd2 = [{'command': 'y', 'prompt': None, 'answer': None}] - cmd.extend(sftp_cmd2) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "ftp"): - ftp_cmd = [{'command': command, 'prompt': 'Password:', - 'answer': password}] - cmd.extend(ftp_cmd) - if(configType == 'startup-config'): - ftp_cmd2 = [{'command': 'y', 'prompt': None, 'answer': None}] - cmd.extend(ftp_cmd2) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - elif(protocol == "tftp"): - command = "copy " + protocol + " " + protocol - command = command + "://" + server + "/" + confPath - command = command + " " + configType + " vrf management\n" - cnos.debugOutput(command) - tftp_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(tftp_cmd) - if(configType == 'startup-config'): - tftp_cmd2 = [{'command': 'y', 'prompt': None, 'answer': None}] - cmd.extend(tftp_cmd2) - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - else: - return "Error-110" - - return retVal -# EOM - - -def main(): - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True), - deviceType=dict(required=True), - configType=dict(required=True), - protocol=dict(required=True), - serverip=dict(required=True), - rcpath=dict(required=True), - serverusername=dict(required=False), - serverpassword=dict(required=False, no_log=True),), - supports_check_mode=False) - - outputfile = module.params['outputfile'] - protocol = module.params['protocol'].lower() - output = '' - if protocol in ('tftp', 'ftp', 'sftp', 'scp'): - transfer_status = doConfigRollBack(module, None, None) - else: - transfer_status = 'Invalid Protocol option' - output = output + "\n Config Transfer status \n" + transfer_status - - # Save it into the file - if '/' in outputfile: - path = outputfile.rsplit('/', 1) - # cnos.debugOutput(path[0]) - if not os.path.exists(path[0]): - os.makedirs(path[0]) - file = open(outputfile, "a") - file.write(output) - file.close() - - # need to add logic to check when changes occur or not - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, msg="Config file transferred to Device") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_save.py b/plugins/modules/network/cnos/cnos_save.py deleted file mode 100644 index 2d03be1fb8..0000000000 --- a/plugins/modules/network/cnos/cnos_save.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to save running config to start up config to Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_save -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Save the running configuration as the startup configuration - on devices running Lenovo CNOS -description: - - This module allows you to copy the running configuration of a switch over - its startup configuration. It is recommended to use this module shortly - after any major configuration changes so they persist after a switch - restart. This module uses SSH to manage network device configuration. - The results of the operation will be placed in a directory named 'results' - that must be created by the user in their local directory to where the - playbook is run. -extends_documentation_fragment: -- community.general.cnos - -options: {} - -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module cnos_save. These are - written in the main.yml file of the tasks directory. ---- -- name: Test Save - cnos_save: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_save_{{ inventory_hostname }}_output.txt" -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Switch Running Config is Saved to Startup Config" -''' - -import sys -import time -import socket -import array -import json -import time -import re -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def main(): - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True), - deviceType=dict(required=True),), - supports_check_mode=False) - - command = 'write memory' - outputfile = module.params['outputfile'] - output = '' - cmd = [{'command': command, 'prompt': None, 'answer': None}] - output = output + str(cnos.run_cnos_commands(module, cmd)) - - # Save it into the file - file = open(outputfile, "a") - file.write(output) - file.close() - - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, - msg="Switch Running Config is Saved to Startup Config ") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_showrun.py b/plugins/modules/network/cnos/cnos_showrun.py deleted file mode 100644 index 3f161f5028..0000000000 --- a/plugins/modules/network/cnos/cnos_showrun.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to display running config of Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_showrun -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Collect the current running configuration on devices running on CNOS -description: - - This module allows you to view the switch running configuration. It - executes the display running-config CLI command on a switch and returns a - file containing the current running configuration of the target network - device. This module uses SSH to manage network device configuration. - The results of the operation will be placed in a directory named 'results' - that must be created by the user in their local directory to where the - playbook is run. -extends_documentation_fragment: -- community.general.cnos - -options: {} - -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module cnos_showrun. These are - written in the main.yml file of the tasks directory. ---- -- name: Run show running-config - cnos_showrun: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - outputfile: "./results/test_showrun_{{ inventory_hostname }}_output.txt" - -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Running Configuration saved in file" -''' - -import sys -import time -import socket -import array -import json -import time -import re -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def main(): - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True),), - supports_check_mode=False) - - command = 'show running-config' - outputfile = module.params['outputfile'] - output = '' - cmd = [{'command': command, 'prompt': None, 'answer': None}] - output = output + str(cnos.run_cnos_commands(module, cmd)) - # Save it into the file - file = open(outputfile, "a") - file.write(output) - file.close() - - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, - msg="Running Configuration saved in file ") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_static_route.py b/plugins/modules/network/cnos/cnos_static_route.py deleted file mode 100644 index 0195831c5b..0000000000 --- a/plugins/modules/network/cnos/cnos_static_route.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2019 Lenovo, Inc. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to work on Link Aggregation with Lenovo Switches -# Lenovo Networking -# -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_static_route -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage static IP routes on Lenovo CNOS network devices -description: - - This module provides declarative management of static - IP routes on Lenovo CNOS network devices. -notes: - - Tested against CNOS 10.10.1 -options: - prefix: - description: - - Network prefix of the static route. - mask: - description: - - Network prefix mask of the static route. - next_hop: - description: - - Next hop IP of the static route. - interface: - description: - - Interface of the static route. - description: - description: - - Name of the static route - aliases: ['description'] - admin_distance: - description: - - Admin distance of the static route. - default: 1 - tag: - description: - - Set tag of the static route. - aggregate: - description: List of static route definitions. - state: - description: - - State of the static route configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: configure static route - cnos_static_route: - prefix: 10.241.107.0 - mask: 255.255.255.0 - next_hop: 10.241.106.1 - -- name: configure ultimate route with name and tag - cnos_static_route: - prefix: 10.241.107.0 - mask: 255.255.255.0 - interface: Ethernet1/13 - description: hello world - tag: 100 - -- name: remove configuration - cnos_static_route: - prefix: 10.241.107.0 - mask: 255.255.255.0 - next_hop: 10.241.106.0 - state: absent - -- name: Add static route aggregates - cnos_static_route: - aggregate: - - { prefix: 10.241.107.0, mask: 255.255.255.0, next_hop: 10.241.105.0 } - - { prefix: 10.241.106.0, mask: 255.255.255.0, next_hop: 10.241.104.0 } - -- name: Remove static route aggregates - cnos_static_route: - aggregate: - - { prefix: 10.241.107.0, mask: 255.255.255.0, next_hop: 10.241.105.0 } - - { prefix: 10.241.106.0, mask: 255.255.255.0, next_hop: 10.241.104.0 } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - ip route 10.241.107.0 255.255.255.0 10.241.106.0 -""" -from copy import deepcopy -from re import findall -from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ipaddress -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import validate_ip_address -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import check_args -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec - - -def map_obj_to_commands(want, have): - commands = list() - - for w in want: - state = w['state'] - command = 'ip route' - prefix = w['prefix'] - mask = w['mask'] - command = ' '.join((command, prefix, mask)) - - for key in ['interface', 'next_hop', 'admin_distance', 'tag', - 'description']: - if w.get(key): - if key == 'description' and len(w.get(key).split()) > 1: - # name with multiple words needs to be quoted - command = ' '.join((command, key, '"%s"' % w.get(key))) - elif key in ('description', 'tag'): - command = ' '.join((command, key, w.get(key))) - else: - command = ' '.join((command, w.get(key))) - - if state == 'absent': - commands.append('no %s' % command) - elif state == 'present': - commands.append(command) - - return commands - - -def map_config_to_obj(module): - obj = [] - - out = get_config(module, flags='| include ip route') - for line in out.splitlines(): - # Split by whitespace but do not split quotes, needed for description - splitted_line = findall(r'[^"\s]\S*|".+?"', line) - route = {} - prefix_with_mask = splitted_line[2] - prefix = None - mask = None - iface = None - nhop = None - if validate_ip_address(prefix_with_mask) is True: - my_net = ipaddress.ip_network(prefix_with_mask) - prefix = str(my_net.network_address) - mask = str(my_net.netmask) - route.update({'prefix': prefix, - 'mask': mask, 'admin_distance': '1'}) - if splitted_line[3] is not None: - if validate_ip_address(splitted_line[3]) is False: - iface = str(splitted_line[3]) - route.update(interface=iface) - if validate_ip_address(splitted_line[4]) is True: - nhop = str(splitted_line[4]) - route.update(next_hop=nhop) - if splitted_line[5].isdigit(): - route.update(admin_distance=str(splitted_line[5])) - elif splitted_line[4].isdigit(): - route.update(admin_distance=str(splitted_line[4])) - else: - if splitted_line[6] is not None and splitted_line[6].isdigit(): - route.update(admin_distance=str(splitted_line[6])) - else: - nhop = str(splitted_line[3]) - route.update(next_hop=nhop) - if splitted_line[4].isdigit(): - route.update(admin_distance=str(splitted_line[4])) - - index = 0 - for word in splitted_line: - if word in ('tag', 'description'): - route.update(word=splitted_line[index + 1]) - index = index + 1 - obj.append(route) - - return obj - - -def map_params_to_obj(module, required_together=None): - keys = ['prefix', 'mask', 'state', 'next_hop', 'interface', 'description', - 'admin_distance', 'tag'] - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - route = item.copy() - for key in keys: - if route.get(key) is None: - route[key] = module.params.get(key) - - route = dict((k, v) for k, v in route.items() if v is not None) - module._check_required_together(required_together, route) - obj.append(route) - else: - module._check_required_together(required_together, module.params) - route = dict() - for key in keys: - if module.params.get(key) is not None: - route[key] = module.params.get(key) - obj.append(route) - - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - prefix=dict(type='str'), - mask=dict(type='str'), - next_hop=dict(type='str'), - interface=dict(type='str'), - description=dict(type='str'), - admin_distance=dict(type='str', default='1'), - tag=dict(type='str'), - state=dict(default='present', choices=['present', 'absent']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['prefix'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - argument_spec.update(element_spec) - - required_one_of = [['aggregate', 'prefix']] - required_together = [['prefix', 'mask']] - mutually_exclusive = [['aggregate', 'prefix']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - check_args(module, warnings) - - result = {'changed': False} - if warnings: - result['warnings'] = warnings - want = map_params_to_obj(module, required_together=required_together) - have = map_config_to_obj(module) - - commands = map_obj_to_commands(want, have) - result['commands'] = commands - if commands: - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_system.py b/plugins/modules/network/cnos/cnos_system.py deleted file mode 100644 index 6c72c44a43..0000000000 --- a/plugins/modules/network/cnos/cnos_system.py +++ /dev/null @@ -1,387 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2019 Lenovo. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to work on System Configuration with Lenovo Switches -# Lenovo Networking -# -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_system -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage the system attributes on Lenovo CNOS devices -description: - - This module provides declarative management of node system attributes - on Lenovo CNOS devices. It provides an option to configure host system - parameters or remove those parameters from the device active - configuration. -options: - hostname: - description: - - Configure the device hostname parameter. This option takes an - ASCII string value or keyword 'default' - domain_name: - description: - - Configures the default domain - name suffix to be used when referencing this node by its - FQDN. This argument accepts either a list of domain names or - a list of dicts that configure the domain name and VRF name or - keyword 'default'. See examples. - lookup_enabled: - description: - - Administrative control for enabling or disabling DNS lookups. - When this argument is set to True, lookups are performed and - when it is set to False, lookups are not performed. - type: bool - domain_search: - description: - - Configures a list of domain - name suffixes to search when performing DNS name resolution. - This argument accepts either a list of domain names or - a list of dicts that configure the domain name and VRF name or - keyword 'default'. See examples. - name_servers: - description: - - List of DNS name servers by IP address to use to perform name resolution - lookups. This argument accepts either a list of DNS servers or - a list of hashes that configure the name server and VRF name or - keyword 'default'. See examples. - lookup_source: - description: - - Provides one or more source interfaces to use for performing DNS - lookups. The interface must be a valid interface configured. - on the device. - state: - description: - - State of the configuration - values in the device's current active configuration. When set - to I(present), the values should be configured in the device active - configuration and when set to I(absent) the values should not be - in the device active configuration - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: configure hostname and domain-name - cnos_system: - hostname: cnos01 - domain_name: test.example.com - -- name: remove configuration - cnos_system: - state: absent - -- name: configure name servers - cnos_system: - name_servers: - - 8.8.8.8 - - 8.8.4.4 - -- name: configure DNS Lookup sources - cnos_system: - lookup_source: MgmtEth0/0/CPU0/0 - lookup_enabled: yes - -- name: configure name servers with VRF support - nxos_system: - name_servers: - - { server: 8.8.8.8, vrf: mgmt } - - { server: 8.8.4.4, vrf: mgmt } -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - hostname cnos01 - - ip domain-name test.example.com vrf default -""" -import re - -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import check_args, debugOutput -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList - -_CONFIGURED_VRFS = None - - -def map_obj_to_commands(want, have, module): - commands = list() - state = module.params['state'] - - def needs_update(x): - return want.get(x) and (want.get(x) != have.get(x)) - - def difference(x, y, z): - return [item for item in x[z] if item not in y[z]] - - if state == 'absent': - if have['hostname']: - commands.append('no hostname') - - for item in have['domain_name']: - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip domain-name {0} vrf {1}'.format(item['name'], my_vrf) - commands.append(cmd) - - for item in have['domain_search']: - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip domain-list {0} vrf {1}'.format(item['name'], my_vrf) - commands.append(cmd) - - for item in have['name_servers']: - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip name-server {0} vrf {1}'.format(item['server'], my_vrf) - commands.append(cmd) - - if state == 'present': - if needs_update('hostname'): - if want['hostname'] == 'default': - if have['hostname']: - commands.append('no hostname') - else: - commands.append('hostname %s' % want['hostname']) - - if want.get('lookup_enabled') is not None: - if have.get('lookup_enabled') != want.get('lookup_enabled'): - cmd = 'ip domain-lookup' - if want['lookup_enabled'] is False: - cmd = 'no %s' % cmd - commands.append(cmd) - - if want['domain_name']: - if want.get('domain_name')[0]['name'] == 'default': - if have['domain_name']: - for item in have['domain_name']: - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip domain-name {0} vrf {1}'.format(item['name'], my_vrf) - commands.append(cmd) - else: - for item in difference(have, want, 'domain_name'): - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip domain-name {0} vrf {1}'.format(item['name'], my_vrf) - commands.append(cmd) - for item in difference(want, have, 'domain_name'): - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'ip domain-name {0} vrf {1}'.format(item['name'], my_vrf) - commands.append(cmd) - - if want['domain_search']: - if want.get('domain_search')[0]['name'] == 'default': - if have['domain_search']: - for item in have['domain_search']: - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip domain-list {0} vrf {1}'.format(item['name'], my_vrf) - commands.append(cmd) - else: - for item in difference(have, want, 'domain_search'): - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip domain-list {0} vrf {1}'.format(item['name'], my_vrf) - commands.append(cmd) - for item in difference(want, have, 'domain_search'): - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'ip domain-list {0} vrf {1}'.format(item['name'], my_vrf) - commands.append(cmd) - - if want['name_servers']: - if want.get('name_servers')[0]['server'] == 'default': - if have['name_servers']: - for item in have['name_servers']: - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip name-server {0} vrf {1}'.format(item['server'], my_vrf) - commands.append(cmd) - else: - for item in difference(have, want, 'name_servers'): - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'no ip name-server {0} vrf {1}'.format(item['server'], my_vrf) - commands.append(cmd) - for item in difference(want, have, 'name_servers'): - my_vrf = 'default' - if item['vrf'] is not None: - my_vrf = item['vrf'] - cmd = 'ip name-server {0} vrf {1}'.format(item['server'], my_vrf) - commands.append(cmd) - - return commands - - -def parse_hostname(config): - match = re.search(r'^hostname (\S+)', config, re.M) - if match: - return match.group(1) - - -def parse_domain_name(config): - objects = list() - myconf = config.splitlines() - for line in myconf: - if 'ip domain-name' in line: - datas = line.split() - objects.append({'name': datas[2], 'vrf': datas[4]}) - - return objects - - -def parse_domain_search(config): - objects = list() - myconf = config.splitlines() - for line in myconf: - if 'ip domain-list' in line: - datas = line.split() - objects.append({'name': datas[2], 'vrf': datas[4]}) - - return objects - - -def parse_name_servers(config): - objects = list() - myconf = config.splitlines() - for line in myconf: - if 'ip name-server' in line: - datas = line.split() - objects.append({'server': datas[2], 'vrf': datas[4]}) - - return objects - - -def map_config_to_obj(module): - config = get_config(module) - configobj = NetworkConfig(indent=2, contents=config) - - return { - 'hostname': parse_hostname(config), - 'lookup_enabled': 'no ip domain-lookup' not in config, - 'domain_name': parse_domain_name(config), - 'domain_search': parse_domain_search(config), - 'name_servers': parse_name_servers(config), - } - - -def map_params_to_obj(module): - obj = { - 'hostname': module.params['hostname'], - 'lookup_enabled': module.params['lookup_enabled'], - } - - domain_name = ComplexList(dict( - name=dict(key=True), - vrf=dict() - ), module) - - domain_search = ComplexList(dict( - name=dict(key=True), - vrf=dict() - ), module) - - name_servers = ComplexList(dict( - server=dict(key=True), - vrf=dict() - ), module) - - for arg, cast in [('domain_name', domain_name), - ('domain_search', domain_search), - ('name_servers', name_servers)]: - if module.params[arg] is not None: - obj[arg] = cast(module.params[arg]) - else: - obj[arg] = None - - return obj - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - hostname=dict(), - lookup_enabled=dict(type='bool'), - - # { name: , vrf: } - domain_name=dict(type='list'), - - # {name: , vrf: } - domain_search=dict(type='list'), - - # { server: ; vrf: } - name_servers=dict(type='list'), - - lookup_source=dict(type='str'), - state=dict(default='present', choices=['present', 'absent']) - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - warnings = list() - check_args(module, warnings) - - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - commands = map_obj_to_commands(want, have, module) - result['commands'] = commands - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_template.py b/plugins/modules/network/cnos/cnos_template.py deleted file mode 100644 index 1e1bc2dd61..0000000000 --- a/plugins/modules/network/cnos/cnos_template.py +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to send CLI templates to Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_template -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage switch configuration using templates on devices running Lenovo CNOS -description: - - This module allows you to work with the running configuration of a switch. It provides a way - to execute a set of CNOS commands on a switch by evaluating the current running configuration - and executing the commands only if the specific settings have not been already configured. - The configuration source can be a set of commands or a template written in the Jinja2 templating language. - This module uses SSH to manage network device configuration. - The results of the operation will be placed in a directory named 'results' - that must be created by the user in their local directory to where the playbook is run. -extends_documentation_fragment: -- community.general.cnos - -options: - commandfile: - description: - - This specifies the path to the CNOS command file which needs to be applied. This usually - comes from the commands folder. Generally this file is the output of the variables applied - on a template file. So this command is preceded by a template module. - Note The command file must contain the Ansible keyword {{ inventory_hostname }} in its - filename to ensure that the command file is unique for each switch and condition. - If this is omitted, the command file will be overwritten during iteration. For example, - commandfile=./commands/clos_leaf_bgp_{{ inventory_hostname }}_commands.txt - required: true - default: Null -''' -EXAMPLES = ''' -Tasks : The following are examples of using the module cnos_template. These are written in the main.yml file of the tasks directory. ---- -- name: Replace Config CLI command template with values - template: - src: demo_template.j2 - dest: "./commands/demo_template_{{ inventory_hostname }}_commands.txt" - vlanid1: 13 - slot_chassis_number1: "1/2" - portchannel_interface_number1: 100 - portchannel_mode1: "active" - -- name: Applying CLI commands on Switches - cnos_template: - deviceType: "{{ hostvars[inventory_hostname]['deviceType'] }}" - commandfile: "./commands/demo_template_{{ inventory_hostname }}_commands.txt" - outputfile: "./results/demo_template_command_{{ inventory_hostname }}_output.txt" - -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "Template Applied." -''' - -import sys -import time -import socket -import array -import json -import time -import re -import os -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def main(): - module = AnsibleModule( - argument_spec=dict( - commandfile=dict(required=True), - outputfile=dict(required=True), - host=dict(required=False), - deviceType=dict(required=True), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True),), - supports_check_mode=False) - commandfile = module.params['commandfile'] - outputfile = module.params['outputfile'] - output = '' - - # Send commands one by one to the device - f = open(commandfile, "r") - cmd = [] - for line in f: - # Omit the comment lines in template file - if not line.startswith("#"): - command = line.strip() - inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] - cmd.extend(inner_cmd) - # Write to memory - save_cmd = [{'command': 'save', 'prompt': None, 'answer': None}] - cmd.extend(save_cmd) - output = output + str(cnos.run_cnos_commands(module, cmd)) - # Write output to file - path = outputfile.rsplit('/', 1) - # cnos.debugOutput(path[0]) - if not os.path.exists(path[0]): - os.makedirs(path[0]) - file = open(outputfile, "a") - file.write(output) - file.close() - - # Logic to check when changes occur or not - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, msg="Template Applied") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_user.py b/plugins/modules/network/cnos/cnos_user.py deleted file mode 100644 index 98a4552c83..0000000000 --- a/plugins/modules/network/cnos/cnos_user.py +++ /dev/null @@ -1,390 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2019 Lenovo. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to work on management of local users on Lenovo CNOS Switches -# Lenovo Networking -# -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_user -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage the collection of local users on Lenovo CNOS devices -description: - - This module provides declarative management of the local usernames - configured on Lenovo CNOS devices. It allows playbooks to manage - either individual usernames or the collection of usernames in the - current running config. It also supports purging usernames from the - configuration that are not explicitly defined. -options: - aggregate: - description: - - The set of username objects to be configured on the remote - Lenovo CNOS device. The list entries can either be the username - or a hash of username and properties. This argument is mutually - exclusive with the C(name) argument. - aliases: ['users', 'collection'] - name: - description: - - The username to be configured on the remote Lenovo CNOS - device. This argument accepts a string value and is mutually - exclusive with the C(aggregate) argument. - configured_password: - description: - - The password to be configured on the network device. The - password needs to be provided in cleartext and it will be encrypted - on the device. - Please note that this option is not same as C(provider password). - update_password: - description: - - Since passwords are encrypted in the device running config, this - argument will instruct the module when to change the password. When - set to C(always), the password will always be updated in the device - and when set to C(on_create) the password will be updated only if - the username is created. - default: always - choices: ['on_create', 'always'] - role: - description: - - The C(role) argument configures the role for the username in the - device running configuration. The argument accepts a string value - defining the role name. This argument does not check if the role - has been configured on the device. - aliases: ['roles'] - sshkey: - description: - - The C(sshkey) argument defines the SSH public key to configure - for the username. This argument accepts a valid SSH key value. - purge: - description: - - The C(purge) argument instructs the module to consider the - resource definition absolute. It will remove any previously - configured usernames on the device with the exception of the - `admin` user which cannot be deleted per cnos constraints. - type: bool - default: 'no' - state: - description: - - The C(state) argument configures the state of the username definition - as it relates to the device operational configuration. When set - to I(present), the username(s) should be configured in the device active - configuration and when set to I(absent) the username(s) should not be - in the device active configuration - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: create a new user - cnos_user: - name: ansible - sshkey: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" - state: present - -- name: remove all users except admin - cnos_user: - purge: yes - -- name: set multiple users role - aggregate: - - name: netop - - name: netend - role: network-operator - state: present -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - name ansible - - name ansible password password -start: - description: The time the job started - returned: always - type: str - sample: "2016-11-16 10:38:15.126146" -end: - description: The time the job ended - returned: always - type: str - sample: "2016-11-16 10:38:25.595612" -delta: - description: The time elapsed to perform all operations - returned: always - type: str - sample: "0:00:10.469466" -""" -import re - -from copy import deepcopy -from functools import partial - -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import run_commands, load_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_config -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import string_types, iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import get_user_roles - - -def validate_roles(value, module): - for item in value: - if item not in get_user_roles(): - module.fail_json(msg='invalid role specified') - - -def map_obj_to_commands(updates, module): - commands = list() - state = module.params['state'] - update_password = module.params['update_password'] - - for update in updates: - want, have = update - - def needs_update(x): - return want.get(x) and (want.get(x) != have.get(x)) - - def add(x): - return commands.append('username %s %s' % (want['name'], x)) - - def remove(x): - return commands.append('no username %s %s' % (want['name'], x)) - - if want['state'] == 'absent': - commands.append('no username %s' % want['name']) - continue - - if want['state'] == 'present' and not have: - commands.append('username %s' % want['name']) - - if needs_update('configured_password'): - if update_password == 'always' or not have: - add('password %s' % want['configured_password']) - - if needs_update('sshkey'): - add('sshkey %s' % want['sshkey']) - - if want['roles']: - if have: - for item in set(have['roles']).difference(want['roles']): - remove('role %s' % item) - - for item in set(want['roles']).difference(have['roles']): - add('role %s' % item) - else: - for item in want['roles']: - add('role %s' % item) - - return commands - - -def parse_password(data): - if 'no password set' in data: - return None - return '' - - -def parse_roles(data): - roles = list() - if 'role:' in data: - items = data.split() - my_item = items[items.index('role:') + 1] - roles.append(my_item) - return roles - - -def parse_username(data): - name = data.split(' ', 1)[0] - username = name[1:] - return username - - -def parse_sshkey(data): - key = None - if 'sskkey:' in data: - items = data.split() - key = items[items.index('sshkey:') + 1] - return key - - -def map_config_to_obj(module): - out = run_commands(module, ['show user-account']) - data = out[0] - objects = list() - datum = data.split('User') - - for item in datum: - objects.append({ - 'name': parse_username(item), - 'configured_password': parse_password(item), - 'sshkey': parse_sshkey(item), - 'roles': parse_roles(item), - 'state': 'present' - }) - return objects - - -def get_param_value(key, item, module): - # if key doesn't exist in the item, get it from module.params - if not item.get(key): - value = module.params[key] - - # if key does exist, do a type check on it to validate it - else: - value_type = module.argument_spec[key].get('type', 'str') - type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type] - type_checker(item[key]) - value = item[key] - - return value - - -def map_params_to_obj(module): - aggregate = module.params['aggregate'] - if not aggregate: - if not module.params['name'] and module.params['purge']: - return list() - elif not module.params['name']: - module.fail_json(msg='username is required') - else: - collection = [{'name': module.params['name']}] - else: - collection = list() - for item in aggregate: - if not isinstance(item, dict): - collection.append({'name': item}) - elif 'name' not in item: - module.fail_json(msg='name is required') - else: - collection.append(item) - - objects = list() - - for item in collection: - get_value = partial(get_param_value, item=item, module=module) - item.update({ - 'configured_password': get_value('configured_password'), - 'sshkey': get_value('sshkey'), - 'roles': get_value('roles'), - 'state': get_value('state') - }) - - for key, value in iteritems(item): - if value: - # validate the param value (if validator func exists) - validator = globals().get('validate_%s' % key) - if all((value, validator)): - validator(value, module) - - objects.append(item) - - return objects - - -def update_objects(want, have): - updates = list() - for entry in want: - item = next((i for i in have if i['name'] == entry['name']), None) - if all((item is None, entry['state'] == 'present')): - updates.append((entry, {})) - elif item: - for key, value in iteritems(entry): - if value and value != item[key]: - updates.append((entry, item)) - return updates - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - name=dict(), - configured_password=dict(no_log=True), - update_password=dict(default='always', choices=['on_create', 'always']), - roles=dict(type='list', aliases=['role']), - sshkey=dict(), - state=dict(default='present', choices=['present', 'absent']) - ) - - aggregate_spec = deepcopy(element_spec) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', - options=aggregate_spec, aliases=['collection', 'users']), - purge=dict(type='bool', default=False) - ) - - argument_spec.update(element_spec) - - mutually_exclusive = [('name', 'aggregate')] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - - result = {'changed': False} - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - commands = map_obj_to_commands(update_objects(want, have), module) - - if module.params['purge']: - want_users = [x['name'] for x in want] - have_users = [x['name'] for x in have] - for item in set(have_users).difference(want_users): - if item != 'admin': - if not item.strip(): - continue - item = item.replace("\\", "\\\\") - commands.append('no username %s' % item) - - result['commands'] = commands - - # the cnos cli prevents this by rule but still - if 'no username admin' in commands: - module.fail_json(msg='Cannot delete the `admin` account') - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_vlag.py b/plugins/modules/network/cnos/cnos_vlag.py deleted file mode 100644 index 8568f7fb42..0000000000 --- a/plugins/modules/network/cnos/cnos_vlag.py +++ /dev/null @@ -1,446 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2017 Lenovo, 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 . -# -# Module to send VLAG commands to Lenovo Switches -# Lenovo Networking -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: cnos_vlag -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage VLAG resources and attributes on devices running - Lenovo CNOS -description: - - This module allows you to work with virtual Link Aggregation Groups - (vLAG) related configurations. The operators used are overloaded to ensure - control over switch vLAG configurations. Apart from the regular device - connection related attributes, there are four vLAG arguments which are - overloaded variables that will perform further configurations. They are - vlagArg1, vlagArg2, vlagArg3, and vlagArg4. For more details on how to use - these arguments, see [Overloaded Variables]. - This module uses SSH to manage network device configuration. - The results of the operation will be placed in a directory named 'results' - that must be created by the user in their local directory to where the - playbook is run. -extends_documentation_fragment: -- community.general.cnos - -options: - vlagArg1: - description: - - This is an overloaded vlag first argument. Usage of this argument can - be found is the User Guide referenced above. - required: Yes - default: Null - choices: [enable, auto-recovery,config-consistency,isl,mac-address-table, - peer-gateway,priority,startup-delay,tier-id,vrrp,instance,hlthchk] - vlagArg2: - description: - - This is an overloaded vlag second argument. Usage of this argument can - be found is the User Guide referenced above. - required: No - default: Null - choices: [Interval in seconds,disable or strict,Port Aggregation Number, - VLAG priority,Delay time in seconds,VLAG tier-id value, - VLAG instance number,keepalive-attempts,keepalive-interval, - retry-interval,peer-ip] - vlagArg3: - description: - - This is an overloaded vlag third argument. Usage of this argument can - be found is the User Guide referenced above. - required: No - default: Null - choices: [enable or port-aggregation,Number of keepalive attempts, - Interval in seconds,Interval in seconds, - VLAG health check peer IP4 address] - vlagArg4: - description: - - This is an overloaded vlag fourth argument. Usage of this argument can - be found is the User Guide referenced above. - required: No - default: Null - choices: [Port Aggregation Number,default or management] - -''' -EXAMPLES = ''' - -Tasks : The following are examples of using the module cnos_vlag. These are - written in the main.yml file of the tasks directory. ---- -- name: Test Vlag - enable - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "enable" - -- name: Test Vlag - autorecovery - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "auto-recovery" - vlagArg2: 266 - -- name: Test Vlag - config-consistency - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "config-consistency" - vlagArg2: "strict" - -- name: Test Vlag - isl - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "isl" - vlagArg2: 23 - -- name: Test Vlag - mac-address-table - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "mac-address-table" - -- name: Test Vlag - peer-gateway - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "peer-gateway" - -- name: Test Vlag - priority - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "priority" - vlagArg2: 1313 - -- name: Test Vlag - startup-delay - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "startup-delay" - vlagArg2: 323 - -- name: Test Vlag - tier-id - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "tier-id" - vlagArg2: 313 - -- name: Test Vlag - vrrp - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "vrrp" - -- name: Test Vlag - instance - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "instance" - vlagArg2: 33 - vlagArg3: 333 - -- name: Test Vlag - instance2 - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "instance" - vlagArg2: "33" - -- name: Test Vlag - keepalive-attempts - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "hlthchk" - vlagArg2: "keepalive-attempts" - vlagArg3: 13 - -- name: Test Vlag - keepalive-interval - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "hlthchk" - vlagArg2: "keepalive-interval" - vlagArg3: 131 - -- name: Test Vlag - retry-interval - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "hlthchk" - vlagArg2: "retry-interval" - vlagArg3: 133 - -- name: Test Vlag - peer ip - cnos_vlag: - deviceType: "{{ hostvars[inventory_hostname]['deviceType']}}" - outputfile: "./results/cnos_vlag_{{ inventory_hostname }}_output.txt" - vlagArg1: "hlthchk" - vlagArg2: "peer-ip" - vlagArg3: "1.2.3.4" - -''' -RETURN = ''' -msg: - description: Success or failure message - returned: always - type: str - sample: "vLAG configurations accomplished" -''' - -import sys -import time -import socket -import array -import json -import time -import re -try: - from ansible_collections.community.general.plugins.module_utils.network.cnos import cnos - HAS_LIB = True -except Exception: - HAS_LIB = False - -from ansible.module_utils.basic import AnsibleModule -from collections import defaultdict - - -def vlagConfig(module, prompt, answer): - - retVal = '' - # vlag config command happens here. - command = 'vlag ' - - vlagArg1 = module.params['vlagArg1'] - vlagArg2 = module.params['vlagArg2'] - vlagArg3 = module.params['vlagArg3'] - vlagArg4 = module.params['vlagArg4'] - deviceType = module.params['deviceType'] - - if(vlagArg1 == "enable"): - # debugOutput("enable") - command = command + vlagArg1 + " " - - elif(vlagArg1 == "auto-recovery"): - # debugOutput("auto-recovery") - command = command + vlagArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "vlag_auto_recovery", vlagArg2) - if(value == "ok"): - command = command + vlagArg2 - else: - retVal = "Error-160" - return retVal - - elif(vlagArg1 == "config-consistency"): - # debugOutput("config-consistency") - command = command + vlagArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "vlag_config_consistency", vlagArg2) - if(value == "ok"): - command = command + vlagArg2 - else: - retVal = "Error-161" - return retVal - - elif(vlagArg1 == "isl"): - # debugOutput("isl") - command = command + vlagArg1 + " port-channel " - value = cnos.checkSanityofVariable( - deviceType, "vlag_port_aggregation", vlagArg2) - if(value == "ok"): - command = command + vlagArg2 - else: - retVal = "Error-162" - return retVal - - elif(vlagArg1 == "mac-address-table"): - # debugOutput("mac-address-table") - command = command + vlagArg1 + " refresh" - - elif(vlagArg1 == "peer-gateway"): - # debugOutput("peer-gateway") - command = command + vlagArg1 + " " - - elif(vlagArg1 == "priority"): - # debugOutput("priority") - command = command + vlagArg1 + " " - value = cnos.checkSanityofVariable(deviceType, "vlag_priority", - vlagArg2) - if(value == "ok"): - command = command + vlagArg2 - else: - retVal = "Error-163" - return retVal - - elif(vlagArg1 == "startup-delay"): - # debugOutput("startup-delay") - command = command + vlagArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "vlag_startup_delay", vlagArg2) - if(value == "ok"): - command = command + vlagArg2 - else: - retVal = "Error-164" - return retVal - - elif(vlagArg1 == "tier-id"): - # debugOutput("tier-id") - command = command + vlagArg1 + " " - value = cnos.checkSanityofVariable(deviceType, "vlag_tier_id", vlagArg2) - if(value == "ok"): - command = command + vlagArg2 - else: - retVal = "Error-165" - return retVal - - elif(vlagArg1 == "vrrp"): - # debugOutput("vrrp") - command = command + vlagArg1 + " active" - - elif(vlagArg1 == "instance"): - # debugOutput("instance") - command = command + vlagArg1 + " " - value = cnos.checkSanityofVariable(deviceType, "vlag_instance", - vlagArg2) - if(value == "ok"): - command = command + vlagArg2 - if(vlagArg3 is not None): - command = command + " port-channel " - value = cnos.checkSanityofVariable( - deviceType, "vlag_port_aggregation", vlagArg3) - if(value == "ok"): - command = command + vlagArg3 - else: - retVal = "Error-162" - return retVal - else: - command = command + " enable " - else: - retVal = "Error-166" - return retVal - - elif(vlagArg1 == "hlthchk"): - # debugOutput("hlthchk") - command = command + vlagArg1 + " " - value = cnos.checkSanityofVariable( - deviceType, "vlag_hlthchk_options", vlagArg2) - if(value == "ok"): - if(vlagArg2 == "keepalive-attempts"): - value = cnos.checkSanityofVariable( - deviceType, "vlag_keepalive_attempts", vlagArg3) - if(value == "ok"): - command = command + vlagArg2 + " " + vlagArg3 - else: - retVal = "Error-167" - return retVal - elif(vlagArg2 == "keepalive-interval"): - value = cnos.checkSanityofVariable( - deviceType, "vlag_keepalive_interval", vlagArg3) - if(value == "ok"): - command = command + vlagArg2 + " " + vlagArg3 - else: - retVal = "Error-168" - return retVal - elif(vlagArg2 == "retry-interval"): - value = cnos.checkSanityofVariable( - deviceType, "vlag_retry_interval", vlagArg3) - if(value == "ok"): - command = command + vlagArg2 + " " + vlagArg3 - else: - retVal = "Error-169" - return retVal - elif(vlagArg2 == "peer-ip"): - # Here I am not taking care of IPV6 option. - value = cnos.checkSanityofVariable( - deviceType, "vlag_peerip", vlagArg3) - if(value == "ok"): - command = command + vlagArg2 + " " + vlagArg3 - if(vlagArg4 is not None): - value = cnos.checkSanityofVariable( - deviceType, "vlag_peerip_vrf", vlagArg4) - if(value == "ok"): - command = command + " vrf " + vlagArg4 - else: - retVal = "Error-170" - return retVal - else: - retVal = "Error-171" - return retVal - - else: - retVal = "Error-172" - return retVal - - # debugOutput(command) - cmd = [{'command': command, 'prompt': None, 'answer': None}] - retVal = retVal + str(cnos.run_cnos_commands(module, cmd)) - return retVal -# EOM - - -def main(): - # - # Define parameters for vlag creation entry - # - module = AnsibleModule( - argument_spec=dict( - outputfile=dict(required=True), - host=dict(required=False), - username=dict(required=False), - password=dict(required=False, no_log=True), - enablePassword=dict(required=False, no_log=True), - deviceType=dict(required=True), - vlagArg1=dict(required=True), - vlagArg2=dict(required=False), - vlagArg3=dict(required=False), - vlagArg4=dict(required=False),), - supports_check_mode=False) - - outputfile = module.params['outputfile'] - output = "" - - # Send the CLi command - output = output + str(vlagConfig(module, '(config)#', None)) - - # Save it into the file - file = open(outputfile, "a") - file.write(output) - file.close() - - # need to add logic to check when changes occur or not - errorMsg = cnos.checkOutputForError(output) - if(errorMsg is None): - module.exit_json(changed=True, msg="VLAG configurations accomplished") - else: - module.fail_json(msg=errorMsg) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_vlan.py b/plugins/modules/network/cnos/cnos_vlan.py deleted file mode 100644 index ce336c2ccb..0000000000 --- a/plugins/modules/network/cnos/cnos_vlan.py +++ /dev/null @@ -1,409 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (C) 2017 Lenovo, Inc. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to send VLAN commands to Lenovo Switches -# Overloading aspect of vlan creation in a range is pending -# Lenovo Networking - - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_vlan -author: "Anil Kumar Mureleedharan(@amuraleedhar)" -short_description: Manage VLANs on CNOS network devices -description: - - This module provides declarative management of VLANs - on Lenovo CNOS network devices. -notes: - - Tested against CNOS 10.8.1 -options: - name: - description: - - Name of the VLAN. - vlan_id: - description: - - ID of the VLAN. Range 1-4094. - required: true - interfaces: - description: - - List of interfaces that should be associated to the VLAN. - required: true - associated_interfaces: - description: - - This is a intent option and checks the operational state of the for - given vlan C(name) for associated interfaces. If the value in the - C(associated_interfaces) does not match with the operational state of - vlan interfaces on device it will result in failure. - delay: - description: - - Delay the play should wait to check for declarative intent params - values. - default: 10 - aggregate: - description: List of VLANs definitions. - purge: - description: - - Purge VLANs not defined in the I(aggregate) parameter. - default: no - type: bool - state: - description: - - State of the VLAN configuration. - default: present - choices: ['present', 'absent', 'active', 'suspend'] - provider: - description: - - B(Deprecated) - - "Starting with Ansible 2.5 we recommend using C(connection: network_cli)." - - For more information please see the L(CNOS Platform Options guide, ../network/user_guide/platform_cnos.html). - - HORIZONTALLINE - - A dict object containing connection details. - 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. - required: true - port: - description: - - Specifies the port to use when building the connection to the remote device. - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_USERNAME) will be used instead. - password: - description: - - Specifies the password to use to authenticate the connection to - the remote device. This value is used to authenticate - the SSH session. If the value is not specified in the task, the - value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead. - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. If the timeout is - exceeded before the operation is completed, the module will error. - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to - the remote device. This value is the path to the - key used to authenticate the SSH session. If the value is not specified - in the task, the value of environment variable C(ANSIBLE_NET_SSH_KEYFILE) - will be used instead. - 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: 'no' - 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. -''' - -EXAMPLES = """ -- name: Create vlan - cnos_vlan: - vlan_id: 100 - name: test-vlan - state: present - -- name: Add interfaces to VLAN - cnos_vlan: - vlan_id: 100 - interfaces: - - Ethernet1/33 - - Ethernet1/44 - -- name: Check if interfaces is assigned to VLAN - cnos_vlan: - vlan_id: 100 - associated_interfaces: - - Ethernet1/33 - - Ethernet1/44 - -- name: Delete vlan - cnos_vlan: - vlan_id: 100 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - vlan 100 - - name test-vlan -""" - -import re -import time - -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import load_config, run_commands -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import debugOutput, check_args -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec -from ansible.module_utils._text import to_text - - -def search_obj_in_list(vlan_id, lst): - obj = list() - for o in lst: - if o['vlan_id'] == vlan_id: - return o - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params['purge'] - - for w in want: - vlan_id = w['vlan_id'] - name = w['name'] - interfaces = w['interfaces'] - state = w['state'] - - obj_in_have = search_obj_in_list(vlan_id, have) - - if state == 'absent': - if obj_in_have: - commands.append('no vlan {0}'.format(vlan_id)) - - elif state == 'present': - if not obj_in_have: - commands.append('vlan {0}'.format(vlan_id)) - if name: - commands.append('name {0}'.format(name)) - - if interfaces: - for i in interfaces: - commands.append('interface {0}'.format(i)) - commands.append('switchport mode access') - commands.append('switchport access vlan {0}'.format(vlan_id)) - - else: - if name: - if name != obj_in_have['name']: - commands.append('vlan {0}'.format(vlan_id)) - commands.append('name {0}'.format(name)) - - if interfaces: - if not obj_in_have['interfaces']: - for i in interfaces: - commands.append('vlan {0}'.format(vlan_id)) - commands.append('interface {0}'.format(i)) - commands.append('switchport mode access') - commands.append('switchport access vlan {0}'.format(vlan_id)) - - elif set(interfaces) != set(obj_in_have['interfaces']): - missing_interfaces = list(set(interfaces) - set(obj_in_have['interfaces'])) - for i in missing_interfaces: - commands.append('vlan {0}'.format(vlan_id)) - commands.append('interface {0}'.format(i)) - commands.append('switchport mode access') - commands.append('switchport access vlan {0}'.format(vlan_id)) - - superfluous_interfaces = list(set(obj_in_have['interfaces']) - set(interfaces)) - for i in superfluous_interfaces: - commands.append('vlan {0}'.format(vlan_id)) - commands.append('interface {0}'.format(i)) - commands.append('switchport mode access') - commands.append('no switchport access vlan') - else: - commands.append('vlan {0}'.format(vlan_id)) - if name: - commands.append('name {0}'.format(name)) - commands.append('state {0}'.format(state)) - - if purge: - for h in have: - obj_in_want = search_obj_in_list(h['vlan_id'], want) - if not obj_in_want and h['vlan_id'] != '1': - commands.append('no vlan {0}'.format(h['vlan_id'])) - - return commands - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - d = item.copy() - d['vlan_id'] = str(d['vlan_id']) - - obj.append(d) - else: - obj.append({ - 'vlan_id': str(module.params['vlan_id']), - 'name': module.params['name'], - 'interfaces': module.params['interfaces'], - # 'associated_interfaces': module.params['associated_interfaces'], - 'state': module.params['state'] - }) - - return obj - - -def parse_to_logical_rows(out): - relevant_data = False - cur_row = [] - for line in out.splitlines(): - if not line: - """Skip empty lines.""" - continue - if '0' < line[0] <= '9': - """Line starting with a number.""" - if len(cur_row) > 0: - yield cur_row - cur_row = [] # Reset it to hold a next chunk - relevant_data = True - if relevant_data: - data = line.strip().split('(') - cur_row.append(data[0]) - yield cur_row - - -def parse_to_obj(logical_rows): - first_row = logical_rows[0] - rest_rows = logical_rows[1:] - vlan_data = first_row.split() - obj = {} - obj['vlan_id'] = vlan_data[0] - obj['name'] = vlan_data[1] - obj['state'] = vlan_data[2] - obj['interfaces'] = rest_rows - return obj - - -def parse_vlan_brief(vlan_out): - return [parse_to_obj(r) for r in parse_to_logical_rows(vlan_out)] - - -def map_config_to_obj(module): - return parse_vlan_brief(run_commands(module, ['show vlan brief'])[0]) - - -def check_declarative_intent_params(want, module, result): - - have = None - is_delay = False - - for w in want: - if w.get('associated_interfaces') is None: - continue - - if result['changed'] and not is_delay: - time.sleep(module.params['delay']) - is_delay = True - - if have is None: - have = map_config_to_obj(module) - - for i in w['associated_interfaces']: - obj_in_have = search_obj_in_list(w['vlan_id'], have) - if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']: - module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id'])) - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - vlan_id=dict(type='int'), - name=dict(), - interfaces=dict(type='list'), - associated_interfaces=dict(type='list'), - delay=dict(default=10, type='int'), - state=dict(default='present', - choices=['present', 'absent', 'active', 'suspend']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['vlan_id'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - argument_spec.update(cnos_argument_spec) - - required_one_of = [['vlan_id', 'aggregate']] - mutually_exclusive = [['vlan_id', 'aggregate']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - warnings = list() - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - check_declarative_intent_params(want, module, result) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cnos/cnos_vrf.py b/plugins/modules/network/cnos/cnos_vrf.py deleted file mode 100644 index 1d8ffc5d6b..0000000000 --- a/plugins/modules/network/cnos/cnos_vrf.py +++ /dev/null @@ -1,369 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type -# -# Copyright (C) 2019 Lenovo. -# (c) 2017, Ansible by Red Hat, 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 . -# -# Module to work on management of local users on Lenovo CNOS Switches -# Lenovo Networking -# -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: cnos_vrf -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage VRFs on Lenovo CNOS network devices -description: - - This module provides declarative management of VRFs - on Lenovo CNOS network devices. -notes: - - Tested against CNOS 10.9.1 -options: - name: - description: - - Name of the VRF. - required: true - rd: - description: - - Route distinguisher of the VRF - interfaces: - description: - - Identifies the set of interfaces that - should be configured in the VRF. Interfaces must be routed - interfaces in order to be placed into a VRF. The name of interface - should be in expanded format and not abbreviated. - associated_interfaces: - description: - - This is a intent option and checks the operational state of the for - given vrf C(name) for associated interfaces. If the value in the - C(associated_interfaces) does not match with the operational state of - vrf interfaces on device it will result in failure. - aggregate: - description: List of VRFs contexts - purge: - description: - - Purge VRFs not defined in the I(aggregate) parameter. - default: no - type: bool - delay: - description: - - Time in seconds to wait before checking for the operational state on - remote device. This wait is applicable for operational state arguments. - default: 10 - state: - description: - - State of the VRF configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: Create vrf - cnos_vrf: - name: test - rd: 1:200 - interfaces: - - Ethernet1/33 - state: present - -- name: Delete VRFs - cnos_vrf: - name: test - state: absent - -- name: Create aggregate of VRFs with purge - cnos_vrf: - aggregate: - - { name: test4, rd: "1:204" } - - { name: test5, rd: "1:205" } - state: present - purge: yes - -- name: Delete aggregate of VRFs - cnos_vrf: - aggregate: - - name: test2 - - name: test3 - - name: test4 - - name: test5 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - vrf context test - - rd 1:100 - - interface Ethernet1/44 - - vrf member test -""" -import re -import time - -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import load_config, run_commands -from ansible_collections.community.general.plugins.module_utils.network.cnos.cnos import cnos_argument_spec, check_args - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - -def get_interface_type(interface): - intf_type = 'unknown' - if interface.upper()[:2] in ('ET', 'GI', 'FA', 'TE', 'FO', 'HU', 'TWE'): - intf_type = 'ethernet' - elif interface.upper().startswith('VL'): - intf_type = 'svi' - elif interface.upper().startswith('LO'): - intf_type = 'loopback' - elif interface.upper()[:2] in ('MG', 'MA'): - intf_type = 'management' - elif interface.upper().startswith('PO'): - intf_type = 'portchannel' - elif interface.upper().startswith('NV'): - intf_type = 'nve' - - return intf_type - - -def is_switchport(name, module): - intf_type = get_interface_type(name) - - if intf_type in ('ethernet', 'portchannel'): - config = run_commands(module, - ['show interface {0} switchport'.format(name)])[0] - match = re.search(r'Switchport : enabled', config) - return bool(match) - return False - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - state = module.params['state'] - purge = module.params['purge'] - - for w in want: - name = w['name'] - rd = w['rd'] - interfaces = w['interfaces'] - - obj_in_have = search_obj_in_list(name, have) - - if name == 'default': - module.fail_json(msg='VRF context default is reserved') - elif len(name) > 63: - module.fail_json(msg='VRF name is too long') - if state == 'absent': - if name == 'management': - module.fail_json(msg='Management VRF context cannot be deleted') - if obj_in_have: - commands.append('no vrf context %s' % name) - elif state == 'present': - if not obj_in_have: - commands.append('vrf context %s' % name) - - if rd is not None: - commands.append('rd %s' % rd) - - if w['interfaces']: - for i in w['interfaces']: - commands.append('interface %s' % i) - commands.append('vrf member %s' % w['name']) - else: - if w['rd'] is not None and w['rd'] != obj_in_have['rd']: - commands.append('vrf context %s' % w['name']) - commands.append('rd %s' % w['rd']) - - if w['interfaces']: - if not obj_in_have['interfaces']: - for i in w['interfaces']: - commands.append('interface %s' % i) - commands.append('vrf member %s' % w['name']) - elif set(w['interfaces']) != obj_in_have['interfaces']: - missing_interfaces = list(set(w['interfaces']) - set(obj_in_have['interfaces'])) - - for i in missing_interfaces: - commands.append('interface %s' % i) - commands.append('vrf member %s' % w['name']) - - if purge: - for h in have: - obj_in_want = search_obj_in_list(h['name'], want) - if not obj_in_want: - commands.append('no vrf context %s' % h['name']) - - return commands - - -def map_config_to_obj(module): - objs = [] - output = run_commands(module, {'command': 'show vrf'}) - if output is not None: - vrfText = output[0].strip() - vrfList = vrfText.split('VRF') - for vrfItem in vrfList: - if 'FIB ID' in vrfItem: - obj = dict() - list_of_words = vrfItem.split() - vrfName = list_of_words[0] - obj['name'] = vrfName[:-1] - obj['rd'] = list_of_words[list_of_words.index('RD') + 1] - start = False - obj['interfaces'] = [] - for intName in list_of_words: - if 'Interfaces' in intName: - start = True - if start is True: - if '!' not in intName and 'Interfaces' not in intName: - obj['interfaces'].append(intName.strip().lower()) - objs.append(obj) - else: - module.fail_json(msg='Could not fetch VRF details from device') - return objs - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - if item.get('interfaces'): - item['interfaces'] = [intf.replace(" ", "").lower() for intf in item.get('interfaces') if intf] - - if item.get('associated_interfaces'): - item['associated_interfaces'] = [intf.replace(" ", "").lower() for intf in item.get('associated_interfaces') if intf] - - obj.append(item.copy()) - else: - obj.append({ - 'name': module.params['name'], - 'state': module.params['state'], - 'rd': module.params['rd'], - 'interfaces': [intf.replace(" ", "").lower() for intf in module.params['interfaces']] if module.params['interfaces'] else [], - 'associated_interfaces': [intf.replace(" ", "").lower() for intf in - module.params['associated_interfaces']] if module.params['associated_interfaces'] else [] - - }) - - return obj - - -def check_declarative_intent_params(want, module, result): - have = None - is_delay = False - - for w in want: - if w.get('associated_interfaces') is None: - continue - - if result['changed'] and not is_delay: - time.sleep(module.params['delay']) - is_delay = True - - if have is None: - have = map_config_to_obj(module) - - for i in w['associated_interfaces']: - obj_in_have = search_obj_in_list(w['name'], have) - - if obj_in_have: - interfaces = obj_in_have.get('interfaces') - if interfaces is not None and i not in interfaces: - module.fail_json(msg="Interface %s not configured on vrf %s" % (i, w['name'])) - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - name=dict(), - interfaces=dict(type='list'), - associated_interfaces=dict(type='list'), - delay=dict(default=10, type='int'), - rd=dict(), - state=dict(default='present', choices=['present', 'absent']) - ) - - aggregate_spec = deepcopy(element_spec) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - check_args(module, warnings) - - result = {'changed': False} - - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - for w in want: - name = w['name'] - name = name.lower() - if is_switchport(name, module): - module.fail_json(msg='Ensure interface is configured to be a L3' - '\nport first before using this module. You can use' - '\nthe cnos_interface module for this.') - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - check_declarative_intent_params(want, module, result) - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/cumulus/nclu.py b/plugins/modules/network/cumulus/nclu.py deleted file mode 100644 index 85a1af6f89..0000000000 --- a/plugins/modules/network/cumulus/nclu.py +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2016-2018, Cumulus Networks -# 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: nclu -author: "Cumulus Networks (@isharacomix)" -short_description: Configure network interfaces using NCLU -description: - - Interface to the Network Command Line Utility, developed to make it easier - to configure operating systems running ifupdown2 and Quagga, such as - Cumulus Linux. Command documentation is available at - U(https://docs.cumulusnetworks.com/cumulus-linux/System-Configuration/Network-Command-Line-Utility-NCLU/) -options: - commands: - description: - - A list of strings containing the net commands to run. Mutually - exclusive with I(template). - template: - description: - - A single, multi-line string with jinja2 formatting. This string - will be broken by lines, and each line will be run through net. - Mutually exclusive with I(commands). - commit: - description: - - When true, performs a 'net commit' at the end of the block. - Mutually exclusive with I(atomic). - default: false - type: bool - abort: - description: - - Boolean. When true, perform a 'net abort' before the block. - This cleans out any uncommitted changes in the buffer. - Mutually exclusive with I(atomic). - default: false - type: bool - atomic: - description: - - When true, equivalent to both I(commit) and I(abort) being true. - Mutually exclusive with I(commit) and I(atomic). - default: false - type: bool - description: - description: - - Commit description that will be recorded to the commit log if - I(commit) or I(atomic) are true. - default: "Ansible-originated commit" -''' - -EXAMPLES = ''' - -- name: Add two interfaces without committing any changes - nclu: - commands: - - add int swp1 - - add int swp2 - -- name: Modify hostname to Cumulus-1 and commit the change - nclu: - commands: - - add hostname Cumulus-1 - commit: true - -- name: Add 48 interfaces and commit the change. - nclu: - template: | - {% for iface in range(1,49) %} - add int swp{{iface}} - {% endfor %} - commit: true - description: "Ansible - add swps1-48" - -- name: Fetch Status Of Interface - nclu: - commands: - - show interface swp1 - register: output - -- name: Print Status Of Interface - debug: - var: output - -- name: Fetch Details From All Interfaces In JSON Format - nclu: - commands: - - show interface json - register: output - -- name: Print Interface Details - debug: - var: output["msg"] - -- name: Atomically add an interface - nclu: - commands: - - add int swp1 - atomic: true - description: "Ansible - add swp1" - -- name: Remove IP address from interface swp1 - nclu: - commands: - - del int swp1 ip address 1.1.1.1/24 - -- name: Configure BGP AS and add 2 EBGP neighbors using BGP Unnumbered - nclu: - commands: - - add bgp autonomous-system 65000 - - add bgp neighbor swp51 interface remote-as external - - add bgp neighbor swp52 interface remote-as external - commit: true - -- name: Configure BGP AS and Add 2 EBGP neighbors Using BGP Unnumbered via Template - nclu: - template: | - {% for neighbor in range(51,53) %} - add bgp neighbor swp{{neighbor}} interface remote-as external - add bgp autonomous-system 65000 - {% endfor %} - atomic: true - -- name: Check BGP Status - nclu: - commands: - - show bgp summary json - register: output - -- name: Print BGP Status In JSON - debug: - var: output["msg"] -''' - -RETURN = ''' -changed: - description: whether the interface was changed - returned: changed - type: bool - sample: True -msg: - description: human-readable report of success or failure - returned: always - type: str - sample: "interface bond0 config updated" -''' - -from ansible.module_utils.basic import AnsibleModule - - -def command_helper(module, command, errmsg=None): - """Run a command, catch any nclu errors""" - (_rc, output, _err) = module.run_command("/usr/bin/net %s" % command) - if _rc or 'ERROR' in output or 'ERROR' in _err: - module.fail_json(msg=errmsg or output) - return str(output) - - -def check_pending(module): - """Check the pending diff of the nclu buffer.""" - pending = command_helper(module, "pending", "Error in pending config. You may want to view `net pending` on this target.") - - delimeter1 = "net add/del commands since the last 'net commit'" - color1 = '\x1b[94m' - if delimeter1 in pending: - pending = pending.split(delimeter1)[0] - pending = pending.replace(color1, '') - return pending.strip() - - -def run_nclu(module, command_list, command_string, commit, atomic, abort, description): - _changed = False - - commands = [] - if command_list: - commands = command_list - elif command_string: - commands = command_string.splitlines() - - do_commit = False - do_abort = abort - if commit or atomic: - do_commit = True - if atomic: - do_abort = True - - if do_abort: - command_helper(module, "abort") - - # First, look at the staged commands. - before = check_pending(module) - # Run all of the net commands - output_lines = [] - for line in commands: - if line.strip(): - output_lines += [command_helper(module, line.strip(), "Failed on line %s" % line)] - output = "\n".join(output_lines) - - # If pending changes changed, report a change. - after = check_pending(module) - if before == after: - _changed = False - else: - _changed = True - - # Do the commit. - if do_commit: - result = command_helper(module, "commit description '%s'" % description) - if "commit ignored" in result: - _changed = False - command_helper(module, "abort") - elif command_helper(module, "show commit last") == "": - _changed = False - - return _changed, output - - -def main(testing=False): - module = AnsibleModule(argument_spec=dict( - commands=dict(required=False, type='list'), - template=dict(required=False, type='str'), - description=dict(required=False, type='str', default="Ansible-originated commit"), - abort=dict(required=False, type='bool', default=False), - commit=dict(required=False, type='bool', default=False), - atomic=dict(required=False, type='bool', default=False)), - mutually_exclusive=[('commands', 'template'), - ('commit', 'atomic'), - ('abort', 'atomic')] - ) - command_list = module.params.get('commands', None) - command_string = module.params.get('template', None) - commit = module.params.get('commit') - atomic = module.params.get('atomic') - abort = module.params.get('abort') - description = module.params.get('description') - - _changed, output = run_nclu(module, command_list, command_string, commit, atomic, abort, description) - if not testing: - module.exit_json(changed=_changed, msg=output) - elif testing: - return {"changed": _changed, "msg": output} - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/edgeos/edgeos_command.py b/plugins/modules/network/edgeos/edgeos_command.py deleted file mode 100644 index 944be74108..0000000000 --- a/plugins/modules/network/edgeos/edgeos_command.py +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' -module: edgeos_command -author: - - Chad Norgan (@beardymcbeards) - - Sam Doran (@samdoran) -short_description: Run one or more commands on EdgeOS devices -description: - - This command module allows running one or more commands on a remote - device running EdgeOS, such as the Ubiquiti EdgeRouter. - - This module does not support running commands in configuration mode. - - Certain C(show) commands in EdgeOS produce many lines of output and - use a custom pager that can cause this module to hang. If the - value of the environment variable C(ANSIBLE_EDGEOS_TERMINAL_LENGTH) - is not set, the default number of 10000 is used. - - "This is a network module and requires C(connection: network_cli) - in order to work properly." - - For more information please see the L(Network Guide,../network/getting_started/index.html). -options: - commands: - description: - - The commands or ordered set of commands that should be run against the - remote device. The output of the command is returned to the playbook. - If the C(wait_for) argument is provided, the module is not returned - until the condition is met or the number of retries is exceeded. - required: True - wait_for: - description: - - Causes the task to wait for a specific condition to be met before - moving forward. If the condition is not met before the specified - number of retries is exceeded, the task will fail. - required: False - match: - description: - - Used in conjunction with C(wait_for) to create match policy. If set to - C(all), then all conditions in C(wait_for) must be met. If set to - C(any), then only one condition must match. - required: False - default: 'all' - choices: ['any', 'all'] - retries: - description: - - Number of times a command should be tried before it is considered failed. - The command is run on the target device and evaluated against the - C(wait_for) conditionals. - required: False - default: 10 - interval: - description: - - The number of seconds to wait between C(retries) of the command. - required: False - default: 1 - -notes: - - Tested against EdgeOS 1.9.7 - - Running C(show system boot-messages all) will cause the module to hang since - EdgeOS is using a custom pager setting to display the output of that command. -''' - -EXAMPLES = """ -tasks: - - name: Reboot the device - edgeos_command: - commands: reboot now - - - name: Show the configuration for eth0 and eth1 - edgeos_command: - commands: show interfaces ethernet {{ item }} - loop: - - eth0 - - eth1 -""" - -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 - type: list - sample: [['...', '...'], ['...'], ['...']] -""" -import time - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import transform_commands, to_lines -from ansible_collections.community.general.plugins.module_utils.network.edgeos.edgeos import run_commands - - -def parse_commands(module, warnings): - commands = transform_commands(module) - - if module.check_mode: - for item in list(commands): - if not item['command'].startswith('show'): - warnings.append( - 'Only show commands are supported when using check mode, not ' - 'executing %s' % item['command'] - ) - commands.remove(item) - - return commands - - -def main(): - spec = dict( - commands=dict(type='list', required=True), - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=spec, supports_check_mode=True) - - warnings = list() - result = {'changed': False, 'warnings': warnings} - commands = parse_commands(module, warnings) - wait_for = module.params['wait_for'] or list() - - try: - conditionals = [Conditional(c) for c in wait_for] - except AttributeError as exc: - module.fail_json(msg=to_text(exc)) - - 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({ - 'stdout': responses, - 'stdout_lines': list(to_lines(responses)), - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/edgeos/edgeos_config.py b/plugins/modules/network/edgeos/edgeos_config.py deleted file mode 100644 index cc3b4f17aa..0000000000 --- a/plugins/modules/network/edgeos/edgeos_config.py +++ /dev/null @@ -1,317 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright (c) 2018 Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: edgeos_config -author: - - "Nathaniel Case (@Qalthos)" - - "Sam Doran (@samdoran)" -short_description: Manage EdgeOS configuration on remote device -description: - - This module provides configuration file management of EdgeOS - devices. It provides arguments for managing both the - configuration file and state of the active configuration. All - configuration statements are based on `set` and `delete` commands - in the device configuration. - - "This is a network module and requires the C(connection: network_cli) in order - to work properly." - - For more information please see the L(Network Guide,../network/getting_started/index.html). -notes: - - Tested against EdgeOS 1.9.7 - - Setting C(ANSIBLE_PERSISTENT_COMMAND_TIMEOUT) to 30 is recommended since - the save command can take longer than the default of 10 seconds on - some EdgeOS hardware. -options: - lines: - description: - - The ordered set of configuration lines to be managed and - compared with the existing configuration on the remote - device. - src: - description: - - The C(src) argument specifies the path to the source config - file to load. The source config file can either be in - bracket format or set format. The source file can include - Jinja2 template variables. - match: - description: - - The C(match) argument controls the method used to match - against the current active configuration. By default, the - desired config is matched against the active config and the - deltas are loaded. If the C(match) argument is set to C(none) - the active configuration is ignored and the configuration is - always loaded. - default: line - choices: ['line', 'none'] - backup: - description: - - The C(backup) argument will backup the current device's active - configuration to the Ansible control host prior to making any - changes. If the C(backup_options) value is not given, the backup - file will be located in the backup folder in the playbook root - directory or role root directory if the playbook is part of an - ansible role. If the directory does not exist, it is created. - type: bool - default: 'no' - comment: - description: - - Allows a commit description to be specified to be included - when the configuration is committed. If the configuration is - not changed or committed, this argument is ignored. - default: 'configured by edgeos_config' - config: - description: - - The C(config) argument specifies the base configuration to use - to compare against the desired configuration. If this value - is not specified, the module will automatically retrieve the - current active configuration from the remote device. - save: - description: - - The C(save) argument controls whether or not changes made - to the active configuration are saved to disk. This is - independent of committing the config. When set to C(True), the - active configuration is saved. - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- name: configure the remote device - edgeos_config: - lines: - - set system host-name {{ inventory_hostname }} - - set service lldp - - delete service dhcp-server - -- name: backup and load from file - edgeos_config: - src: edgeos.cfg - backup: yes - -- name: configurable backup path - edgeos_config: - src: edgeos.cfg - backup: yes - backup_options: - filename: backup.cfg - dir_path: /home/user -""" - -RETURN = """ -commands: - description: The list of configuration commands sent to the device - returned: always - type: list - sample: ['...', '...'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/edgeos_config.2016-07-16@22:28:34 -""" - -import re - -from ansible.module_utils._text import to_native -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.community.general.plugins.module_utils.network.edgeos.edgeos import load_config, get_config, run_commands - - -DEFAULT_COMMENT = 'configured by edgeos_config' - - -def config_to_commands(config): - set_format = config.startswith('set') or config.startswith('delete') - candidate = NetworkConfig(indent=4, contents=config) - if not set_format: - candidate = [c.line for c in candidate.items] - commands = list() - # this filters out less specific lines - for item in candidate: - for index, entry in enumerate(commands): - if item.startswith(entry): - del commands[index] - break - commands.append(item) - - commands = ['set %s' % cmd.replace(' {', '') for cmd in commands] - - else: - commands = to_native(candidate).split('\n') - - return commands - - -def get_candidate(module): - contents = module.params['src'] or module.params['lines'] - - if module.params['lines']: - contents = '\n'.join(contents) - - return config_to_commands(contents) - - -def check_command(module, command): - """Tests against a command line to be valid otherwise raise errors - - Error on uneven single quote which breaks ansible waiting for further input. Ansible - will handle even single quote failures correctly. - - :param command: the command line from current or new config - :type command: string - :raises ValueError: - * if contains odd number of single quotes - :return: command string unchanged - :rtype: string - """ - if command.count("'") % 2 != 0: - module.fail_json(msg="Unmatched single (') quote found in command: " + command) - - return command - - -def diff_config(module, commands, config): - config = [to_native(check_command(module, c)) for c in config.splitlines()] - - updates = list() - visited = set() - delete_commands = [line for line in commands if line.startswith('delete')] - - for line in commands: - item = to_native(check_command(module, line)) - - if not item.startswith('set') and not item.startswith('delete'): - raise ValueError('line must start with either `set` or `delete`') - - elif item.startswith('set'): - - if item not in config: - updates.append(line) - - # If there is a corresponding delete command in the desired config, make sure to append - # the set command even though it already exists in the running config - else: - ditem = re.sub('set', 'delete', item) - for line in delete_commands: - if ditem.startswith(line): - updates.append(item) - - elif item.startswith('delete'): - if not config: - updates.append(line) - else: - item = re.sub(r'delete', 'set', item) - for entry in config: - if entry.startswith(item) and line not in visited: - updates.append(line) - visited.add(line) - - return list(updates) - - -def run(module, result): - # get the current active config from the node or passed in via - # the config param - config = module.params['config'] or get_config(module) - - # create the candidate config object from the arguments - candidate = get_candidate(module) - - # create loadable config that includes only the configuration updates - commands = diff_config(module, candidate, config) - - result['commands'] = commands - - commit = not module.check_mode - comment = module.params['comment'] - - if commands: - load_config(module, commands, commit=commit, comment=comment) - - result['changed'] = True - - -def main(): - - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - spec = dict( - src=dict(type='path'), - lines=dict(type='list'), - - match=dict(default='line', choices=['line', 'none']), - - comment=dict(default=DEFAULT_COMMENT), - - config=dict(), - - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - save=dict(type='bool', default=False), - ) - - mutually_exclusive = [('lines', 'src')] - - module = AnsibleModule( - argument_spec=spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True - ) - - warnings = list() - - result = dict(changed=False, warnings=warnings) - - if module.params['backup']: - result['__backup__'] = get_config(module=module) - - if any((module.params['src'], module.params['lines'])): - run(module, result) - - if module.params['save']: - diff = run_commands(module, commands=['configure', 'compare saved'])[1] - if diff != '[edit]': - run_commands(module, commands=['save']) - result['changed'] = True - run_commands(module, commands=['exit']) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/edgeos/edgeos_facts.py b/plugins/modules/network/edgeos/edgeos_facts.py deleted file mode 100644 index 104061829a..0000000000 --- a/plugins/modules/network/edgeos/edgeos_facts.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright (c) 2018 Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: edgeos_facts -author: - - Nathaniel Case (@Qalthos) - - Sam Doran (@samdoran) -short_description: Collect facts from remote devices running EdgeOS -description: - - Collects a base set of device facts from a remote device that - is running EdgeOS. This module prepends all of the - base network fact keys with U(ansible_net_). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -notes: - - Tested against EdgeOS 1.9.7 -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, default, config, and neighbors. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: "!config" -''' - -EXAMPLES = """ -- name: collect all facts from the device - edgeos_facts: - gather_subset: all - -- name: collect only the config and default facts - edgeos_facts: - gather_subset: config - -- name: collect everything exception the config - edgeos_facts: - gather_subset: "!config" -""" - -RETURN = """ -ansible_net_config: - description: The running-config from the device - returned: when config is configured - type: str -ansible_net_commits: - description: The set of available configuration revisions - returned: when present - type: list -ansible_net_hostname: - description: The configured system hostname - returned: always - type: str -ansible_net_model: - description: The device model string - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the device - returned: always - type: str -ansible_net_version: - description: The version of the software running - returned: always - type: str -ansible_net_neighbors: - description: The set of LLDP neighbors - returned: when interface is configured - type: list -ansible_net_gather_subset: - description: The list of subsets gathered by the module - returned: always - type: list -""" - -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.community.general.plugins.module_utils.network.edgeos.edgeos import run_commands - - -class FactsBase(object): - - COMMANDS = frozenset() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, list(self.COMMANDS)) - - -class Default(FactsBase): - - COMMANDS = [ - 'show version', - 'show host name', - ] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - self.facts['model'] = self.parse_model(data) - - self.facts['hostname'] = self.responses[1] - - def parse_version(self, data): - match = re.search(r'Version:\s*v(\S+)', data) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'HW model:\s*([A-Za-z0-9- ]+)', data) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'HW S/N:\s+(\S+)', data) - if match: - return match.group(1) - - -class Config(FactsBase): - - COMMANDS = [ - 'show configuration commands', - 'show system commit', - ] - - def populate(self): - super(Config, self).populate() - - self.facts['config'] = self.responses - - commits = self.responses[1] - entries = list() - entry = None - - for line in commits.split('\n'): - match = re.match(r'(\d+)\s+(.+)by(.+)via(.+)', line) - if match: - if entry: - entries.append(entry) - - entry = dict(revision=match.group(1), - datetime=match.group(2), - by=str(match.group(3)).strip(), - via=str(match.group(4)).strip(), - comment=None) - elif entry: - entry['comment'] = line.strip() - - self.facts['commits'] = entries - - -class Neighbors(FactsBase): - - COMMANDS = [ - 'show lldp neighbors', - 'show lldp neighbors detail', - ] - - def populate(self): - super(Neighbors, self).populate() - - all_neighbors = self.responses[0] - if 'LLDP not configured' not in all_neighbors: - neighbors = self.parse( - self.responses[1] - ) - self.facts['neighbors'] = self.parse_neighbors(neighbors) - - def parse(self, data): - parsed = list() - values = None - for line in data.split('\n'): - if not line: - continue - elif line[0] == ' ': - values += '\n%s' % line - elif line.startswith('Interface'): - if values: - parsed.append(values) - values = line - if values: - parsed.append(values) - return parsed - - def parse_neighbors(self, data): - facts = dict() - for item in data: - interface = self.parse_interface(item) - host = self.parse_host(item) - port = self.parse_port(item) - if interface not in facts: - facts[interface] = list() - facts[interface].append(dict(host=host, port=port)) - return facts - - def parse_interface(self, data): - match = re.search(r'^Interface:\s+(\S+),', data) - return match.group(1) - - def parse_host(self, data): - match = re.search(r'SysName:\s+(.+)$', data, re.M) - if match: - return match.group(1) - - def parse_port(self, data): - match = re.search(r'PortDescr:\s+(.+)$', data, re.M) - if match: - return match.group(1) - - -FACT_SUBSETS = dict( - default=Default, - neighbors=Neighbors, - config=Config -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - module = AnsibleModule(argument_spec=spec, - supports_check_mode=True) - - warnings = list() - - 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='Subset must be one of [%s], got %s' % - (', '.join(VALID_SUBSETS), subset)) - - if exclude: - exclude_subsets.add(subset) - else: - runable_subsets.add(subset) - - if not runable_subsets: - runable_subsets.update(VALID_SUBSETS) - - runable_subsets.difference_update(exclude_subsets) - runable_subsets.add('default') - - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - instances.append(FACT_SUBSETS[key](module)) - - for inst in instances: - inst.populate() - facts.update(inst.facts) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/edgeswitch/edgeswitch_facts.py b/plugins/modules/network/edgeswitch/edgeswitch_facts.py deleted file mode 100644 index 9d6969ecb6..0000000000 --- a/plugins/modules/network/edgeswitch/edgeswitch_facts.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright (c) 2018 Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: edgeswitch_facts -author: "Frederic Bor (@f-bor)" -short_description: Collect facts from remote devices running Edgeswitch -description: - - Collects a base set of device facts from a remote device that - is running Ubiquiti Edgeswitch. This module prepends all of the - base network fact keys with C(ansible_net_). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -notes: - - Tested against Edgeswitch 1.7.4 -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: '!config' -''' - -EXAMPLES = """ -# Collect all facts from the device -- edgeswitch_facts: - gather_subset: all - -# Collect only the config and default facts -- edgeswitch_facts: - gather_subset: - - config - -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - -# config -ansible_net_config: - description: The current active config from the device - returned: when config is configured - type: str - -# interfaces -ansible_net_interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -""" -import re - -from ansible_collections.community.general.plugins.module_utils.network.edgeswitch.edgeswitch import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, commands=self.COMMANDS, check_rc=False) - - def run(self, cmd): - return run_commands(self.module, commands=cmd, check_rc=False) - - -class Default(FactsBase): - - COMMANDS = ['show version', 'show sysinfo'] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - self.facts['model'] = self.parse_model(data) - self.facts['hostname'] = self.parse_hostname(self.responses[1]) - - def parse_version(self, data): - match = re.search(r'Software Version\.+ (.*)', data) - if match: - return match.group(1) - - def parse_hostname(self, data): - match = re.search(r'System Name\.+ (.*)', data) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'Machine Model\.+ (.*)', data) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'Serial Number\.+ (.*)', data) - if match: - return match.group(1) - - -class Config(FactsBase): - - COMMANDS = ['show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'show interfaces description', - 'show interfaces status all' - ] - - def populate(self): - super(Interfaces, self).populate() - - interfaces = {} - - data = self.responses[0] - self.parse_interfaces_description(data, interfaces) - - data = self.responses[1] - self.parse_interfaces_status(data, interfaces) - - self.facts['interfaces'] = interfaces - - def parse_interfaces_description(self, data, interfaces): - for line in data.split('\n'): - match = re.match(r'(\d\/\d+)\s+(\w+)\s+(\w+)', line) - if match: - name = match.group(1) - interface = {} - interface['operstatus'] = match.group(2) - interface['lineprotocol'] = match.group(3) - interface['description'] = line[30:] - interfaces[name] = interface - - def parse_interfaces_status(self, data, interfaces): - for line in data.split('\n'): - match = re.match(r'(\d\/\d+)', line) - if match: - name = match.group(1) - interface = interfaces[name] - interface['physicalstatus'] = line[61:71].strip() - interface['mediatype'] = line[73:91].strip() - - -FACT_SUBSETS = dict( - default=Default, - config=Config, - interfaces=Interfaces, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - gather_subset = module.params['gather_subset'] - - runable_subsets = set() - exclude_subsets = set() - - for subset in gather_subset: - if subset == 'all': - runable_subsets.update(VALID_SUBSETS) - continue - - if subset.startswith('!'): - subset = subset[1:] - if subset == 'all': - exclude_subsets.update(VALID_SUBSETS) - continue - exclude = True - else: - exclude = False - - if subset not in VALID_SUBSETS: - module.fail_json(msg='Bad subset') - - if exclude: - exclude_subsets.add(subset) - else: - runable_subsets.add(subset) - - if not runable_subsets: - runable_subsets.update(VALID_SUBSETS) - - runable_subsets.difference_update(exclude_subsets) - runable_subsets.add('default') - - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - instances.append(FACT_SUBSETS[key](module)) - - for inst in instances: - inst.populate() - facts.update(inst.facts) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - module.exit_json(ansible_facts=ansible_facts) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/edgeswitch/edgeswitch_vlan.py b/plugins/modules/network/edgeswitch/edgeswitch_vlan.py deleted file mode 100644 index a367ffd3d7..0000000000 --- a/plugins/modules/network/edgeswitch/edgeswitch_vlan.py +++ /dev/null @@ -1,497 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2018, Ansible by Red Hat, inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: edgeswitch_vlan -author: "Frederic Bor (@f-bor)" -short_description: Manage VLANs on Ubiquiti Edgeswitch network devices -description: - - This module provides declarative management of VLANs - on Ubiquiti Edgeswitch network devices. -notes: - - Tested against edgeswitch 1.7.4 - - This module use native Ubiquiti vlan syntax and does not support switchport compatibility syntax. - For clarity, it is strongly advised to not use both syntaxes on the same interface. - - Edgeswitch does not support deleting or changing name of VLAN 1 - - As auto_tag, auto_untag and auto_exclude are a kind of default setting for all interfaces, they are mutually exclusive - -options: - name: - description: - - Name of the VLAN. - vlan_id: - description: - - ID of the VLAN. Range 1-4093. - tagged_interfaces: - description: - - List of interfaces that should accept and transmit tagged frames for the VLAN. - Accept range of interfaces. - untagged_interfaces: - description: - - List of interfaces that should accept untagged frames and transmit them tagged for the VLAN. - Accept range of interfaces. - excluded_interfaces: - description: - - List of interfaces that should be excluded of the VLAN. - Accept range of interfaces. - auto_tag: - description: - - Each of the switch interfaces will be set to accept and transmit - untagged frames for I(vlan_id) unless defined in I(*_interfaces). - This is a default setting for all switch interfaces. - type: bool - auto_untag: - description: - - Each of the switch interfaces will be set to accept untagged frames and - transmit them tagged for I(vlan_id) unless defined in I(*_interfaces). - This is a default setting for all switch interfaces. - type: bool - auto_exclude: - description: - - Each of the switch interfaces will be excluded from I(vlan_id) - unless defined in I(*_interfaces). - This is a default setting for all switch interfaces. - type: bool - aggregate: - description: List of VLANs definitions. - purge: - description: - - Purge VLANs not defined in the I(aggregate) parameter. - default: no - type: bool - state: - description: - - action on the VLAN configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: Create vlan - edgeswitch_vlan: - vlan_id: 100 - name: voice - action: present - -- name: Add interfaces to VLAN - edgeswitch_vlan: - vlan_id: 100 - tagged_interfaces: - - 0/1 - - 0/4-0/6 - -- name: setup three vlans and delete the rest - edgeswitch_vlan: - purge: true - aggregate: - - { vlan_id: 1, name: default, auto_untag: true, excluded_interfaces: 0/45-0/48 } - - { vlan_id: 100, name: voice, auto_tag: true } - - { vlan_id: 200, name: video, auto_exclude: true, untagged_interfaces: 0/45-0/48, tagged_interfaces: 0/49 } - -- name: Delete vlan - edgeswitch_vlan: - vlan_id: 100 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - vlan database - - vlan 100 - - vlan name 100 "test vlan" - - exit - - interface 0/1 - - vlan pvid 50 - - vlan participation include 50,100 - - vlan tagging 100 - - vlan participation exclude 200 - - no vlan tagging 200 -""" - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.edgeswitch.edgeswitch import load_config, run_commands -from ansible_collections.community.general.plugins.module_utils.network.edgeswitch.edgeswitch import build_aggregate_spec, map_params_to_obj -from ansible_collections.community.general.plugins.module_utils.network.edgeswitch.edgeswitch_interface import InterfaceConfiguration, merge_interfaces - - -def search_obj_in_list(vlan_id, lst): - for o in lst: - if o['vlan_id'] == vlan_id: - return o - - -def map_vlans_to_commands(want, have, module): - commands = [] - vlans_added = [] - vlans_removed = [] - vlans_names = [] - - for w in want: - vlan_id = w['vlan_id'] - name = w['name'] - state = w['state'] - - obj_in_have = search_obj_in_list(vlan_id, have) - - if state == 'absent': - if obj_in_have: - vlans_removed.append(vlan_id) - - elif state == 'present': - if not obj_in_have: - vlans_added.append(vlan_id) - if name: - vlans_names.append('vlan name {0} "{1}"'.format(vlan_id, name)) - else: - if name: - if name != obj_in_have['name']: - vlans_names.append('vlan name {0} "{1}"'.format(vlan_id, name)) - - if module.params['purge']: - for h in have: - obj_in_want = search_obj_in_list(h['vlan_id'], want) - # you can't delete vlan 1 on Edgeswitch - if not obj_in_want and h['vlan_id'] != '1': - vlans_removed.append(h['vlan_id']) - - if vlans_removed: - commands.append('no vlan {0}'.format(','.join(vlans_removed))) - - if vlans_added: - commands.append('vlan {0}'.format(','.join(vlans_added))) - - if vlans_names: - commands.extend(vlans_names) - - if commands: - commands.insert(0, 'vlan database') - commands.append('exit') - - return commands - - -class VlanInterfaceConfiguration(InterfaceConfiguration): - """ class holding vlan definitions for a given interface - """ - def __init__(self): - InterfaceConfiguration.__init__(self) - self.tagged = [] - self.untagged = [] - self.excluded = [] - - def set_vlan(self, vlan_id, type): - try: - self.tagged.remove(vlan_id) - except ValueError: - pass - - try: - self.untagged.remove(vlan_id) - except ValueError: - pass - - try: - self.excluded.remove(vlan_id) - except ValueError: - pass - - f = getattr(self, type) - f.append(vlan_id) - - def gen_commands(self, port, module): - """ to reduce commands generated by this module - we group vlans changes to have a max of 5 vlan commands by interface - """ - exclude = [] - include = [] - tag = [] - untag = [] - pvid = [] - - for vlan_id in self.excluded: - if vlan_id not in port['forbidden_vlans']: - exclude.append(vlan_id) - - if vlan_id in port['tagged_vlans']: - untag.append(vlan_id) - - for vlan_id in self.untagged: - if vlan_id in port['forbidden_vlans'] or vlan_id not in port['untagged_vlans'] and vlan_id not in port['tagged_vlans']: - include.append(vlan_id) - - if vlan_id in port['tagged_vlans']: - untag.append(vlan_id) - - if vlan_id != port['pvid_mode']: - pvid.append(vlan_id) - - for vlan_id in self.tagged: - if vlan_id not in port['tagged_vlans']: - tag.append(vlan_id) - include.append(vlan_id) - - if include: - self.commands.append('vlan participation include {0}'.format(','.join(include))) - - if pvid: - if len(pvid) > 1: - module.fail_json(msg='{0} can\'t have more than one untagged vlan') - return - self.commands.append('vlan pvid {0}'.format(pvid[0])) - - if untag: - self.commands.append('no vlan tagging {0}'.format(','.join(untag))) - - if tag: - self.commands.append('vlan tagging {0}'.format(','.join(tag))) - - if exclude: - self.commands.append('vlan participation exclude {0}'.format(','.join(exclude))) - - -def set_interfaces_vlan(interfaces_param, interfaces, vlan_id, type): - """ set vlan_id type for each interface in interfaces_param on interfaces - unrange interfaces_param if needed - """ - if interfaces_param: - for i in interfaces_param: - match = re.search(r'(\d+)\/(\d+)-(\d+)\/(\d+)', i) - if match: - group = match.group(1) - start = int(match.group(2)) - end = int(match.group(4)) - for x in range(start, end + 1): - key = '{0}/{1}'.format(group, x) - interfaces[key].set_vlan(vlan_id, type) - else: - interfaces[i].set_vlan(vlan_id, type) - - -def map_interfaces_to_commands(want, ports, module): - commands = list() - - # generate a configuration for each interface - interfaces = {} - for key, value in ports.items(): - interfaces[key] = VlanInterfaceConfiguration() - - for w in want: - state = w['state'] - if state != 'present': - continue - - auto_tag = w['auto_tag'] - auto_untag = w['auto_untag'] - auto_exclude = w['auto_exclude'] - vlan_id = w['vlan_id'] - tagged_interfaces = w['tagged_interfaces'] - untagged_interfaces = w['untagged_interfaces'] - excluded_interfaces = w['excluded_interfaces'] - - # set the default type, if any - for key, value in ports.items(): - if auto_tag: - interfaces[key].tagged.append(vlan_id) - elif auto_exclude: - interfaces[key].excluded.append(vlan_id) - elif auto_untag: - interfaces[key].untagged.append(vlan_id) - - # set explicit definitions - set_interfaces_vlan(tagged_interfaces, interfaces, vlan_id, 'tagged') - set_interfaces_vlan(untagged_interfaces, interfaces, vlan_id, 'untagged') - set_interfaces_vlan(excluded_interfaces, interfaces, vlan_id, 'excluded') - - # generate commands for each interface - for i, interface in interfaces.items(): - port = ports[i] - interface.gen_commands(port, module) - - # reduce them using range syntax when possible - interfaces = merge_interfaces(interfaces) - - # final output - for i, interface in interfaces.items(): - if len(interface.commands) > 0: - commands.append('interface {0}'.format(i)) - commands.extend(interface.commands) - - return commands - - -def parse_vlan_brief(vlan_out): - have = [] - for line in vlan_out.split('\n'): - obj = re.match(r'(?P\d+)\s+(?P[^\s]+)\s+', line) - if obj: - have.append(obj.groupdict()) - return have - - -def unrange(vlans): - res = [] - for vlan in vlans: - match = re.match(r'(\d+)-(\d+)', vlan) - if match: - start = int(match.group(1)) - end = int(match.group(2)) - for vlan_id in range(start, end + 1): - res.append(str(vlan_id)) - else: - res.append(vlan) - return res - - -def parse_interfaces_switchport(cmd_out): - ports = dict() - objs = re.findall( - r'Port: (\d+\/\d+)\n' - 'VLAN Membership Mode:(.*)\n' - 'Access Mode VLAN:(.*)\n' - 'General Mode PVID:(.*)\n' - 'General Mode Ingress Filtering:(.*)\n' - 'General Mode Acceptable Frame Type:(.*)\n' - 'General Mode Dynamically Added VLANs:(.*)\n' - 'General Mode Untagged VLANs:(.*)\n' - 'General Mode Tagged VLANs:(.*)\n' - 'General Mode Forbidden VLANs:(.*)\n', cmd_out) - for o in objs: - port = { - 'interface': o[0], - 'pvid_mode': o[3].replace("(default)", "").strip(), - 'untagged_vlans': unrange(o[7].strip().split(',')), - 'tagged_vlans': unrange(o[8].strip().split(',')), - 'forbidden_vlans': unrange(o[9].strip().split(',')) - } - ports[port['interface']] = port - return ports - - -def map_ports_to_obj(module): - return parse_interfaces_switchport(run_commands(module, ['show interfaces switchport'])[0]) - - -def map_config_to_obj(module): - return parse_vlan_brief(run_commands(module, ['show vlan brief'])[0]) - - -def check_params(module, want): - """ Deeper checks on parameters - """ - def check_parmams_interface(interfaces): - if interfaces: - for i in interfaces: - match = re.search(r'(\d+)\/(\d+)-(\d+)\/(\d+)', i) - if match: - if match.group(1) != match.group(3): - module.fail_json(msg="interface range must be within same group: " + i) - else: - match = re.search(r'(\d+)\/(\d+)', i) - if not match: - module.fail_json(msg="wrong interface format: " + i) - - for w in want: - auto_tag = w['auto_tag'] - auto_untag = w['auto_untag'] - auto_exclude = w['auto_exclude'] - - c = 0 - if auto_tag: - c = c + 1 - - if auto_untag: - c = c + 1 - - if auto_exclude: - c = c + 1 - - if c > 1: - module.fail_json(msg="parameters are mutually exclusive: auto_tag, auto_untag, auto_exclude") - return - - check_parmams_interface(w['tagged_interfaces']) - check_parmams_interface(w['untagged_interfaces']) - check_parmams_interface(w['excluded_interfaces']) - w['vlan_id'] = str(w['vlan_id']) - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - vlan_id=dict(type='int'), - name=dict(), - tagged_interfaces=dict(type='list'), - untagged_interfaces=dict(type='list'), - excluded_interfaces=dict(type='list'), - auto_tag=dict(type='bool'), - auto_exclude=dict(type='bool'), - auto_untag=dict(type='bool'), - state=dict(default='present', - choices=['present', 'absent']) - ) - - argument_spec = build_aggregate_spec( - element_spec, - ['vlan_id'], - dict(purge=dict(default=False, type='bool')) - ) - - required_one_of = [['vlan_id', 'aggregate']] - mutually_exclusive = [ - ['vlan_id', 'aggregate'], - ['auto_tag', 'auto_untag', 'auto_exclude']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - result = {'changed': False} - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - check_params(module, want) - - # vlans are not created/deleted in configure mode - commands = map_vlans_to_commands(want, have, module) - result['commands'] = commands - - if commands: - if not module.check_mode: - run_commands(module, commands, check_rc=False) - result['changed'] = True - - ports = map_ports_to_obj(module) - - # interfaces vlan are set in configure mode - commands = map_interfaces_to_commands(want, ports, module) - result['commands'].extend(commands) - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/enos/enos_command.py b/plugins/modules/network/enos/enos_command.py deleted file mode 100644 index 2da3ebe868..0000000000 --- a/plugins/modules/network/enos/enos_command.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Module to execute ENOS Commands on Lenovo Switches. -# Lenovo Networking -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: enos_command -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Run arbitrary commands on Lenovo ENOS devices -description: - - Sends arbitrary commands to an ENOS node and returns the results - read from the device. The C(enos_command) 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. -extends_documentation_fragment: -- community.general.enos - -options: - commands: - description: - - List of commands to send to the remote 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 retires as expired. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - 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. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. ---- -vars: - cli: - host: "{{ inventory_hostname }}" - port: 22 - username: admin - password: admin - timeout: 30 - ---- -- name: test contains operator - enos_command: - commands: - - show version - - show system memory - wait_for: - - "result[0] contains 'Lenovo'" - - "result[1] contains 'MemFree'" - provider: "{{ cli }}" - register: result - -- assert: - that: - - "result.changed == false" - - "result.stdout is defined" - -- name: get output for single command - enos_command: - commands: ['show version'] - provider: "{{ cli }}" - register: result - -- assert: - that: - - "result.changed == false" - - "result.stdout is defined" - -- name: get output for multiple commands - enos_command: - commands: - - show version - - show interface information - provider: "{{ cli }}" - register: result - -- assert: - that: - - "result.changed == false" - - "result.stdout is defined" - - "result.stdout | length == 2" -""" - -RETURN = """ -stdout: - description: the set of responses from the commands - returned: always - type: list - sample: ['...', '...'] -stdout_lines: - description: The value of stdout split into a list - returned: always - type: list - sample: [['...', '...'], ['...'], ['...']] -failed_conditions: - description: the conditionals that failed - returned: failed - type: list - sample: ['...', '...'] -""" - -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.enos.enos import run_commands, check_args -from ansible_collections.community.general.plugins.module_utils.network.enos.enos import enos_argument_spec -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 main(): - spec = dict( - # { command: , prompt: , response: } - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - spec.update(enos_argument_spec) - - module = AnsibleModule(argument_spec=spec, supports_check_mode=True) - result = {'changed': False} - - wait_for = module.params['wait_for'] or list() - conditionals = [Conditional(c) for c in wait_for] - - commands = module.params['commands'] - 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/plugins/modules/network/enos/enos_config.py b/plugins/modules/network/enos/enos_config.py deleted file mode 100644 index 3aecf6b7c5..0000000000 --- a/plugins/modules/network/enos/enos_config.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Module to configure Lenovo Switches. -# Lenovo Networking -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: enos_config -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Manage Lenovo ENOS configuration sections -description: - - Lenovo ENOS configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with ENOS configuration sections in - a deterministic way. -extends_documentation_fragment: -- community.general.enos - -notes: - - Tested against ENOS 8.4.1 -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. - aliases: ['commands'] - parents: - description: - - The ordered set of parents that uniquely identify the section - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is - mutually exclusive with I(lines), I(parents). - 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. - 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. - 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. - 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. - default: line - choices: ['line', 'block', 'config'] - 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. - 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' - comment: - description: - - Allows a commit description to be specified to be included - when the configuration is committed. If the configuration is - not changed or committed, this argument is ignored. - default: 'configured by enos_config' - admin: - description: - - Enters into administration configuration mode for making config - changes to the device. - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- name: configure top level configuration - enos_config: - "lines: hostname {{ inventory_hostname }}" - -- name: configure interface settings - enos_config: - lines: - - enable - - ip ospf enable - parents: interface ip 13 - -- name: load a config from disk and replace the current config - enos_config: - src: config.cfg - backup: yes - -- name: configurable backup path - enos_config: - src: config.cfg - 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: Only when lines is specified. - type: list - sample: ['...', '...'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/enos01.2016-07-16@22:28:34 -""" -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.enos.enos import load_config, get_config -from ansible_collections.community.general.plugins.module_utils.network.enos.enos import enos_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.enos.enos import check_args -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - - -DEFAULT_COMMIT_COMMENT = 'configured by enos_config' - - -def get_running_config(module): - contents = module.params['config'] - if not contents: - contents = get_config(module) - return NetworkConfig(indent=1, contents=contents) - - -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() - candidate.add(module.params['lines'], parents=parents) - return candidate - - -def run(module, result): - match = module.params['match'] - replace = module.params['replace'] - replace_config = replace == 'config' - path = module.params['parents'] - comment = module.params['comment'] - admin = module.params['admin'] - check_mode = module.check_mode - - candidate = get_candidate(module) - - if match != 'none' and replace != 'config': - contents = get_running_config(module) - configobj = NetworkConfig(contents=contents, indent=1) - commands = candidate.difference(configobj, path=path, match=match, - replace=replace) - else: - commands = candidate.items - - if commands: - commands = dumps(commands, 'commands').split('\n') - - if any((module.params['lines'], module.params['src'])): - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - - diff = load_config(module, commands) - if diff: - result['diff'] = dict(prepared=diff) - result['changed'] = True - - -def main(): - """main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block', 'config']), - - config=dict(), - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - comment=dict(default=DEFAULT_COMMIT_COMMENT), - admin=dict(type='bool', default=False) - ) - - argument_spec.update(enos_argument_spec) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('replace', 'config', ['src'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - warnings = list() - check_args(module, warnings) - - result = dict(changed=False, warnings=warnings) - - if module.params['backup']: - result['__backup__'] = get_config(module) - - run(module, result) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/enos/enos_facts.py b/plugins/modules/network/enos/enos_facts.py deleted file mode 100644 index dda2653101..0000000000 --- a/plugins/modules/network/enos/enos_facts.py +++ /dev/null @@ -1,507 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Module to Collect facts from Lenovo Switches running Lenovo ENOS commands -# Lenovo Networking -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: enos_facts -author: "Anil Kumar Muraleedharan (@amuraleedhar)" -short_description: Collect facts from remote devices running Lenovo ENOS -description: - - Collects a base set of device facts from a remote Lenovo device - running on ENOS. This module prepends all of the - base network fact keys with C(ansible_net_). 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: -- community.general.enos - -notes: - - Tested against ENOS 8.4.1 -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: '!config' -''' -EXAMPLES = ''' -Tasks: The following are examples of using the module enos_facts. ---- -- name: Test Enos Facts - enos_facts: - provider={{ cli }} - - vars: - cli: - host: "{{ inventory_hostname }}" - port: 22 - username: admin - password: admin - transport: cli - timeout: 30 - authorize: True - auth_pass: - ---- -# Collect all facts from the device -- enos_facts: - gather_subset: all - provider: "{{ cli }}" - -# Collect only the config and default facts -- enos_facts: - gather_subset: - - config - provider: "{{ cli }}" - -# Do not collect hardware facts -- enos_facts: - gather_subset: - - "!hardware" - provider: "{{ cli }}" - -''' -RETURN = ''' - ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list -# default - ansible_net_model: - description: The model name returned from the Lenovo ENOS device - returned: always - type: str - ansible_net_serialnum: - description: The serial number of the Lenovo ENOS device - returned: always - type: str - ansible_net_version: - description: The ENOS operating system version running on the remote device - returned: always - type: str - ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - ansible_net_image: - description: Indicates the active image for the device - returned: always - type: str -# hardware - ansible_net_memfree_mb: - description: The available free 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. - This gives information on description, mac address, mtu, speed, - duplex and operstatus - returned: when interfaces is configured - type: dict - ansible_net_neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -''' - -import re - -from ansible_collections.community.general.plugins.module_utils.network.enos.enos import run_commands, enos_argument_spec, check_args -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible.module_utils.six.moves import zip - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - self.PERSISTENT_COMMAND_TIMEOUT = 60 - - 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', 'show run'] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - data_run = self.responses[1] - if data: - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - self.facts['model'] = self.parse_model(data) - self.facts['image'] = self.parse_image(data) - if data_run: - self.facts['hostname'] = self.parse_hostname(data_run) - - def parse_version(self, data): - match = re.search(r'^Software Version (.*?) ', data, re.M | re.I) - if match: - return match.group(1) - - def parse_hostname(self, data_run): - for line in data_run.split('\n'): - line = line.strip() - match = re.match(r'hostname (.*?)', line, re.M | re.I) - if match: - hosts = line.split() - hostname = hosts[1].strip('\"') - return hostname - return "NA" - - def parse_model(self, data): - match = re.search(r'^Lenovo RackSwitch (\S+)', data, re.M | re.I) - if match: - return match.group(1) - - def parse_image(self, data): - match = re.search(r'(.*) image1(.*)', data, re.M | re.I) - if match: - return "Image1" - else: - return "Image2" - - def parse_serialnum(self, data): - match = re.search(r'^Switch Serial No: (\S+)', data, re.M | re.I) - if match: - return match.group(1) - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show system memory' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.run(['show system memory']) - data = to_text(data, errors='surrogate_or_strict').strip() - data = data.replace(r"\n", "\n") - if data: - self.facts['memtotal_mb'] = self.parse_memtotal(data) - self.facts['memfree_mb'] = self.parse_memfree(data) - - def parse_memtotal(self, data): - match = re.search(r'^MemTotal:\s*(.*) kB', data, re.M | re.I) - if match: - return int(match.group(1)) / 1024 - - def parse_memfree(self, data): - match = re.search(r'^MemFree:\s*(.*) kB', data, re.M | re.I) - if match: - return int(match.group(1)) / 1024 - - -class Config(FactsBase): - - COMMANDS = ['show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = ['show interface status'] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - data1 = self.run(['show interface status']) - data1 = to_text(data1, errors='surrogate_or_strict').strip() - data1 = data1.replace(r"\n", "\n") - data2 = self.run(['show lldp port']) - data2 = to_text(data2, errors='surrogate_or_strict').strip() - data2 = data2.replace(r"\n", "\n") - lines1 = None - lines2 = None - if data1: - lines1 = self.parse_interfaces(data1) - if data2: - lines2 = self.parse_interfaces(data2) - if lines1 is not None and lines2 is not None: - self.facts['interfaces'] = self.populate_interfaces(lines1, lines2) - data3 = self.run(['show lldp remote-device port']) - data3 = to_text(data3, errors='surrogate_or_strict').strip() - data3 = data3.replace(r"\n", "\n") - - lines3 = None - if data3: - lines3 = self.parse_neighbors(data3) - if lines3 is not None: - self.facts['neighbors'] = self.populate_neighbors(lines3) - - data4 = self.run(['show interface ip']) - data4 = data4[0].split('\n') - lines4 = None - if data4: - lines4 = self.parse_ipaddresses(data4) - ipv4_interfaces = self.set_ipv4_interfaces(lines4) - self.facts['all_ipv4_addresses'] = ipv4_interfaces - ipv6_interfaces = self.set_ipv6_interfaces(lines4) - self.facts['all_ipv6_addresses'] = ipv6_interfaces - - def parse_ipaddresses(self, data4): - parsed = list() - for line in data4: - if len(line) == 0: - continue - else: - line = line.strip() - if len(line) == 0: - continue - match = re.search(r'IP4', line, re.M | re.I) - if match: - key = match.group() - parsed.append(line) - match = re.search(r'IP6', line, re.M | re.I) - if match: - key = match.group() - parsed.append(line) - return parsed - - def set_ipv4_interfaces(self, line4): - ipv4_addresses = list() - for line in line4: - ipv4Split = line.split() - if ipv4Split[1] == "IP4": - ipv4_addresses.append(ipv4Split[2]) - return ipv4_addresses - - def set_ipv6_interfaces(self, line4): - ipv6_addresses = list() - for line in line4: - ipv6Split = line.split() - if ipv6Split[1] == "IP6": - ipv6_addresses.append(ipv6Split[2]) - return ipv6_addresses - - def populate_neighbors(self, lines3): - neighbors = dict() - for line in lines3: - neighborSplit = line.split("|") - innerData = dict() - innerData['Remote Chassis ID'] = neighborSplit[2].strip() - innerData['Remote Port'] = neighborSplit[3].strip() - sysName = neighborSplit[4].strip() - if sysName is not None: - innerData['Remote System Name'] = neighborSplit[4].strip() - else: - innerData['Remote System Name'] = "NA" - neighbors[neighborSplit[0].strip()] = innerData - return neighbors - - def populate_interfaces(self, lines1, lines2): - interfaces = dict() - for line1, line2 in zip(lines1, lines2): - line = line1 + " " + line2 - intfSplit = line.split() - innerData = dict() - innerData['description'] = intfSplit[6].strip() - innerData['macaddress'] = intfSplit[8].strip() - innerData['mtu'] = intfSplit[9].strip() - innerData['speed'] = intfSplit[1].strip() - innerData['duplex'] = intfSplit[2].strip() - innerData['operstatus'] = intfSplit[5].strip() - if("up" not in intfSplit[5].strip()) and ("down" not in intfSplit[5].strip()): - innerData['description'] = intfSplit[7].strip() - innerData['macaddress'] = intfSplit[9].strip() - innerData['mtu'] = intfSplit[10].strip() - innerData['operstatus'] = intfSplit[6].strip() - interfaces[intfSplit[0].strip()] = innerData - return interfaces - - def parse_neighbors(self, neighbors): - parsed = list() - for line in neighbors.split('\n'): - if len(line) == 0: - continue - else: - line = line.strip() - match = re.match(r'^([0-9]+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(INT+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(EXT+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(MGT+)', line) - if match: - key = match.group(1) - parsed.append(line) - return parsed - - def parse_interfaces(self, data): - parsed = list() - for line in data.split('\n'): - if len(line) == 0: - continue - else: - line = line.strip() - match = re.match(r'^([0-9]+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(INT+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(EXT+)', line) - if match: - key = match.group(1) - parsed.append(line) - match = re.match(r'^(MGT+)', line) - if match: - key = match.group(1) - parsed.append(line) - return parsed - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - -PERSISTENT_COMMAND_TIMEOUT = 60 - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - argument_spec.update(enos_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/plugins/modules/network/eric_eccli/eric_eccli_command.py b/plugins/modules/network/eric_eccli/eric_eccli_command.py deleted file mode 100644 index 8b226feee0..0000000000 --- a/plugins/modules/network/eric_eccli/eric_eccli_command.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2019 Ericsson AB. -# 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: eric_eccli_command -author: Ericsson IPOS OAM team (@itercheng) -short_description: Run commands on remote devices running ERICSSON ECCLI -description: - - Sends arbitrary commands to an ERICSSON eccli node and returns the results - read from the device. This module includes an - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. - - This module also support running commands in configuration mode - in raw command style. -options: - commands: - description: - - List of commands to send to the remote ECCLI device over the - configured provider. The resulting output from the command - is returned. If the I(wait_for) argument is provided, the - module is not returned until the condition is satisfied or - the number of retries has expired. If a command sent to the - device requires answering a prompt, it is possible to pass - a dict containing I(command), I(answer) and I(prompt). - Common answers are 'y' or "\\r" (carriage return, must be - double quotes). See examples. - type: list - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - type: list - aliases: ['waitfor'] - match: - description: - - The I(match) argument is used in conjunction with the - I(wait_for) argument to specify the match policy. Valid - values are C(all) or C(any). If the value is set to C(all) - then all conditionals in the wait_for must be satisfied. If - the value is set to C(any) then only one of the values must be - satisfied. - type: str - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - type: int - default: 10 - interval: - description: - - Configures the interval in seconds to wait between retries - of the command. If the command does not pass the specified - conditions, the interval indicates how long to wait before - trying the command again. - type: int - default: 1 -notes: - - Tested against IPOS 19.3 - - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide ` - - For more information on using Ansible to manage Ericsson devices see the Ericsson documents. - - "Starting with Ansible 2.5 we recommend using C(connection: network_cli)." - - For more information please see the L(ERIC_ECCLI Platform Options guide,../network/user_guide/platform_eric_eccli.html). -""" - -EXAMPLES = r""" -tasks: - - name: run show version on remote devices - eric_eccli_command: - commands: show version - - - name: run show version and check to see if output contains IPOS - eric_eccli_command: - commands: show version - wait_for: result[0] contains IPOS - - - name: run multiple commands on remote nodes - eric_eccli_command: - commands: - - show version - - show running-config interfaces - - - name: run multiple commands and evaluate the output - eric_eccli_command: - commands: - - show version - - show running-config interfaces - wait_for: - - result[0] contains IPOS - - result[1] contains management -""" - -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: ['...', '...'] -""" -import re -import time - -from ansible_collections.community.general.plugins.module_utils.network.eric_eccli.eric_eccli import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import transform_commands -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional -from ansible.module_utils.six import string_types - - -def parse_commands(module, warnings): - commands = transform_commands(module) - - for item in list(commands): - if module.check_mode: - if item['command'].startswith('conf'): - warnings.append( - 'only non-config commands are supported when using check mode, not ' - 'executing %s' % item['command'] - ) - commands.remove(item) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list', aliases=['waitfor']), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - 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() - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/exos/exos_command.py b/plugins/modules/network/exos/exos_command.py deleted file mode 100644 index 3282c512a7..0000000000 --- a/plugins/modules/network/exos/exos_command.py +++ /dev/null @@ -1,220 +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 -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: exos_command -author: "Rafael D. Vencioneck (@rdvencioneck)" -short_description: Run commands on remote devices running Extreme EXOS -description: - - Sends arbitrary commands to an Extreme EXOS 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 configuration commands. - Please use M(exos_config) to configure EXOS devices. -notes: - - If a command sent to the device requires answering a prompt, it is possible - to pass a dict containing I(command), I(answer) and I(prompt). See examples. -options: - commands: - description: - - List of commands to send to the remote EXOS 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. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - 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. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -tasks: - - name: run show version on remote devices - exos_command: - commands: show version - - name: run show version and check to see if output contains ExtremeXOS - exos_command: - commands: show version - wait_for: result[0] contains ExtremeXOS - - name: run multiple commands on remote nodes - exos_command: - commands: - - show version - - show ports no-refresh - - name: run multiple commands and evaluate the output - exos_command: - commands: - - show version - - show ports no-refresh - wait_for: - - result[0] contains ExtremeXOS - - result[1] contains 20 - - name: run command that requires answering a prompt - exos_command: - commands: - - command: 'clear license-info' - prompt: 'Are you sure.*' - answer: 'Yes' -""" - -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: ['...', '...'] -""" -import re -import time - -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import run_commands -from ansible.module_utils.basic import AnsibleModule -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 item in list(commands): - command_split = re.match(r'^(\w*)(.*)$', item['command']) - 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'] - ) - commands.remove(item) - elif command_split and command_split.group(1) not in ('check', 'clear', 'debug', 'history', - 'ls', 'mrinfo', 'mtrace', 'nslookup', - 'ping', 'rtlookup', 'show', 'traceroute'): - module.fail_json( - msg='some commands were not recognized. exos_command can only run read-only' - 'commands. For configuration commands, please use exos_config instead' - ) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - 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 be 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/plugins/modules/network/exos/exos_config.py b/plugins/modules/network/exos/exos_config.py deleted file mode 100644 index d9ab02fd30..0000000000 --- a/plugins/modules/network/exos/exos_config.py +++ /dev/null @@ -1,436 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2018, Extreme Networks 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) -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: exos_config -author: "Lance Richardson (@hlrichardson)" -short_description: Manage Extreme Networks EXOS configuration sections -description: - - Extreme EXOS configurations use a simple flat text file syntax. - This module provides an implementation for working with EXOS - configuration lines in a deterministic way. -notes: - - Tested against EXOS version 22.6.0b19 -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. - aliases: ['commands'] - 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). - 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. - 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. - 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. - 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. - default: line - choices: ['line', 'block'] - 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' - running_config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(running_config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - aliases: ['config'] - defaults: - description: - - This argument specifies whether or not to collect all defaults - when getting the remote device running config. When enabled, - the module will get the current config by issuing the command - C(show running-config all). - type: bool - default: 'no' - save_when: - description: - - When changes are made to the device running-configuration, the - changes are not copied to non-volatile storage by default. Using - this argument will change that behavior. If the argument is set to - I(always), then the running-config will always be copied to the - startup-config and the I(modified) flag will always be set to - True. If the argument is set to I(modified), then the running-config - will only be copied to the startup-config if it has changed since - the last save to startup-config. If the argument is set to - I(never), the running-config will never be copied to the - startup-config. If the argument is set to I(changed), then the running-config - will only be copied to the startup-config if the task has made a change. - default: never - choices: ['always', 'never', 'modified', 'changed'] - diff_against: - description: - - When using the C(ansible-playbook --diff) command line argument - the module can generate diffs against different sources. - - When this option is configure as I(startup), the module will return - the diff of the running-config against the startup-config. - - When this option is configured as I(intended), the module will - return the diff of the running-config against the configuration - provided in the C(intended_config) argument. - - When this option is configured as I(running), the module will - return the before and after diff of the running-config with respect - to any changes made to the device configuration. - default: running - choices: ['running', 'startup', 'intended'] - diff_ignore_lines: - description: - - Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - intended_config: - description: - - The C(intended_config) provides the master configuration that - the node should conform to and is used to check the final - running-config against. This argument will not modify any settings - on the remote device and is strictly used to check the compliance - of the current device's configuration against. When specifying this - argument, the task should also modify the C(diff_against) value and - set it to I(intended). - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- name: configure SNMP system name - exos_config: - lines: configure snmp sysName "{{ inventory_hostname }}" - -- name: configure interface settings - exos_config: - lines: - - configure ports 2 description-string "Master Uplink" - backup: yes - -- name: check the running-config against master config - exos_config: - diff_against: intended - intended_config: "{{ lookup('file', 'master.cfg') }}" - -- name: check the startup-config against the running-config - exos_config: - diff_against: startup - diff_ignore_lines: - - ntp clock .* - -- name: save running to startup when modified - exos_config: - save_when: modified - -- name: configurable backup path - exos_config: - lines: - - configure ports 2 description-string "Master Uplink" - 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: ['switch-attributes hostname foo', 'router ospf', 'area 0'] -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['create vlan "foo"', 'configure snmp sysName "x620-red"'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/x870_config.2018-08-08@15:00:21 - -""" -import re - -from ansible_collections.community.general.plugins.module_utils.network.exos.exos import run_commands, get_config, load_config, get_diff -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list - -__metaclass__ = type - - -def get_running_config(module, current_config=None, flags=None): - contents = module.params['running_config'] - if not contents: - if current_config: - contents = current_config.config_text - else: - contents = get_config(module, flags=flags) - return contents - - -def get_startup_config(module, flags=None): - reply = run_commands(module, {'command': 'show switch', 'output': 'text'}) - match = re.search(r'Config Selected: +(\S+)\.cfg', to_text(reply, errors='surrogate_or_strict').strip(), re.MULTILINE) - if match: - cfgname = match.group(1).strip() - command = ' '.join(['debug cfgmgr show configuration file', cfgname]) - if flags: - command += ' '.join(to_list(flags)).strip() - reply = run_commands(module, {'command': command, 'output': 'text'}) - data = reply[0] - else: - data = '' - return data - - -def get_candidate(module): - candidate = NetworkConfig(indent=1) - - if module.params['src']: - candidate.load(module.params['src']) - elif module.params['lines']: - candidate.add(module.params['lines']) - candidate = dumps(candidate, 'raw') - return candidate - - -def save_config(module, result): - result['changed'] = True - if not module.check_mode: - command = {"command": "save configuration", - "prompt": "Do you want to save configuration", "answer": "y"} - run_commands(module, command) - else: - module.warn('Skipping command `save configuration` ' - 'due to check_mode. Configuration not copied to ' - 'non-volatile storage') - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - - running_config=dict(aliases=['config']), - intended_config=dict(), - - defaults=dict(type='bool', default=False), - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - - save_when=dict(choices=['always', 'never', 'modified', 'changed'], default='never'), - - diff_against=dict(choices=['startup', 'intended', 'running'], default='running'), - diff_ignore_lines=dict(type='list'), - ) - - mutually_exclusive = [('lines', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('diff_against', 'intended', ['intended_config'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - if warnings: - result['warnings'] = warnings - - config = None - flags = ['detail'] if module.params['defaults'] else [] - diff_ignore_lines = module.params['diff_ignore_lines'] - - if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'): - contents = get_config(module, flags=flags) - config = NetworkConfig(indent=1, contents=contents) - if module.params['backup']: - result['__backup__'] = contents - - if any((module.params['lines'], module.params['src'])): - match = module.params['match'] - replace = module.params['replace'] - - candidate = get_candidate(module) - running = get_running_config(module, config) - - try: - response = get_diff(module, candidate=candidate, running=running, diff_match=match, diff_ignore_lines=diff_ignore_lines, diff_replace=replace) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - config_diff = response.get('config_diff') - - if config_diff: - commands = config_diff.split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - result['updates'] = commands - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - if commands: - load_config(module, commands) - - result['changed'] = True - - running_config = None - startup_config = None - - if module.params['save_when'] == 'always': - save_config(module, result) - elif module.params['save_when'] == 'modified': - running = get_running_config(module) - startup = get_startup_config(module) - - running_config = NetworkConfig(indent=1, contents=running, ignore_lines=diff_ignore_lines) - startup_config = NetworkConfig(indent=1, contents=startup, ignore_lines=diff_ignore_lines) - - if running_config.sha1 != startup_config.sha1: - save_config(module, result) - elif module.params['save_when'] == 'changed' and result['changed']: - save_config(module, result) - - if module._diff: - if not running_config: - contents = get_running_config(module) - else: - contents = running_config.config_text - - # recreate the object in order to process diff_ignore_lines - running_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if module.params['diff_against'] == 'running': - if module.check_mode: - module.warn("unable to perform diff against running-config due to check mode") - contents = None - else: - contents = config.config_text - - elif module.params['diff_against'] == 'startup': - if not startup_config: - contents = get_startup_config(module) - else: - contents = startup_config.config_text - - elif module.params['diff_against'] == 'intended': - contents = module.params['intended_config'] - - if contents is not None: - base_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if running_config.sha1 != base_config.sha1: - if module.params['diff_against'] == 'intended': - before = running_config - after = base_config - elif module.params['diff_against'] in ('startup', 'running'): - before = base_config - after = running_config - - result.update({ - 'changed': True, - 'diff': {'before': str(before), 'after': str(after)} - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/exos/exos_facts.py b/plugins/modules/network/exos/exos_facts.py deleted file mode 100644 index b5f79708d8..0000000000 --- a/plugins/modules/network/exos/exos_facts.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: exos_facts -author: - - "Lance Richardson (@hlrichardson)" - - "Ujwal Koamrla (@ujwalkomarla)" -short_description: Collect facts from devices running Extreme EXOS -description: - - Collects a base set of device facts from a remote device that - is running EXOS. This module prepends all of the base network - fact keys with C(ansible_net_). The facts module will - always collect a base set of facts from the device and can - enable or disable collection of additional facts. -notes: - - Tested against EXOS 22.5.1.7 -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - type: list - default: ['!config'] - gather_network_resources: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all and the resources like interfaces, vlans etc. - 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. - Valid subsets are 'all', 'lldp_global'. - type: list -''' - -EXAMPLES = """ - - name: Gather all legacy facts - exos_facts: - gather_subset: all - - - name: Gather only the config and default facts - exos_facts: - gather_subset: config - - - name: do not gather hardware facts - exos_facts: - gather_subset: "!hardware" - - - name: Gather legacy and resource facts - exos_facts: - gather_subset: all - gather_network_resources: all - - - name: Gather only the lldp global resource facts and no legacy facts - exos_facts: - gather_subset: - - '!all' - - '!min' - gather_network_resource: - - lldp_global - - - name: Gather lldp global resource and minimal legacy facts - exos_facts: - gather_subset: min - gather_network_resource: lldp_global -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -ansible_net_gather_network_resources: - description: The list of fact for network resource subsets collected from the device - returned: when the resource is configured - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - -# hardware -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 Primary 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 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.facts.facts import FactsArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.facts.facts import Facts - - -def main(): - """Main entry point for AnsibleModule - """ - argument_spec = FactsArgs.argument_spec - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - warnings = ['default value for `gather_subset` ' - 'will be changed to `min` from `!config` v2.11 onwards'] - - result = Facts(module).get_facts() - - ansible_facts, additional_warnings = result - warnings.extend(additional_warnings) - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/exos/exos_l2_interfaces.py b/plugins/modules/network/exos/exos_l2_interfaces.py deleted file mode 100644 index 199e53f38f..0000000000 --- a/plugins/modules/network/exos/exos_l2_interfaces.py +++ /dev/null @@ -1,1136 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The module file for exos_l2_interfaces -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} - -DOCUMENTATION = ''' ---- -module: exos_l2_interfaces -short_description: Manage L2 interfaces on Extreme Networks EXOS devices. -description: This module provides declarative management of L2 interfaces on Extreme Networks EXOS network devices. -author: Jayalakshmi Viswanathan (@jayalakshmiV) -notes: - - Tested against EXOS 30.2.1.8 - - This module works with connection C(httpapi). - See L(EXOS Platform Options,../network/user_guide/platform_exos.html) -options: - config: - description: A dictionary of L2 interfaces options - type: list - elements: dict - suboptions: - name: - description: - - Name of the interface - type: str - required: True - access: - description: - - Switchport mode access command to configure the interface as a layer 2 access. - type: dict - suboptions: - vlan: - description: - - Configure given VLAN in access port. It's used as the access VLAN ID. - type: int - trunk: - description: - - Switchport mode trunk command to configure the interface as a Layer 2 trunk. - type: dict - suboptions: - native_vlan: - description: - - Native VLAN to be configured in trunk port. It's used as the trunk native VLAN ID. - type: int - trunk_allowed_vlans: - description: - - List of allowed VLANs in a given trunk port. These are the only VLANs that will be configured on the trunk. - type: list - state: - description: - - The state the configuration should be left in - type: str - choices: - - merged - - replaced - - overridden - - deleted - default: merged -''' -EXAMPLES = """ -# Using deleted - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 10 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 1, -# "trunk-vlans": [ -# 10 -# ] -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 10, -# "trunk-vlans": [ -# 20, -# 30 -# ] -# } -# } -# } -# } -# ] -# } -# } - -- name: Delete L2 interface configuration for the given arguments - exos_l2_interfaces: - config: - - name: '3' - state: deleted - -# Module Execution Results: -# ------------------------- -# -# "before": [ -# { -# "access": { -# "vlan": 10 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": null, -# "name": "2", -# "trunk": { -# "native_vlan": 1, -# "trunk_allowed_vlans": [ -# 10 -# ] -# } -# }, -# { -# "access": null, -# "name": "3", -# "trunk": { -# "native_vlan": 10, -# "trunk_allowed_vlans": [ -# 20, -# 30 -# ] -# } -# } -# ], -# -# "requests": [ -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 1, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=3/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# } -# ], -# -# "after": [ -# { -# "access": { -# "vlan": 10 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": null, -# "name": "2", -# "trunk": { -# "native_vlan": 1, -# "trunk_allowed_vlans": [ -# 10 -# ] -# } -# }, -# { -# "access": { -# "vlan": 1 -# }, -# "name": "3", -# "trunk": null -# } -# ] -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 10 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 1, -# "trunk-vlans": [ -# 10 -# ] -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# } -# } -# } -# } -# ] -# } -# } - - -# Using deleted without any config passed -#"(NOTE: This will delete all of configured resource module attributes from each configured interface)" - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 10 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 1, -# "trunk-vlans": [ -# 10 -# ] -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 10, -# "trunk-vlans": [ -# 20, -# 30 -# ] -# } -# } -# } -# } -# ] -# } -# } - -- name: Delete L2 interface configuration for the given arguments - exos_l2_interfaces: - state: deleted - -# Module Execution Results: -# ------------------------- -# -# "before": [ -# { -# "access": { -# "vlan": 10 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": null, -# "name": "2", -# "trunk": { -# "native_vlan": 1, -# "trunk_allowed_vlans": [ -# 10 -# ] -# } -# }, -# { -# "access": null, -# "name": "3", -# "trunk": { -# "native_vlan": 10, -# "trunk_allowed_vlans": [ -# 20, -# 30 -# ] -# } -# } -# ], -# -# "requests": [ -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 1, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# }, -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 1, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=2/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# }, -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 1, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=3/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# } -# ], -# -# "after": [ -# { -# "access": { -# "vlan": 1 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": { -# "vlan": 1 -# }, -# "name": "2", -# "trunk": null -# }, -# { -# "access": { -# "vlan": 1 -# }, -# "name": "3", -# "trunk": null -# } -# ] -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# } -# } -# } -# } -# ] -# } -# } - - -# Using merged - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# }, -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# }, -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# }, -# } -# } -# }, -# ] -# } -# } - -- name: Merge provided configuration with device configuration - exos_l2_interfaces: - config: - - access: - vlan: 10 - name: '1' - - name: '2' - trunk: - trunk_allowed_vlans: 10 - - name: '3' - trunk: - native_vlan: 10 - trunk_allowed_vlans: 20 - state: merged - -# Module Execution Results: -# ------------------------- -# -# "before": [ -# { -# "access": { -# "vlan": 1 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": { -# "vlan": 1 -# }, -# "name": "2", -# "trunk": null -# }, -# { -# "access": { -# "vlan": 1 -# }, -# "name": "3", -# "trunk": null -# } -# ], -# -# "requests": [ -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 10, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# }, -# { -# "data": { -# "openconfig-vlan:config": { -# "trunk-vlans": [10], -# "interface-mode": "TRUNK" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=2/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# }, -# { -# "data": { -# "openconfig-vlan:config": { -# "native-vlan": 10, -# "trunk-vlans": [20], -# "interface-mode": "TRUNK" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=3/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# } -# ], -# -# "after": [ -# { -# "access": { -# "vlan": 10 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": null, -# "name": "2", -# "trunk": { -# "native_vlan": 1, -# "trunk_allowed_vlans": [ -# 10 -# ] -# } -# }, -# { -# "access": null, -# "name": "3", -# "trunk": { -# "native_vlan": 10, -# "trunk_allowed_vlans": [ -# 20 -# ] -# } -# } -# ] -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 10 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 1, -# "trunk-vlans": [ -# 10 -# ] -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 10, -# "trunk-vlans": [ -# 20 -# ] -# } -# } -# } -# }, -# ] -# } -# } - - -# Using overridden - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 10 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 1, -# "trunk-vlans": [ -# 10 -# ] -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 10, -# "trunk-vlans": [ -# 20, -# 30 -# ] -# } -# } -# } -# } -# ] -# } -# } - -- name: Overrride device configuration of all L2 interfaces with provided configuration - exos_l2_interfaces: - config: - - access: - vlan: 10 - name: '2' - state: overridden - -# Module Execution Results: -# ------------------------- -# -# "before": [ -# { -# "access": { -# "vlan": 10 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": null, -# "name": "2", -# "trunk": { -# "native_vlan": 1, -# "trunk_allowed_vlans": [ -# 10 -# ] -# } -# }, -# { -# "access": null, -# "name": "3", -# "trunk": { -# "native_vlan": 10, -# "trunk_allowed_vlans": [ -# 20, -# 30 -# ] -# } -# } -# ], -# -# "requests": [ -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 1, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# }, -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 10, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=2/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# } -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 1, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=3/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# } -# ], -# -# "after": [ -# { -# "access": { -# "vlan": 1 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": { -# "vlan": 10 -# }, -# "name": "2", -# "trunk": null -# }, -# { -# "access": { -# "vlan": 1 -# }, -# "name": "3", -# "trunk": null -# } -# ] -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 10 -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 1 -# } -# } -# } -# } -# ] -# } -# } - - -# Using replaced - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 10 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 20 -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 1, -# "trunk-vlans": [ -# 10 -# ] -# } -# } -# } -# } -# ] -# } -# } - -- name: Replace device configuration of listed L2 interfaces with provided configuration - exos_l2_interfaces: - config: - - access: - vlan: 20 - name: '1' - - name: '2' - trunk: - trunk_allowed_vlans: 10 - - name: '3' - trunk: - native_vlan: 10 - trunk_allowed_vlan: 20,30 - state: replaced - -# Module Execution Results: -# ------------------------- -# -# "before": [ -# { -# "access": { -# "vlan": 10 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": { -# "vlan": 20 -# }, -# "name": "2", -# "trunk": null -# }, -# { -# "access": null, -# "name": "3", -# "trunk": { -# "native_vlan": 1, -# "trunk_allowed_vlans": [ -# 10 -# ] -# } -# } -# ], -# -# "requests": [ -# { -# "data": { -# "openconfig-vlan:config": { -# "access-vlan": 20, -# "interface-mode": "ACCESS" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=1/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# }, -# { -# "data": { -# "openconfig-vlan:config": { -# "trunk-vlans": [10], -# "interface-mode": "TRUNK" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=2/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# }, -# { -# "data": { -# "openconfig-vlan:config": { -# "native-vlan": 10, -# "trunk-vlans": [20, 30] -# "interface-mode": "TRUNK" -# } -# } -# "method": "PATCH", -# "path": "rest/restconf/data/openconfig-interfaces:interfaces/interface=3/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" -# } -# ], -# -# "after": [ -# { -# "access": { -# "vlan": 20 -# }, -# "name": "1", -# "trunk": null -# }, -# { -# "access": null, -# "name": "2", -# "trunk": { -# "native_vlan": null, -# "trunk_allowed_vlans": [ -# 10 -# ] -# } -# }, -# { -# "access": null, -# "name": "3", -# "trunk": { -# "native_vlan": 10, -# "trunk_allowed_vlans": [ -# 20, -# 30 -# ] -# } -# } -# ] -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-interfaces:interfaces/ -# method: GET -# data: -# { -# "openconfig-interfaces:interfaces": { -# "interface": [ -# { -# "name": "1", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "ACCESS", -# "access-vlan": 20 -# } -# } -# } -# }, -# { -# "name": "2", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "trunk-vlans": [ -# 10 -# ] -# } -# } -# } -# }, -# { -# "name": "3", -# "openconfig-if-ethernet:ethernet": { -# "openconfig-vlan:switched-vlan": { -# "config": { -# "interface-mode": "TRUNK", -# "native-vlan": 10, -# "trunk-vlans": [ -# 20, -# 30 -# ] -# } -# } -# } -# } -# ] -# } -# } - - -""" -RETURN = """ -before: - description: The configuration prior to the model invocation. - returned: always - sample: > - The configuration returned will always be in the same format - of the parameters above. - type: list -after: - description: The resulting configuration model invocation. - returned: when changed - sample: > - The configuration returned will always be in the same format - of the parameters above. - type: list -requests: - description: The set of requests pushed to the remote device. - returned: always - type: list - sample: [{"data": "...", "method": "...", "path": "..."}, {"data": "...", "method": "...", "path": "..."}, {"data": "...", "method": "...", "path": "..."}] -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.l2_interfaces.l2_interfaces import L2_interfacesArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.config.l2_interfaces.l2_interfaces import L2_interfaces - - -def main(): - """ - Main entry point for module execution - - :returns: the result form module invocation - """ - required_if = [('state', 'merged', ('config', )), - ('state', 'replaced', ('config', ))] - module = AnsibleModule(argument_spec=L2_interfacesArgs.argument_spec, required_if=required_if, - supports_check_mode=True) - - result = L2_interfaces(module).execute_module() - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/exos/exos_lldp_global.py b/plugins/modules/network/exos/exos_lldp_global.py deleted file mode 100644 index 8ed421aa2b..0000000000 --- a/plugins/modules/network/exos/exos_lldp_global.py +++ /dev/null @@ -1,429 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The module file for exos_lldp_global -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} - -DOCUMENTATION = ''' ---- -module: exos_lldp_global -short_description: Configure and manage Link Layer Discovery Protocol(LLDP) attributes on EXOS platforms. -description: This module configures and manages the Link Layer Discovery Protocol(LLDP) attributes on Extreme Networks EXOS platforms. -author: Ujwal Komarla (@ujwalkomarla) -notes: -- Tested against Extreme Networks EXOS version 30.2.1.8 on x460g2. -- This module works with connection C(httpapi). - See L(EXOS Platform Options,../network/user_guide/platform_exos.html) -options: - config: - description: A dictionary of LLDP options - type: dict - suboptions: - interval: - description: - - Frequency at which LLDP advertisements are sent (in seconds). By default - 30 seconds. - type: int - default: 30 - tlv_select: - description: - - This attribute can be used to specify the TLVs that need to be sent in the LLDP packets. By default, only system name and system description is sent - type: dict - suboptions: - management_address: - description: - - Used to specify the management address in TLV messages - type: bool - port_description: - description: - - Used to specify the port description TLV - type: bool - system_capabilities: - description: - - Used to specify the system capabilities TLV - type: bool - system_description: - description: - - Used to specify the system description TLV - type: bool - default: true - system_name: - description: - - Used to specify the system name TLV - type: bool - default: true - - state: - description: - - The state of the configuration after module completion. - type: str - choices: - - merged - - replaced - - deleted - default: merged -''' -EXAMPLES = """ -# Using merged - - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig_lldp:lldp/config -# method: GET -# data: -# { -# "openconfig_lldp:config": { -# "enabled": true, -# "hello-timer": 30, -# "suppress-tlv-advertisement": [ -# "PORT_DESCRIPTION", -# "SYSTEM_CAPABILITIES", -# "MANAGEMENT_ADDRESS" -# ], -# "system-description": "ExtremeXOS (X460G2-24t-10G4) version 30.2.1.8" -# "system-name": "X460G2-24t-10G4" -# } -# } - -- name: Merge provided LLDP configuration with device configuration - exos_lldp_global: - config: - interval: 10000 - tlv_select: - system_capabilities: true - state: merged - -# Module Execution Results: -# ------------------------- -# -# "before": [ -# { -# "interval": 30, -# "tlv_select": { -# "system_name": true, -# "system_description": true -# "port_description": false, -# "management_address": false, -# "system_capabilities": false -# } -# } -# ] -# -# "requests": [ -# { -# "data": { -# "openconfig_lldp:config": { -# "hello-timer": 10000, -# "suppress-tlv-advertisement": [ -# "PORT_DESCRIPTION", -# "MANAGEMENT_ADDRESS" -# ] -# } -# }, -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig_lldp:lldp/config" -# } -# ] -# -# "after": [ -# { -# "interval": 10000, -# "tlv_select": { -# "system_name": true, -# "system_description": true, -# "port_description": false, -# "management_address": false, -# "system_capabilities": true -# } -# } -# ] - - -# After state: -# ------------- -# path: /rest/restconf/data/openconfig_lldp:lldp/config -# method: GET -# data: -# { -# "openconfig_lldp:config": { -# "enabled": true, -# "hello-timer": 10000, -# "suppress-tlv-advertisement": [ -# "PORT_DESCRIPTION", -# "MANAGEMENT_ADDRESS" -# ], -# "system-description": "ExtremeXOS (X460G2-24t-10G4) version 30.2.1.8" -# "system-name": "X460G2-24t-10G4" -# } -# } - - -# Using replaced - - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig_lldp:lldp/config -# method: GET -# data: -# { -# "openconfig_lldp:config": { -# "enabled": true, -# "hello-timer": 30, -# "suppress-tlv-advertisement": [ -# "PORT_DESCRIPTION", -# "SYSTEM_CAPABILITIES", -# "MANAGEMENT_ADDRESS" -# ], -# "system-description": "ExtremeXOS (X460G2-24t-10G4) version 30.2.1.8" -# "system-name": "X460G2-24t-10G4" -# } -# } - -- name: Replace device configuration with provided LLDP configuration - exos_lldp_global: - config: - interval: 10000 - tlv_select: - system_capabilities: true - state: replaced - -# Module Execution Results: -# ------------------------- -# -# "before": [ -# { -# "interval": 30, -# "tlv_select": { -# "system_name": true, -# "system_description": true -# "port_description": false, -# "management_address": false, -# "system_capabilities": false -# } -# } -# ] -# -# "requests": [ -# { -# "data": { -# "openconfig_lldp:config": { -# "hello-timer": 10000, -# "suppress-tlv-advertisement": [ -# "SYSTEM_NAME", -# "SYSTEM_DESCRIPTION", -# "PORT_DESCRIPTION", -# "MANAGEMENT_ADDRESS" -# ] -# } -# }, -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig_lldp:lldp/config" -# } -# ] -# -# "after": [ -# { -# "interval": 10000, -# "tlv_select": { -# "system_name": false, -# "system_description": false, -# "port_description": false, -# "management_address": false, -# "system_capabilities": true -# } -# } -# ] - - -# After state: -# ------------- -# path: /rest/restconf/data/openconfig_lldp:lldp/config -# method: GET -# data: -# { -# "openconfig_lldp:config": { -# "enabled": true, -# "hello-timer": 10000, -# "suppress-tlv-advertisement": [ -# "SYSTEM_NAME", -# "SYSTEM_DESCRIPTION", -# "PORT_DESCRIPTION", -# "MANAGEMENT_ADDRESS" -# ], -# "system-description": "ExtremeXOS (X460G2-24t-10G4) version 30.2.1.8" -# "system-name": "X460G2-24t-10G4" -# } -# } - - -# Using deleted - - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig_lldp:lldp/config -# method: GET -# data: -# { -# "openconfig_lldp:config": { -# "enabled": true, -# "hello-timer": 10000, -# "suppress-tlv-advertisement": [ -# "SYSTEM_CAPABILITIES", -# "MANAGEMENT_ADDRESS" -# ], -# "system-description": "ExtremeXOS (X460G2-24t-10G4) version 30.2.1.8" -# "system-name": "X460G2-24t-10G4" -# } -# } - -- name: Delete attributes of given LLDP service (This won't delete the LLDP service itself) - exos_lldp_global: - config: - state: deleted - -# Module Execution Results: -# ------------------------- -# -# "before": [ -# { -# "interval": 10000, -# "tlv_select": { -# "system_name": true, -# "system_description": true, -# "port_description": true, -# "management_address": false, -# "system_capabilities": false -# } -# } -# ] -# -# "requests": [ -# { -# "data": { -# "openconfig_lldp:config": { -# "hello-timer": 30, -# "suppress-tlv-advertisement": [ -# "SYSTEM_CAPABILITIES", -# "PORT_DESCRIPTION", -# "MANAGEMENT_ADDRESS" -# ] -# } -# }, -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig_lldp:lldp/config" -# } -# ] -# -# "after": [ -# { -# "interval": 30, -# "tlv_select": { -# "system_name": true, -# "system_description": true, -# "port_description": false, -# "management_address": false, -# "system_capabilities": false -# } -# } -# ] - - -# After state: -# ------------- -# path: /rest/restconf/data/openconfig_lldp:lldp/config -# method: GET -# data: -# { -# "openconfig_lldp:config": { -# "enabled": true, -# "hello-timer": 30, -# "suppress-tlv-advertisement": [ -# "SYSTEM_CAPABILITIES", -# "PORT_DESCRIPTION", -# "MANAGEMENT_ADDRESS" -# ], -# "system-description": "ExtremeXOS (X460G2-24t-10G4) version 30.2.1.8" -# "system-name": "X460G2-24t-10G4" -# } -# } - - -""" -RETURN = """ -before: - description: The configuration as structured data prior to module invocation. - returned: always - sample: > - The configuration returned will always be in the same format - of the parameters above. - type: list -after: - description: The configuration as structured data after module completion. - returned: when changed - sample: > - The configuration returned will always be in the same format - of the parameters above. - type: list -requests: - description: The set of requests pushed to the remote device. - returned: always - type: list - sample: [{"data": "...", "method": "...", "path": "..."}, {"data": "...", "method": "...", "path": "..."}, {"data": "...", "method": "...", "path": "..."}] -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.lldp_global.lldp_global import Lldp_globalArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.config.lldp_global.lldp_global import Lldp_global - - -def main(): - """ - Main entry point for module execution - - :returns: the result form module invocation - """ - required_if = [('state', 'merged', ('config',)), - ('state', 'replaced', ('config',))] - module = AnsibleModule(argument_spec=Lldp_globalArgs.argument_spec, required_if=required_if, - supports_check_mode=True) - - result = Lldp_global(module).execute_module() - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/exos/exos_lldp_interfaces.py b/plugins/modules/network/exos/exos_lldp_interfaces.py deleted file mode 100644 index 744f76c24b..0000000000 --- a/plugins/modules/network/exos/exos_lldp_interfaces.py +++ /dev/null @@ -1,679 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The module file for exos_lldp_interfaces -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} - -DOCUMENTATION = ''' ---- -module: exos_lldp_interfaces -short_description: Manage link layer discovery protocol (LLDP) attributes of interfaces on EXOS platforms. -description: - - This module manages link layer discovery protocol (LLDP) attributes of interfaces on Extreme Networks EXOS platforms. -author: Jayalakshmi Viswanathan (@JayalakshmiV) -options: - config: - description: The list of link layer discovery protocol interface attribute configurations - type: list - elements: dict - suboptions: - name: - description: - - Name of the interface LLDP needs to be configured on. - type: str - required: True - enabled: - description: - - This is a boolean value to control disabling of LLDP on the interface C(name) - type: bool - state: - description: - - The state the configuration should be left in. - type: str - choices: - - merged - - replaced - - overridden - - deleted - default: merged -''' -EXAMPLES = """ -# Using merged - -# Before state: -# ------------- -# -# path: /rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4 -# method: GET -# data: -# { -# "openconfig-lldp:interfaces": { -# "interface": [ -# { -# "config": { -# "enabled": true, -# "name": "1" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "2" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "3" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "4" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "5" -# } -# } -# ] -# } -# } - -- name: Merge provided configuration with device configuration - exos_lldp_interfaces: - config: - - name: '2' - enabled: false - - name: '5' - enabled: true - state: merged - -# Module Execution Results: -# ------------------------- -# -# "before": -# - name: '1' -# enabled: True -# - name: '2' -# enabled: True -# - name: '3' -# enabled: False -# - name: '4' -# enabled: True -# - name: '5' -# enabled: False -# -# "requests": [ -# { -# "data": | -# { -# "openconfig-lldp:config": { -# "enabled": false, -# "name": "2" -# } -# } -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=2/config" -# }, -# { -# "data": | -# { -# "openconfig-lldp:config": { -# "enabled": true, -# "name": "5" -# } -# } -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=5/config" -# } -# ] -# -# "after": -# - name: '1' -# enabled: True -# - name: '2' -# enabled: False -# - name: '3' -# enabled: False -# - name: '4' -# enabled: True -# - name: '5' -# enabled: True - -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4 -# method: GET -# data: -# { -# "openconfig-lldp:interfaces": { -# "interface": [ -# { -# "config": { -# "enabled": true, -# "name": "1" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "2" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "3" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "4" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "5" -# } -# } -# ] -# } -# } - - -# Using replaced - -# Before state: -# ------------- -# -# path: /rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4 -# method: GET -# data: -# { -# "openconfig-lldp:interfaces": { -# "interface": [ -# { -# "config": { -# "enabled": true, -# "name": "1" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "2" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "3" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "4" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "5" -# } -# } -# ] -# } -# } - -- name: Replaces device configuration of listed lldp_interfaces with provided configuration - exos_lldp_interfaces: - config: - - name: '1' - enabled: false - - name: '3' - enabled: true - state: merged - -# Module Execution Results: -# ------------------------- -# -# "before": -# - name: '1' -# enabled: True -# - name: '2' -# enabled: True -# - name: '3' -# enabled: False -# - name: '4' -# enabled: True -# - name: '5' -# enabled: False -# -# "requests": [ -# { -# "data": | -# { -# "openconfig-lldp:config": { -# "enabled": false, -# "name": "1" -# } -# } -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=1/config" -# }, -# { -# "data": | -# { -# "openconfig-lldp:config": { -# "enabled": true, -# "name": "3" -# } -# } -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=3/config" -# } -# ] -# -# "after": -# - name: '1' -# enabled: False -# - name: '2' -# enabled: True -# - name: '3' -# enabled: True -# - name: '4' -# enabled: True -# - name: '5' -# enabled: False - -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4 -# method: GET -# data: -# { -# "openconfig-lldp:interfaces": { -# "interface": [ -# { -# "config": { -# "enabled": false, -# "name": "1" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "2" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "3" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "4" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "5" -# } -# } -# ] -# } -# } - - -# Using deleted - -# Before state: -# ------------- -# -# path: /rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4 -# method: GET -# data: -# { -# "openconfig-lldp:interfaces": { -# "interface": [ -# { -# "config": { -# "enabled": false, -# "name": "1" -# }, -# }, -# { -# "config": { -# "enabled": false, -# "name": "2" -# }, -# }, -# { -# "config": { -# "enabled": false, -# "name": "3" -# }, -# } -# ] -# } -# } - -- name: Delete lldp interface configuration (this will not delete other lldp configuration) - exos_lldp_interfaces: - config: - - name: '1' - - name: '3' - state: deleted - -# Module Execution Results: -# ------------------------- -# -# "before": -# - name: '1' -# enabled: False -# - name: '2' -# enabled: False -# - name: '3' -# enabled: False -# -# "requests": [ -# { -# "data": | -# { -# "openconfig-lldp:config": { -# "enabled": true, -# "name": "1" -# } -# } -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=1/config" -# }, -# { -# "data": | -# { -# "openconfig-lldp:config": { -# "enabled": true, -# "name": "3" -# } -# } -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=3/config" -# } -# ] -# -# "after": -# - name: '1' -# enabled: True -# - name: '2' -# enabled: False -# - name: '3' -# enabled: True -# -# After state: -# ------------- -# path: /rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4 -# method: GET -# data: -# { -# "openconfig-lldp:interfaces": { -# "interface": [ -# { -# "config": { -# "enabled": true, -# "name": "1" -# }, -# }, -# { -# "config": { -# "enabled": false, -# "name": "2" -# }, -# }, -# { -# "config": { -# "enabled": true, -# "name": "3" -# }, -# } -# ] -# } -# } - - -# Using overridden - -# Before state: -# ------------- -# -# path: /rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4 -# method: GET -# data: -# { -# "openconfig-lldp:interfaces": { -# "interface": [ -# { -# "config": { -# "enabled": true, -# "name": "1" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "2" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "3" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "4" -# } -# }, -# { -# "config": { -# "enabled": false, -# "name": "5" -# } -# } -# ] -# } -# } - -- name: Override device configuration of all lldp_interfaces with provided configuration - exos_lldp_interfaces: - config: - - name: '3' - enabled: true - state: overridden - -# Module Execution Results: -# ------------------------- -# -# "before": -# - name: '1' -# enabled: True -# - name: '2' -# enabled: True -# - name: '3' -# enabled: False -# - name: '4' -# enabled: True -# - name: '5' -# enabled: False -# -# "requests": [ -# { -# "data": | -# { -# "openconfig-lldp:config": { -# "enabled": true, -# "name": "5" -# } -# } -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=5/config" -# }, -# { -# "data": | -# { -# "openconfig-lldp:config": { -# "enabled": true, -# "name": "3" -# } -# } -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-lldp:lldp/interfaces/interface=3/config" -# } -# ] -# -# "after": -# - name: '1' -# enabled: True -# - name: '2' -# enabled: True -# - name: '3' -# enabled: True -# - name: '4' -# enabled: True -# - name: '5' -# enabled: True - -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-lldp:lldp/interfaces?depth=4 -# method: GET -# data: -# { -# "openconfig-lldp:interfaces": { -# "interface": [ -# { -# "config": { -# "enabled": true, -# "name": "1" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "2" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "3" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "4" -# } -# }, -# { -# "config": { -# "enabled": true, -# "name": "5" -# } -# } -# ] -# } -# } - - -""" -RETURN = """ -before: - description: The configuration prior to the model invocation. - returned: always - sample: > - The configuration returned will always be in the same format - of the parameters above. - type: list -after: - description: The resulting configuration model invocation. - returned: when changed - sample: > - The configuration returned will always be in the same format - of the parameters above. - type: list -requests: - description: The set of requests pushed to the remote device. - returned: always - type: list - sample: [{"data": "...", "method": "...", "path": "..."}, {"data": "...", "method": "...", "path": "..."}, {"data": "...", "method": "...", "path": "..."}] -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.lldp_interfaces.lldp_interfaces import Lldp_interfacesArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.config.lldp_interfaces.lldp_interfaces import Lldp_interfaces - - -def main(): - """ - Main entry point for module execution - - :returns: the result form module invocation - """ - required_if = [('state', 'merged', ('config', )), - ('state', 'replaced', ('config', ))] - module = AnsibleModule(argument_spec=Lldp_interfacesArgs.argument_spec, - required_if=required_if, - supports_check_mode=True) - - result = Lldp_interfaces(module).execute_module() - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/exos/exos_vlans.py b/plugins/modules/network/exos/exos_vlans.py deleted file mode 100644 index d828c7cd70..0000000000 --- a/plugins/modules/network/exos/exos_vlans.py +++ /dev/null @@ -1,758 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The module file for exos_vlans -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} - -DOCUMENTATION = ''' ---- -module: exos_vlans -short_description: Manage VLANs on Extreme Networks EXOS devices. -description: This module provides declarative management of VLANs on Extreme Networks EXOS network devices. -author: Jayalakshmi Viswanathan (@jayalakshmiV) -notes: - - Tested against EXOS 30.2.1.8 - - This module works with connection C(httpapi). - See L(EXOS Platform Options,../network/user_guide/platform_exos.html) -options: - config: - description: A dictionary of VLANs options - type: list - elements: dict - suboptions: - name: - description: - - Ascii name of the VLAN. - type: str - vlan_id: - description: - - ID of the VLAN. Range 1-4094 - type: int - required: True - state: - description: - - Operational state of the VLAN - type: str - choices: - - active - - suspend - default: active - state: - description: - - The state the configuration should be left in - type: str - choices: - - merged - - replaced - - overridden - - deleted - default: merged -''' -EXAMPLES = """ -# Using deleted - -# Before state: -# ------------- -# -# path: /rest/restconf/data/openconfig-vlan:vlans/ -# method: GET -# data: -# { -# "openconfig-vlan:vlans": { -# "vlan": [ -# { -# "config": { -# "name": "Default", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 1 -# }, -# }, -# { -# "config": { -# "name": "vlan_10", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 10 -# }, -# }, -# { -# "config": { -# "name": "vlan_20", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 20 -# }, -# }, -# { -# "config": { -# "name": "vlan_30", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 30 -# }, -# } -# ] -# } -# } - -- name: Delete attributes of given VLANs - exos_vlans: - config: - - vlan_id: 10 - - vlan_id: 20 - - vlan_id: 30 - state: deleted - -# Module Execution Results: -# ------------------------- -# -# "after": [ -# { -# "name": "Default", -# "state": "active", -# "vlan_id": 1 -# } -# ], -# -# "before": [ -# { -# "name": "Default", -# "state": "active", -# "vlan_id": 1 -# }, -# { -# "name": "vlan_10", -# "state": "active", -# "vlan_id": 10 -# }, -# { -# "name": "vlan_20", -# "state": "active", -# "vlan_id": 20 -# } -# { -# "name": "vlan_30", -# "state": "active", -# "vlan_id": 30 -# } -# ], -# -# "requests": [ -# { -# "data": null, -# "method": "DELETE", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/vlan=10" -# }, -# { -# "data": null, -# "method": "DELETE", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/vlan=20" -# }, -# { -# "data": null, -# "method": "DELETE", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/vlan=30" -# } -# ] -# -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-vlan:vlans/ -# method: GET -# data: -# { -# "openconfig-vlan:vlans": { -# "vlan": [ -# { -# "config": { -# "name": "Default", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 1 -# }, -# } -# ] -# } -# } - - -# Using merged - -# Before state: -# ------------- -# path: /rest/restconf/data/openconfig-vlan:vlans/ -# method: GET -# data: -# { -# "openconfig-vlan:vlans": { -# "vlan": [ -# { -# "config": { -# "name": "Default", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 1 -# }, -# } -# ] -# } -# } - -- name: Merge provided configuration with device configuration - exos_vlans: - config: - - name: vlan_10 - vlan_id: 10 - state: active - - name: vlan_20 - vlan_id: 20 - state: active - - name: vlan_30 - vlan_id: 30 - state: active - state: merged - -# Module Execution Results: -# ------------------------- -# -# "after": [ -# { -# "name": "Default", -# "state": "active", -# "vlan_id": 1 -# }, -# { -# "name": "vlan_10", -# "state": "active", -# "vlan_id": 10 -# }, -# { -# "name": "vlan_20", -# "state": "active", -# "vlan_id": 20 -# }, -# { -# "name": "vlan_30", -# "state": "active", -# "vlan_id": 30 -# } -# ], -# -# "before": [ -# { -# "name": "Default", -# "state": "active", -# "vlan_id": 1 -# } -# ], -# -# "requests": [ -# { -# "data": { -# "openconfig-vlan:vlan": [ -# { -# "config": { -# "name": "vlan_10", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 10 -# } -# } -# ] -# }, -# "method": "POST", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/" -# }, -# { -# "data": { -# "openconfig-vlan:vlan": [ -# { -# "config": { -# "name": "vlan_20", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 20 -# } -# } -# ] -# }, -# "method": "POST", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/" -# }, -# "data": { -# "openconfig-vlan:vlan": [ -# { -# "config": { -# "name": "vlan_30", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 30 -# } -# } -# ] -# }, -# "method": "POST", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/" -# } -# ] -# -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-vlan:vlans/ -# method: GET -# data: -# { -# "openconfig-vlan:vlans": { -# "vlan": [ -# { -# "config": { -# "name": "Default", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 1 -# }, -# }, -# { -# "config": { -# "name": "vlan_10", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 10 -# }, -# }, -# { -# "config": { -# "name": "vlan_20", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 20 -# }, -# }, -# { -# "config": { -# "name": "vlan_30", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 30 -# }, -# } -# ] -# } -# } - - -# Using overridden - -# Before state: -# ------------- -# -# path: /rest/restconf/data/openconfig-vlan:vlans/ -# method: GET -# data: -# { -# "openconfig-vlan:vlans": { -# "vlan": [ -# { -# "config": { -# "name": "Default", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 1 -# }, -# }, -# { -# "config": { -# "name": "vlan_10", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 10 -# }, -# }, -# { -# "config": { -# "name": "vlan_20", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 20 -# }, -# }, -# { -# "config": { -# "name": "vlan_30", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 30 -# }, -# } -# ] -# } -# } - -- name: Override device configuration of all VLANs with provided configuration - exos_vlans: - config: - - name: TEST_VLAN10 - vlan_id: 10 - state: overridden - -# Module Execution Results: -# ------------------------- -# -# "after": [ -# { -# "name": "Default", -# "state": "active", -# "vlan_id": 1 -# }, -# { -# "name": "TEST_VLAN10", -# "state": "active", -# "vlan_id": 10 -# }, -# ], -# -# "before": [ -# { -# "name": "Default", -# "state": "active", -# "vlan_id": 1 -# }, -# { -# "name": "vlan_10", -# "state": "active", -# "vlan_id": 10 -# }, -# { -# "name": "vlan_20", -# "state": "active", -# "vlan_id": 20 -# }, -# { -# "name": "vlan_30", -# "state": "active", -# "vlan_id": 30 -# } -# ], -# -# "requests": [ -# { -# "data": { -# "openconfig-vlan:vlan": { -# "vlan": [ -# { -# "config": { -# "name": "TEST_VLAN10", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 10 -# } -# } -# ] -# } -# } -# }, -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/" -# }, -# { -# "data": null, -# "method": "DELETE", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/vlan=20" -# }, -# { -# "data": null, -# "method": "DELETE", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/vlan=30" -# } -# ] -# -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-vlan:vlans/ -# method: GET -# data: -# { -# "openconfig-vlan:vlans": { -# "vlan": [ -# { -# "config": { -# "name": "Default", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 1 -# }, -# }, -# { -# "config": { -# "name": "TEST_VLAN10", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 10 -# }, -# } -# ] -# } -# } - - -# Using replaced - -# Before state: -# ------------- -# -# path: /rest/restconf/data/openconfig-vlan:vlans/ -# method: GET -# data: -# { -# "openconfig-vlan:vlans": { -# "vlan": [ -# { -# "config": { -# "name": "Default", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 1 -# }, -# }, -# { -# "config": { -# "name": "vlan_10", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 10 -# }, -# }, -# { -# "config": { -# "name": "vlan_20", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 20 -# }, -# }, -# { -# "config": { -# "name": "vlan_30", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 30 -# }, -# } -# ] -# } -# } - -- name: Replaces device configuration of listed VLANs with provided configuration - exos_vlans: - config: - - name: Test_VLAN20 - vlan_id: 20 - - name: Test_VLAN30 - vlan_id: 30 - state: replaced - -# Module Execution Results: -# ------------------------- -# -# "after": [ -# { -# "name": "Default", -# "state": "active", -# "vlan_id": 1 -# }, -# { -# "name": "vlan_10", -# "state": "active", -# "vlan_id": 10 -# }, -# { -# "name": "TEST_VLAN20", -# "state": "active", -# "vlan_id": 20 -# }, -# { -# "name": "TEST_VLAN30", -# "state": "active", -# "vlan_id": 30 -# } -# ], -# -# "before": [ -# { -# "name": "Default", -# "state": "active", -# "vlan_id": 1 -# }, -# { -# "name": "vlan_10", -# "state": "active", -# "vlan_id": 10 -# }, -# { -# "name": "vlan_20", -# "state": "active", -# "vlan_id": 20 -# }, -# { -# "name": "vlan_30", -# "state": "active", -# "vlan_id": 30 -# } -# ], -# -# "requests": [ -# { -# "data": { -# "openconfig-vlan:vlan": { -# "vlan": [ -# { -# "config": { -# "name": "TEST_VLAN20", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 20 -# } -# "config": { -# "name": "TEST_VLAN30", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 30 -# } -# } -# ] -# }, -# "method": "PATCH", -# "path": "/rest/restconf/data/openconfig-vlan:vlans/" -# } -# ] -# -# After state: -# ------------- -# -# path: /rest/restconf/data/openconfig-vlan:vlans/ -# method: GET -# data: -# { -# "openconfig-vlan:vlans": { -# "vlan": [ -# { -# "config": { -# "name": "Default", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 1 -# }, -# }, -# { -# "config": { -# "name": "vlan_10", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 10 -# }, -# }, -# { -# "config": { -# "name": "TEST_VLAN20", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 20 -# }, -# }, -# { -# "config": { -# "name": "TEST_VLAN30", -# "status": "ACTIVE", -# "tpid": "oc-vlan-types:TPID_0x8100", -# "vlan-id": 30 -# }, -# } -# ] -# } -# } - - -""" -RETURN = """ -before: - description: The configuration prior to the model invocation. - returned: always - sample: > - The configuration returned will always be in the same format - of the parameters above. - type: list -after: - description: The resulting configuration model invocation. - returned: when changed - sample: > - The configuration returned will always be in the same format - of the parameters above. - type: list -requests: - description: The set of requests pushed to the remote device. - returned: always - type: list - sample: [{"data": "...", "method": "...", "path": "..."}, {"data": "...", "method": "...", "path": "..."}, {"data": "...", "method": "...", "path": "..."}] -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.exos.argspec.vlans.vlans import VlansArgs -from ansible_collections.community.general.plugins.module_utils.network.exos.config.vlans.vlans import Vlans - - -def main(): - """ - Main entry point for module execution - - :returns: the result form module invocation - """ - required_if = [('state', 'merged', ('config',)), - ('state', 'replaced', ('config',))] - module = AnsibleModule(argument_spec=VlansArgs.argument_spec, required_if=required_if, - supports_check_mode=True) - - result = Vlans(module).execute_module() - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_asm_policy.py b/plugins/modules/network/f5/bigip_asm_policy.py deleted file mode 100644 index 2217f0d780..0000000000 --- a/plugins/modules/network/f5/bigip_asm_policy.py +++ /dev/null @@ -1,1062 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright: (c) 2017, F5 Networks 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': ['deprecated'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigip_asm_policy -short_description: Manage BIG-IP ASM policies -description: - - Manage BIG-IP ASM policies. -deprecated: - removed_in: '2.12' - alternative: bigip_asm_policy_manage - why: > - The bigip_asm_policy module has been split into three new modules to handle import, export and general policy - management. This will allow scalability of the asm policy management as well as ease of maintenance. - Additionally to further reduce the burden of having multiple smaller module F5 has created asm_policy - role in Ansible Galaxy for a more declarative way of ASM policy management. -options: - active: - description: - - If C(yes) will apply and activate existing inactive policy. If C(no), it will - deactivate existing active policy. Generally should be C(yes) only in cases where - you want to activate new or existing policy. - default: no - type: bool - name: - description: - - The ASM policy to manage or create. - required: True - state: - description: - - When C(state) is C(present), and C(file) or C(template) parameter is provided, - new ASM policy is imported and created with the given C(name). - - When C(state) is present and no C(file) or C(template) parameter is provided - new blank ASM policy is created with the given C(name). - - When C(state) is C(absent), ensures that the policy is removed, even if it is - currently active. - choices: - - present - - absent - default: present - file: - description: - - Full path to a policy file to be imported into the BIG-IP ASM. - - Policy files exported from newer versions of BIG-IP cannot be imported into older - versions of BIG-IP. The opposite, however, is true; you can import older into - newer. - template: - description: - - An ASM policy built-in template. If the template does not exist we will raise an error. - - Once the policy has been created, this value cannot change. - - The C(Comprehensive), C(Drupal), C(Fundamental), C(Joomla), - C(Vulnerability Assessment Baseline), and C(Wordpress) templates are only available - on BIG-IP versions >= 13. - choices: - - ActiveSync v1.0 v2.0 (http) - - ActiveSync v1.0 v2.0 (https) - - Comprehensive - - Drupal - - Fundamental - - Joomla - - LotusDomino 6.5 (http) - - LotusDomino 6.5 (https) - - OWA Exchange 2003 (http) - - OWA Exchange 2003 (https) - - OWA Exchange 2003 with ActiveSync (http) - - OWA Exchange 2003 with ActiveSync (https) - - OWA Exchange 2007 (http) - - OWA Exchange 2007 (https) - - OWA Exchange 2007 with ActiveSync (http) - - OWA Exchange 2007 with ActiveSync (https) - - OWA Exchange 2010 (http) - - OWA Exchange 2010 (https) - - Oracle 10g Portal (http) - - Oracle 10g Portal (https) - - Oracle Applications 11i (http) - - Oracle Applications 11i (https) - - PeopleSoft Portal 9 (http) - - PeopleSoft Portal 9 (https) - - Rapid Deployment Policy - - SAP NetWeaver 7 (http) - - SAP NetWeaver 7 (https) - - SharePoint 2003 (http) - - SharePoint 2003 (https) - - SharePoint 2007 (http) - - SharePoint 2007 (https) - - SharePoint 2010 (http) - - SharePoint 2010 (https) - - Vulnerability Assessment Baseline - - Wordpress - partition: - description: - - Device partition to manage resources on. - default: Common -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -author: - - Wojciech Wypior (@wojtek0806) - - Tim Rupp (@caphrim007) -''' - -EXAMPLES = r''' -- name: Import and activate ASM policy - bigip_asm_policy: - name: new_asm_policy - file: /root/asm_policy.xml - active: yes - state: present - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Import ASM policy from template - bigip_asm_policy: - name: new_sharepoint_policy - template: SharePoint 2007 (http) - state: present - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Create blank ASM policy - bigip_asm_policy: - name: new_blank_policy - state: present - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Create blank ASM policy and activate - bigip_asm_policy: - name: new_blank_policy - active: yes - state: present - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Activate ASM policy - bigip_asm_policy: - name: inactive_policy - active: yes - state: present - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Deactivate ASM policy - bigip_asm_policy: - name: active_policy - state: present - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Import and activate ASM policy in Role - bigip_asm_policy: - name: new_asm_policy - file: "{{ role_path }}/files/asm_policy.xml" - active: yes - state: present - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Import ASM binary policy - bigip_asm_policy: - name: new_asm_policy - file: "/root/asm_policy.plc" - active: yes - state: present - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost -''' - -RETURN = r''' -active: - description: Set when activating/deactivating ASM policy - returned: changed - type: bool - sample: yes -state: - description: Action performed on the target device. - returned: changed - type: str - sample: absent -file: - description: Local path to ASM policy file. - returned: changed - type: str - sample: /root/some_policy.xml -template: - description: Name of the built-in ASM policy template - returned: changed - type: str - sample: OWA Exchange 2007 (https) -name: - description: Name of the ASM policy to be managed/created - returned: changed - type: str - sample: Asm_APP1_Transparent -''' - -import os -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.basic import env_fallback -from distutils.version import LooseVersion - - -try: - from library.module_utils.network.f5.bigip import F5RestClient - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import AnsibleF5Parameters - from library.module_utils.network.f5.common import fq_name - from library.module_utils.network.f5.common import f5_argument_spec - from library.module_utils.network.f5.icontrol import upload_file - from library.module_utils.network.f5.icontrol import tmos_version - from library.module_utils.network.f5.icontrol import module_provisioned -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.bigip import F5RestClient - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import fq_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - from ansible_collections.f5networks.f5_modules.plugins.module_utils.icontrol import upload_file - from ansible_collections.f5networks.f5_modules.plugins.module_utils.icontrol import tmos_version - from ansible_collections.f5networks.f5_modules.plugins.module_utils.icontrol import module_provisioned - - -class Parameters(AnsibleF5Parameters): - updatables = [ - 'active', - ] - - returnables = [ - 'name', - 'template', - 'file', - 'active', - ] - - api_attributes = [ - 'name', - 'file', - 'active', - ] - api_map = { - 'filename': 'file', - } - - @property - def template_link(self): - if self._values['template_link'] is not None: - return self._values['template_link'] - collection = self._templates_from_device() - for resource in collection['items']: - if resource['name'] == self.template.upper(): - return dict(link=resource['selfLink']) - return None - - @property - def full_path(self): - return fq_name(self.name) - - def _templates_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/asm/policy-templates/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return response - - def to_return(self): - result = {} - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - - -class V1Parameters(Parameters): - @property - def template(self): - if self._values['template'] is None: - return None - template_map = { - 'ActiveSync v1.0 v2.0 (http)': 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTP', - 'ActiveSync v1.0 v2.0 (https)': 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTPS', - 'LotusDomino 6.5 (http)': 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTP', - 'LotusDomino 6.5 (https)': 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTPS', - 'OWA Exchange 2003 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTP', - 'OWA Exchange 2003 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTPS', - 'OWA Exchange 2003 with ActiveSync (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTP', - 'OWA Exchange 2003 with ActiveSync (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTPS', - 'OWA Exchange 2007 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTP', - 'OWA Exchange 2007 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTPS', - 'OWA Exchange 2007 with ActiveSync (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTP', - 'OWA Exchange 2007 with ActiveSync (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTPS', - 'OWA Exchange 2010 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTP', - 'OWA Exchange 2010 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTPS', - 'Oracle 10g Portal (http)': 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTP', - 'Oracle 10g Portal (https)': 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTPS', - 'Oracle Applications 11i (http)': 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTP', - 'Oracle Applications 11i (https)': 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTPS', - 'PeopleSoft Portal 9 (http)': 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTP', - 'PeopleSoft Portal 9 (https)': 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTPS', - 'Rapid Deployment Policy': 'POLICY_TEMPLATE_RAPID_DEPLOYMENT', - 'SAP NetWeaver 7 (http)': 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTP', - 'SAP NetWeaver 7 (https)': 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTPS', - 'SharePoint 2003 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTP', - 'SharePoint 2003 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTPS', - 'SharePoint 2007 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTP', - 'SharePoint 2007 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTPS', - 'SharePoint 2010 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTP', - 'SharePoint 2010 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTPS' - } - if self._values['template'] in template_map: - return template_map[self._values['template']] - else: - raise F5ModuleError( - "The specified template is not valid for this version of BIG-IP." - ) - - -class V2Parameters(Parameters): - @property - def template(self): - if self._values['template'] is None: - return None - template_map = { - 'ActiveSync v1.0 v2.0 (http)': 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTP', - 'ActiveSync v1.0 v2.0 (https)': 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTPS', - 'Comprehensive': 'POLICY_TEMPLATE_COMPREHENSIVE', # v13 - 'Drupal': 'POLICY_TEMPLATE_DRUPAL', # v13 - 'Fundamental': 'POLICY_TEMPLATE_FUNDAMENTAL', # v13 - 'Joomla': 'POLICY_TEMPLATE_JOOMLA', # v13 - 'LotusDomino 6.5 (http)': 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTP', - 'LotusDomino 6.5 (https)': 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTPS', - 'OWA Exchange 2003 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTP', - 'OWA Exchange 2003 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTPS', - 'OWA Exchange 2003 with ActiveSync (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTP', - 'OWA Exchange 2003 with ActiveSync (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTPS', - 'OWA Exchange 2007 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTP', - 'OWA Exchange 2007 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTPS', - 'OWA Exchange 2007 with ActiveSync (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTP', - 'OWA Exchange 2007 with ActiveSync (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTPS', - 'OWA Exchange 2010 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTP', - 'OWA Exchange 2010 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTPS', - 'Oracle 10g Portal (http)': 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTP', - 'Oracle 10g Portal (https)': 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTPS', - 'Oracle Applications 11i (http)': 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTP', - 'Oracle Applications 11i (https)': 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTPS', - 'PeopleSoft Portal 9 (http)': 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTP', - 'PeopleSoft Portal 9 (https)': 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTPS', - 'Rapid Deployment Policy': 'POLICY_TEMPLATE_RAPID_DEPLOYMENT', - 'SAP NetWeaver 7 (http)': 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTP', - 'SAP NetWeaver 7 (https)': 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTPS', - 'SharePoint 2003 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTP', - 'SharePoint 2003 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTPS', - 'SharePoint 2007 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTP', - 'SharePoint 2007 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTPS', - 'SharePoint 2010 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTP', - 'SharePoint 2010 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTPS', - 'Vulnerability Assessment Baseline': 'POLICY_TEMPLATE_VULNERABILITY_ASSESSMENT', # v13 - 'Wordpress': 'POLICY_TEMPLATE_WORDPRESS' # v13 - } - return template_map[self._values['template']] - - -class Changes(Parameters): - @property - def template(self): - if self._values['template'] is None: - return None - template_map = { - 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTP': 'ActiveSync v1.0 v2.0 (http)', - 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTPS': 'ActiveSync v1.0 v2.0 (https)', - 'POLICY_TEMPLATE_COMPREHENSIVE': 'Comprehensive', - 'POLICY_TEMPLATE_DRUPAL': 'Drupal', - 'POLICY_TEMPLATE_FUNDAMENTAL': 'Fundamental', - 'POLICY_TEMPLATE_JOOMLA': 'Joomla', - 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTP': 'LotusDomino 6.5 (http)', - 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTPS': 'LotusDomino 6.5 (https)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTP': 'OWA Exchange 2003 (http)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTPS': 'OWA Exchange 2003 (https)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTP': 'OWA Exchange 2003 with ActiveSync (http)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTPS': 'OWA Exchange 2003 with ActiveSync (https)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTP': 'OWA Exchange 2007 (http)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTPS': 'OWA Exchange 2007 (https)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTP': 'OWA Exchange 2007 with ActiveSync (http)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTPS': 'OWA Exchange 2007 with ActiveSync (https)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTP': 'OWA Exchange 2010 (http)', - 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTPS': 'OWA Exchange 2010 (https)', - 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTP': 'Oracle 10g Portal (http)', - 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTPS': 'Oracle 10g Portal (https)', - 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTP': 'Oracle Applications 11i (http)', - 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTPS': 'Oracle Applications 11i (https)', - 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTP': 'PeopleSoft Portal 9 (http)', - 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTPS': 'PeopleSoft Portal 9 (https)', - 'POLICY_TEMPLATE_RAPID_DEPLOYMENT': 'Rapid Deployment Policy', - 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTP': 'SAP NetWeaver 7 (http)', - 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTPS': 'SAP NetWeaver 7 (https)', - 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTP': 'SharePoint 2003 (http)', - 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTPS': 'SharePoint 2003 (https)', - 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTP': 'SharePoint 2007 (http)', - 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTPS': 'SharePoint 2007 (https)', - 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTP': 'SharePoint 2010 (http)', - 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTPS': 'SharePoint 2010 (https)', - 'POLICY_TEMPLATE_VULNERABILITY_ASSESSMENT': 'Vulnerability Assessment Baseline', - 'POLICY_TEMPLATE_WORDPRESS': 'Wordpress', - } - return template_map[self._values['template']] - - -class Difference(object): - def __init__(self, want, have=None): - self.want = want - self.have = have - - def compare(self, param): - try: - result = getattr(self, param) - return result - except AttributeError: - return self.__default(param) - - def __default(self, param): - attr1 = getattr(self.want, param) - try: - attr2 = getattr(self.have, param) - if attr1 != attr2: - return attr1 - except AttributeError: - return attr1 - - @property - def active(self): - if self.want.active is True and self.have.active is False: - return True - if self.want.active is False and self.have.active is True: - return False - - -class BaseManager(object): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - self.have = None - self.changes = Changes() - - def exec_module(self): - changed = False - result = dict() - state = self.want.state - - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - - changes = self.changes.to_return() - result.update(**changes) - result.update(dict(changed=changed)) - self._announce_deprecations(result) - return result - - def _announce_deprecations(self, result): - warnings = result.pop('__warnings', []) - for warning in warnings: - self.client.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) - - def _set_changed_options(self): - changed = {} - for key in Parameters.returnables: - if getattr(self.want, key) is not None: - changed[key] = getattr(self.want, key) - if changed: - self.changes = Changes(params=changed) - - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - - def _update_changed_options(self): - diff = Difference(self.want, self.have) - updatables = Parameters.updatables - changed = dict() - for k in updatables: - change = diff.compare(k) - if change is None: - continue - else: - if isinstance(change, dict): - changed.update(change) - else: - changed[k] = change - if changed: - self.changes = Changes(params=changed) - return True - return False - - def present(self): - if self.exists(): - return self.update() - else: - return self.create() - - def absent(self): - if not self.exists(): - return False - else: - return self.remove() - - def exists(self): - uri = "https://{0}:{1}/mgmt/tm/asm/policies/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if any(p['name'] == self.want.name and p['partition'] == self.want.partition for p in response['items']): - return True - return False - - def _file_is_missing(self): - if self.want.template and self.want.file is None: - return False - if self.want.template is None and self.want.file is None: - return False - if not os.path.exists(self.want.file): - return True - return False - - def create(self): - if self.want.active is None: - self.want.update(dict(active=False)) - if self._file_is_missing(): - raise F5ModuleError( - "The specified ASM policy file does not exist" - ) - self._set_changed_options() - if self.module.check_mode: - return True - - if self.want.template is None and self.want.file is None: - self.create_blank() - else: - if self.want.template is not None: - self.create_from_template() - elif self.want.file is not None: - self.create_from_file() - - if self.want.active: - self.activate() - return True - else: - return True - - def update(self): - self.have = self.read_current_from_device() - if not self.should_update(): - return False - if self.module.check_mode: - return True - self.update_on_device() - if self.changes.active: - self.activate() - return True - - def activate(self): - self.have = self.read_current_from_device() - task_id = self.apply_on_device() - if self.wait_for_task(task_id, 'apply'): - return True - else: - raise F5ModuleError('Apply policy task failed.') - - def wait_for_task(self, task_id, task): - uri = '' - if task == 'apply': - uri = "https://{0}:{1}/mgmt/tm/asm/tasks/apply-policy/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - task_id - ) - elif task == 'import': - uri = "https://{0}:{1}/mgmt/tm/asm/tasks/import-policy/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - task_id - ) - while True: - resp = self.client.api.get(uri) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - if response['status'] in ['COMPLETED', 'FAILURE']: - break - time.sleep(1) - - if response['status'] == 'FAILURE': - return False - if response['status'] == 'COMPLETED': - return True - - def _get_policy_id(self): - name = self.want.name - partition = self.want.partition - uri = "https://{0}:{1}/mgmt/tm/asm/policies/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - policy_id = next( - (p['id'] for p in response['items'] if p['name'] == name and p['partition'] == partition), None - ) - - if not policy_id: - raise F5ModuleError("The policy was not found") - return policy_id - - def update_on_device(self): - params = self.changes.api_params() - policy_id = self._get_policy_id() - uri = "https://{0}:{1}/mgmt/tm/asm/policies/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - policy_id - ) - if not params['active']: - resp = self.client.api.patch(uri, json=params) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - def create_blank(self): - self.create_on_device() - if self.exists(): - return True - else: - raise F5ModuleError( - 'Failed to create ASM policy: {0}'.format(self.want.name) - ) - - def remove(self): - if self.module.check_mode: - return True - self.remove_from_device() - if self.exists(): - raise F5ModuleError( - 'Failed to delete ASM policy: {0}'.format(self.want.name) - ) - return True - - def is_activated(self): - if self.want.active is True: - return True - else: - return False - - def read_current_from_device(self): - policy_id = self._get_policy_id() - uri = "https://{0}:{1}/mgmt/tm/asm/policies/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - policy_id - ) - resp = self.client.api.get(uri) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - response.update((dict(self_link=response['selfLink']))) - - return Parameters(params=response) - - def upload_file_to_device(self, content, name): - url = 'https://{0}:{1}/mgmt/shared/file-transfer/uploads'.format( - self.client.provider['server'], - self.client.provider['server_port'] - ) - try: - upload_file(self.client, url, content, name) - except F5ModuleError: - raise F5ModuleError( - "Failed to upload the file." - ) - - def import_to_device(self): - name = os.path.split(self.want.file)[1] - self.upload_file_to_device(self.want.file, name) - time.sleep(2) - - full_name = fq_name(self.want.partition, self.want.name) - cmd = 'tmsh load asm policy {0} file /var/config/rest/downloads/{1}'.format(full_name, name) - - uri = "https://{0}:{1}/mgmt/tm/util/bash/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - args = dict( - command='run', - utilCmdArgs='-c "{0}"'.format(cmd) - ) - resp = self.client.api.post(uri, json=args) - - try: - response = resp.json() - if 'commandResult' in response: - if 'Unexpected Error' in response['commandResult']: - raise F5ModuleError(response['commandResult']) - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return True - - def remove_temp_policy_from_device(self): - name = os.path.split(self.want.file)[1] - tpath_name = '/var/config/rest/downloads/{0}'.format(name) - uri = "https://{0}:{1}/mgmt/tm/util/unix-rm/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - args = dict( - command='run', - utilCmdArgs=tpath_name - ) - resp = self.client.api.post(uri, json=args) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - def apply_on_device(self): - uri = "https://{0}:{1}/mgmt/tm/asm/tasks/apply-policy/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - params = dict(policyReference={'link': self.have.self_link}) - resp = self.client.api.post(uri, json=params) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return response['id'] - - def create_from_template_on_device(self): - full_name = fq_name(self.want.partition, self.want.name) - cmd = 'tmsh create asm policy {0} policy-template {1}'.format(full_name, self.want.template) - uri = "https://{0}:{1}/mgmt/tm/util/bash/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - args = dict( - command='run', - utilCmdArgs='-c "{0}"'.format(cmd) - ) - resp = self.client.api.post(uri, json=args) - - try: - response = resp.json() - if 'commandResult' in response: - if 'Unexpected Error' in response['commandResult']: - raise F5ModuleError(response['commandResult']) - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - def create_on_device(self): - params = self.changes.api_params() - params['name'] = self.want.name - params['partition'] = self.want.partition - # we need to remove active from params as API will raise an error if the active is set to True, - # policies can only be activated via apply-policy task endpoint. - params.pop('active') - uri = "https://{0}:{1}/mgmt/tm/asm/policies/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.post(uri, json=params) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] in [400, 401, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - time.sleep(2) - return response['selfLink'] - - def remove_from_device(self): - policy_id = self._get_policy_id() - uri = "https://{0}:{1}/mgmt/tm/asm/policies/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - policy_id - ) - response = self.client.api.delete(uri) - if response.status in [200, 201]: - return True - raise F5ModuleError(response.content) - - -class ModuleManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = F5RestClient(**self.module.params) - self.kwargs = kwargs - - def exec_module(self): - if not module_provisioned(self.client, 'asm'): - raise F5ModuleError( - "ASM must be provisioned to use this module." - ) - if self.version_is_less_than_13(): - manager = self.get_manager('v1') - else: - manager = self.get_manager('v2') - return manager.exec_module() - - def get_manager(self, type): - if type == 'v1': - return V1Manager(**self.kwargs) - elif type == 'v2': - return V2Manager(**self.kwargs) - - def version_is_less_than_13(self): - version = tmos_version(self.client) - if LooseVersion(version) < LooseVersion('13.0.0'): - return True - else: - return False - - -class V1Manager(BaseManager): - def __init__(self, *args, **kwargs): - module = kwargs.get('module', None) - client = F5RestClient(**module.params) - super(V1Manager, self).__init__(client=client, module=module) - self.want = V1Parameters(params=module.params, client=client) - - def create_from_file(self): - self.import_to_device() - self.remove_temp_policy_from_device() - - def create_from_template(self): - self.create_from_template_on_device() - - -class V2Manager(BaseManager): - def __init__(self, *args, **kwargs): - module = kwargs.get('module', None) - client = F5RestClient(**module.params) - super(V2Manager, self).__init__(client=client, module=module) - self.want = V2Parameters(params=module.params, client=client) - - def create_from_template(self): - if not self.create_from_template_on_device(): - return False - - def create_from_file(self): - if not self.import_to_device(): - return False - self.remove_temp_policy_from_device() - - -class ArgumentSpec(object): - def __init__(self): - self.template_map = [ - 'ActiveSync v1.0 v2.0 (http)', - 'ActiveSync v1.0 v2.0 (https)', - 'Comprehensive', - 'Drupal', - 'Fundamental', - 'Joomla', - 'LotusDomino 6.5 (http)', - 'LotusDomino 6.5 (https)', - 'OWA Exchange 2003 (http)', - 'OWA Exchange 2003 (https)', - 'OWA Exchange 2003 with ActiveSync (http)', - 'OWA Exchange 2003 with ActiveSync (https)', - 'OWA Exchange 2007 (http)', - 'OWA Exchange 2007 (https)', - 'OWA Exchange 2007 with ActiveSync (http)', - 'OWA Exchange 2007 with ActiveSync (https)', - 'OWA Exchange 2010 (http)', - 'OWA Exchange 2010 (https)', - 'Oracle 10g Portal (http)', - 'Oracle 10g Portal (https)', - 'Oracle Applications 11i (http)', - 'Oracle Applications 11i (https)', - 'PeopleSoft Portal 9 (http)', - 'PeopleSoft Portal 9 (https)', - 'Rapid Deployment Policy', - 'SAP NetWeaver 7 (http)', - 'SAP NetWeaver 7 (https)', - 'SharePoint 2003 (http)', - 'SharePoint 2003 (https)', - 'SharePoint 2007 (http)', - 'SharePoint 2007 (https)', - 'SharePoint 2010 (http)', - 'SharePoint 2010 (https)', - 'Vulnerability Assessment Baseline', - 'Wordpress', - ] - self.supports_check_mode = True - argument_spec = dict( - name=dict( - required=True, - ), - file=dict(type='path'), - template=dict( - choices=self.template_map - ), - active=dict( - type='bool' - ), - state=dict( - default='present', - choices=['present', 'absent'] - ), - partition=dict( - default='Common', - fallback=(env_fallback, ['F5_PARTITION']) - ) - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec, - supports_check_mode=spec.supports_check_mode, - mutually_exclusive=[ - ['file', 'template'] - ] - ) - - client = F5RestClient(**module.params) - - try: - mm = ModuleManager(module=module) - results = mm.exec_module() - module.exit_json(**results) - except F5ModuleError as ex: - module.fail_json(msg=str(ex)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_device_facts.py b/plugins/modules/network/f5/bigip_device_facts.py deleted file mode 120000 index 0b7da78370..0000000000 --- a/plugins/modules/network/f5/bigip_device_facts.py +++ /dev/null @@ -1 +0,0 @@ -bigip_device_info.py \ No newline at end of file diff --git a/plugins/modules/network/f5/bigip_device_info.py b/plugins/modules/network/f5/bigip_device_info.py deleted file mode 100644 index 957ecbd2be..0000000000 --- a/plugins/modules/network/f5/bigip_device_info.py +++ /dev/null @@ -1,16267 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright: (c) 2017, F5 Networks Inc. -# Copyright: (c) 2013, Matt Hite -# 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': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigip_device_info -short_description: Collect information from F5 BIG-IP devices -description: - - Collect information from F5 BIG-IP devices. - - This module was called C(bigip_device_facts) before Ansible 2.9. The usage did not change. -options: - gather_subset: - description: - - When supplied, this argument will restrict the information returned to a given subset. - - Can specify a list of values to include a larger subset. - - Values can also be used with an initial C(!) to specify that a specific subset - should not be collected. - type: list - required: True - choices: - - all - - monitors - - profiles - - asm-policy-stats - - asm-policies - - asm-server-technologies - - asm-signature-sets - - client-ssl-profiles - - devices - - device-groups - - external-monitors - - fasthttp-profiles - - fastl4-profiles - - gateway-icmp-monitors - - gtm-pools - - gtm-servers - - gtm-wide-ips - - gtm-a-pools - - gtm-a-wide-ips - - gtm-aaaa-pools - - gtm-aaaa-wide-ips - - gtm-cname-pools - - gtm-cname-wide-ips - - gtm-mx-pools - - gtm-mx-wide-ips - - gtm-naptr-pools - - gtm-naptr-wide-ips - - gtm-srv-pools - - gtm-srv-wide-ips - - http-monitors - - https-monitors - - http-profiles - - iapp-services - - iapplx-packages - - icmp-monitors - - interfaces - - internal-data-groups - - irules - - ltm-pools - - ltm-policies - - nodes - - oneconnect-profiles - - partitions - - provision-info - - self-ips - - server-ssl-profiles - - software-volumes - - software-images - - software-hotfixes - - ssl-certs - - ssl-keys - - system-db - - system-info - - tcp-monitors - - tcp-half-open-monitors - - tcp-profiles - - traffic-groups - - trunks - - udp-profiles - - users - - vcmp-guests - - virtual-addresses - - virtual-servers - - vlans - - "!all" - - "!monitors" - - "!profiles" - - "!asm-policy-stats" - - "!asm-policies" - - "!asm-server-technologies" - - "!asm-signature-sets" - - "!client-ssl-profiles" - - "!devices" - - "!device-groups" - - "!external-monitors" - - "!fasthttp-profiles" - - "!fastl4-profiles" - - "!gateway-icmp-monitors" - - "!gtm-pools" - - "!gtm-servers" - - "!gtm-wide-ips" - - "!gtm-a-pools" - - "!gtm-a-wide-ips" - - "!gtm-aaaa-pools" - - "!gtm-aaaa-wide-ips" - - "!gtm-cname-pools" - - "!gtm-cname-wide-ips" - - "!gtm-mx-pools" - - "!gtm-mx-wide-ips" - - "!gtm-naptr-pools" - - "!gtm-naptr-wide-ips" - - "!gtm-srv-pools" - - "!gtm-srv-wide-ips" - - "!http-monitors" - - "!https-monitors" - - "!http-profiles" - - "!iapp-services" - - "!iapplx-packages" - - "!icmp-monitors" - - "!interfaces" - - "!internal-data-groups" - - "!irules" - - "!ltm-pools" - - "!ltm-policies" - - "!nodes" - - "!oneconnect-profiles" - - "!partitions" - - "!provision-info" - - "!self-ips" - - "!server-ssl-profiles" - - "!software-volumes" - - "!software-images" - - "!software-hotfixes" - - "!ssl-certs" - - "!ssl-keys" - - "!system-db" - - "!system-info" - - "!tcp-monitors" - - "!tcp-half-open-monitors" - - "!tcp-profiles" - - "!traffic-groups" - - "!trunks" - - "!udp-profiles" - - "!users" - - "!vcmp-guests" - - "!virtual-addresses" - - "!virtual-servers" - - "!vlans" - aliases: ['include'] -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -author: - - Tim Rupp (@caphrim007) - - Wojciech Wypior (@wojtek0806) -''' - -EXAMPLES = r''' -- name: Collect BIG-IP information - bigip_device_info: - gather_subset: - - interfaces - - vlans - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Collect all BIG-IP information - bigip_device_info: - gather_subset: - - all - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Collect all BIG-IP information except trunks - bigip_device_info: - gather_subset: - - all - - "!trunks" - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost -''' - -RETURN = r''' -asm_policy_stats: - description: Miscellaneous ASM policy related information. - returned: When C(asm-policy-stats) is specified in C(gather_subset). - type: complex - contains: - policies: - description: - - The total number of ASM policies on the device. - returned: queried - type: int - sample: 3 - policies_active: - description: - - The number of ASM policies that are marked as active. - returned: queried - type: int - sample: 3 - policies_attached: - description: - - The number of ASM policies that are attached to virtual servers. - returned: queried - type: int - sample: 1 - policies_inactive: - description: - - The number of ASM policies that are marked as inactive. - returned: queried - type: int - sample: 0 - policies_unattached: - description: - - The number of ASM policies that are not attached to a virtual server. - returned: queried - type: int - sample: 3 - sample: hash/dictionary of values -asm_policies: - description: Detailed information for ASM policies present on device. - returned: When C(asm-policies) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/foo_policy - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: foo_policy - policy_id: - description: - - Generated ID of the ASM policy resource. - returned: queried - type: str - sample: l0Ckxe-7yHsXp8U5tTgbFQ - active: - description: - - Indicates if an ASM policy is active. - returned: queried - type: bool - sample: yes - protocol_independent: - description: - - Indicates if the ASM policy differentiates between HTTP/WS and HTTPS/WSS URLs. - returned: queried - type: bool - sample: no - has_parent: - description: - - Indicates if the ASM policy is a child of another ASM policy. - returned: queried - type: bool - sample: no - type: - description: - - The type of policy, can be C(Security) or C(Parent). - returned: queried - type: str - sample: security - virtual_servers: - description: - - Virtual server or servers which have this policy assigned to them. - returned: queried - type: list - sample: ['/Common/foo_VS/'] - allowed_response_codes: - description: - - Lists the response status codes between 400 and 599 that the security profile considers legal. - returned: queried - type: list - sample: ['400', '404'] - description: - description: - - Description of the resource. - returned: queried - type: str - sample: Significant Policy Description - learning_mode: - description: - - Determine how the policy is built. - returned: queried - type: str - sample: manual - enforcement_mode: - description: - - Specifies whether blocking is active or inactive for the ASM policy. - returned: queried - type: str - sample: blocking - trust_xff: - description: - - Indicates the system has confidence in an XFF (X-Forwarded-For) header in the request. - returned: queried - type: bool - sample: yes - custom_xff_headers: - description: - - List of custom XFF headers trusted by the system. - returned: queried - type: str - sample: asm-proxy1 - case_insensitive: - description: - - Indicates if the ASM policy treats file types, URLs, and parameters as case sensitive. - returned: queried - type: bool - sample: yes - signature_staging: - description: - - Specifies if the staging feature is active on the ASM policy. - returned: queried - type: bool - sample: yes - place_signatures_in_staging: - description: - - Specifies if the system places new or updated signatures in staging - for the number of days specified in the enforcement readiness period. - returned: queried - type: bool - sample: no - enforcement_readiness_period: - description: - - Period in days both security policy entities and attack signatures - remain in staging mode before the system suggests to enforce them. - returned: queried - type: int - sample: 8 - path_parameter_handling: - description: - - Specifies how the system handles path parameters that are attached to path segments in URIs. - returned: queried - type: str - sample: ignore - trigger_asm_irule_event: - description: - - Indicates if iRule event is enabled. - returned: queried - type: str - sample: disabled - inspect_http_uploads: - description: - - Specify if the system should inspect all http uploads. - returned: queried - type: bool - sample: yes - mask_credit_card_numbers_in_request: - description: - - Indicates if the system masks credit card numbers. - returned: queried - type: bool - sample: no - maximum_http_header_length: - description: - - Maximum length of an HTTP header name and value that the system processes. - returned: queried - type: int - sample: 8192 - use_dynamic_session_id_in_url: - description: - - Specifies how the security policy processes URLs that use dynamic sessions. - returned: queried - type: bool - sample: no - maximum_cookie_header_length: - description: - - Maximum length of a cookie header name and value that the system processes. - returned: queried - type: int - sample: 8192 - application_language: - description: - - The language encoding for the web application. - returned: queried - type: str - sample: utf-8 - disallowed_geolocations: - description: - - Displays countries that may not access the web application. - returned: queried - type: str - sample: Argentina - csrf_protection_enabled: - description: - - Specifies if CSRF protection is active on the ASM policy. - returned: queried - type: bool - sample: yes - csrf_protection_ssl_only: - description: - - Specifies that only HTTPS URLs will be checked for CSRF protection. - returned: queried - type: bool - sample: yes - csrf_protection_expiration_time_in_seconds: - description: - - Specifies how long, in seconds, a configured CSRF token is valid before it expires. - returned: queried - type: int - sample: 600 - csrf_urls: - description: - - Specifies a list of URLs for CSRF token verification. - - In version 13.0.0 and above this has become a sub-collection and a list of dictionaries. - - In version 12.x this is a list of simple strings. - returned: queried - type: complex - contains: - csrf_url_required_parameters: - description: - - Indicates whether to ignore or require one of the specified parameters is present - in a request when checking if the URL entry matches the request. - returned: queried - type: str - sample: ignore - csrf_url_parameters_list: - description: - - List of parameters to look for in a request when checking if the URL entry matches the request. - returned: queried - type: list - sample: ['fooparam'] - csrf_url: - description: - - Specifies an URL to protect. - returned: queried - type: str - sample: ['/foo.html'] - csrf_url_method: - description: - - Method for the specified URL. - returned: queried - type: str - sample: POST - csrf_url_enforcement_action: - description: - - Indicates the action specified for the system to take when the URL entry matches. - returned: queried - type: str - sample: none - csrf_url_id: - description: - - Specified the generated ID for the configured CSRF url resource. - returned: queried - type: str - sample: l0Ckxe-7yHsXp8U5tTgbFQ - csrf_url_wildcard_order: - description: - - Specified the order in which the wildcard URLs are enforced. - returned: queried - type: str - sample: 1 - sample: hash/dictionary of values -asm_server_technologies: - description: Detailed information for ASM server technologies present on device. - returned: When C(asm-server-technologies) is specified in C(gather_subset). - type: complex - contains: - id: - description: - - Displays the generated ID for the server technology resource. - returned: queried - type: str - sample: l0Ckxe-7yHsXp8U5tTgbFQ - server_technology_name: - description: - - Human friendly name of the server technology resource. - returned: queried - type: str - sample: Wordpress - server_technology_references: - description: - - List of dictionaries containing API self links of the associated technology resources. - returned: queried - type: complex - contains: - link: - description: - - A self link to an associated server technology. - sample: https://localhost/mgmt/tm/asm/server-technologies/NQG7CT02OBC2cQWbnP7T-A?ver=13.1.0 - sample: hash/dictionary of values -asm_signature_sets: - description: Detailed information for ASM signature sets present on device. - returned: When C(asm-signature-sets) is specified in C(gather_subset). - type: complex - contains: - name: - description: - - Name of the signature set - returned: queried - type: str - sample: WebSphere signatures - id: - description: - - Displays the generated ID for the signature set resource. - returned: queried - type: str - sample: l0Ckxe-7yHsXp8U5tTgbFQ - type: - description: - - The method used to select signatures to be a part of the signature set. - returned: queried - type: str - sample: filter-based - category: - description: - - Displays the category of the signature set. - returned: queried - type: str - sample: filter-based - is_user_defined: - description: - - Specifies that this signature set was added by a user. - returned: queried - type: bool - sample: no - assign_to_policy_by_default: - description: - - Indicates whether the system assigns this signature set to a new created security policy by default. - returned: queried - type: bool - sample: yes - default_alarm: - description: - - Displays whether the security policy logs the request data in the Statistics - screen if a request matches a signature that is included in the signature set - returned: queried - type: bool - sample: yes - default_block: - description: - - Displays, when the security policy's enforcement mode is Blocking, - how the system treats requests that match a signature included in the signature set. - returned: queried - type: bool - sample: yes - default_learn: - description: - - Displays whether the security policy learns all requests that match a signature - that is included in the signature set. - returned: queried - type: bool - sample: yes - sample: hash/dictionary of values -client_ssl_profiles: - description: Client SSL Profile related information. - returned: When C(client-ssl-profiles) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/bigip02.internal - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: bigip02.internal - alert_timeout: - description: - - Maximum time period in seconds to keep the SSL session active after alert - message is sent, or indefinite. - returned: queried - type: int - sample: 0 - allow_non_ssl: - description: - - Enables or disables non-SSL connections. - returned: queried - type: bool - sample: yes - authenticate_depth: - description: - - Specifies the authenticate depth. This is the client certificate chain maximum traversal depth. - returned: queried - type: int - sample: 9 - authenticate_frequency: - description: - - Specifies how often the system authenticates a user. - returned: queried - type: str - sample: once - ca_file: - description: - - Specifies the certificate authority (CA) file name. - returned: queried - type: str - sample: /Common/default-ca.crt - cache_size: - description: - - Specifies the SSL session cache size. - returned: queried - type: int - sample: 262144 - cache_timeout: - description: - - Specifies the SSL session cache timeout value. - returned: queried - type: int - sample: 3600 - certificate_file: - description: - - Specifies the name of the certificate installed on the traffic - management system for the purpose of terminating or initiating - an SSL connection. - returned: queried - type: str - sample: /Common/default.crt - chain_file: - description: - - Specifies or builds a certificate chain file that a client can - use to authenticate the profile. - returned: queried - type: str - sample: /Common/ca-chain.crt - ciphers: - description: - - Specifies a list of cipher names. - returned: queried - type: str - sample: ['DEFAULT'] - crl_file: - description: - - Specifies the certificate revocation list file name. - returned: queried - type: str - sample: /Common/default.crl - parent: - description: - - Parent of the profile - returned: queried - type: str - sample: /Common/clientssl - description: - description: - - Description of the profile. - returned: queried - type: str - sample: My profile - modssl_methods: - description: - - Enables or disables ModSSL method emulation. - returned: queried - type: bool - sample: no - peer_certification_mode: - description: - - Specifies the peer certificate mode. - returned: queried - type: str - sample: ignore - sni_require: - description: - - When this option is C(yes), a client connection that does not - specify a known server name or does not support SNI extension will - be rejected. - returned: queried - type: bool - sample: no - sni_default: - description: - - When C(yes), this profile is the default SSL profile when the server - name in a client connection does not match any configured server - names, or a client connection does not specify any server name at - all. - returned: queried - type: bool - sample: yes - strict_resume: - description: - - Enables or disables strict-resume. - returned: queried - type: bool - sample: yes - profile_mode_enabled: - description: - - Specifies the profile mode, which enables or disables SSL - processing. - returned: queried - type: bool - sample: yes - renegotiation_maximum_record_delay: - description: - - Maximum number of SSL records that the traffic - management system can receive before it renegotiates an SSL - session. - returned: queried - type: int - sample: 0 - renegotiation_period: - description: - - Number of seconds required to renegotiate an SSL - session. - returned: queried - type: int - sample: 0 - renegotiation: - description: - - Specifies whether renegotiations are enabled. - returned: queried - type: bool - sample: yes - server_name: - description: - - Specifies the server names to be matched with SNI (server name - indication) extension information in ClientHello from a client - connection. - returned: queried - type: str - sample: bigip01 - session_ticket: - description: - - Enables or disables session-ticket. - returned: queried - type: bool - sample: no - unclean_shutdown: - description: - - Whether to force the SSL profile to perform a clean shutdown of all SSL - connections or not - returned: queried - type: bool - sample: no - retain_certificate: - description: - - APM module requires storing certificate in SSL session. When - C(no), certificate will not be stored in SSL session. - returned: queried - type: bool - sample: yes - secure_renegotiation_mode: - description: - - Specifies the secure renegotiation mode. - returned: queried - type: str - sample: require - handshake_timeout: - description: - - Specifies the handshake timeout in seconds. - returned: queried - type: int - sample: 10 - forward_proxy_certificate_extension_include: - description: - - Specifies the extensions of the web server certificates to be - included in the generated certificates using SSL Forward Proxy. - returned: queried - type: list - sample: ["basic-constraints", "subject-alternative-name"] - forward_proxy_certificate_lifespan: - description: - - Specifies the lifespan of the certificate generated using the SSL - forward proxy feature. - returned: queried - type: int - sample: 30 - forward_proxy_lookup_by_ipaddr_port: - description: - - Specifies whether to perform certificate look up by IP address and - port number. - returned: queried - type: bool - sample: no - forward_proxy_enabled: - description: - - Enables or disables SSL forward proxy feature. - returned: queried - type: bool - sample: yes - forward_proxy_ca_passphrase: - description: - - Specifies the passphrase of the key file that is used as the - certification authority key when SSL forward proxy feature is - enabled. - returned: queried - type: str - forward_proxy_ca_certificate_file: - description: - - Specifies the name of the certificate file that is used as the - certification authority certificate when SSL forward proxy feature - is enabled. - returned: queried - type: str - forward_proxy_ca_key_file: - description: - - Specifies the name of the key file that is used as the - certification authority key when SSL forward proxy feature is - enabled. - returned: queried - type: str - sample: hash/dictionary of values -devices: - description: Device related information. - returned: When C(devices) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/bigip02.internal - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: bigip02.internal - active_modules: - description: - - The currently licensed and provisioned modules on the device. - returned: queried - type: list - sample: ["DNS Services (LAB)", "PSM, VE"] - base_mac_address: - description: - - Media Access Control address (MAC address) of the device. - returned: queried - type: str - sample: "fa:16:3e:c3:42:6f" - build: - description: - - The minor version information of the total product version. - returned: queried - type: str - sample: 0.0.1 - chassis_id: - description: - - Serial number of the device. - returned: queried - type: str - sample: 11111111-2222-3333-444444444444 - chassis_type: - description: - - Displays the chassis type. The possible values are C(individual) and C(viprion). - returned: queried - type: str - sample: individual - comment: - description: - - User comments about the device. - returned: queried - type: str - sample: My device - configsync_address: - description: - - IP address used for configuration synchronization. - returned: queried - type: str - sample: 10.10.10.10 - contact: - description: - - Administrator contact information. - returned: queried - type: str - sample: The User - description: - description: - - Description of the device. - returned: queried - type: str - sample: My device - edition: - description: - - Displays the software edition. - returned: queried - type: str - sample: Point Release 7 - failover_state: - description: - - Device failover state. - returned: queried - type: str - sample: active - hostname: - description: - - Device hostname - returned: queried - type: str - sample: bigip02.internal - location: - description: - - Specifies the physical location of the device. - returned: queried - type: str - sample: London - management_address: - description: - - IP address of the management interface. - returned: queried - type: str - sample: 3.3.3.3 - marketing_name: - description: - - Marketing name of the device platform. - returned: queried - type: str - sample: BIG-IP Virtual Edition - multicast_address: - description: - - Specifies the multicast IP address used for failover. - returned: queried - type: str - sample: 4.4.4.4 - optional_modules: - description: - - Modules that are available for the current platform, but are not currently licensed. - returned: queried - type: list - sample: ["App Mode (TMSH Only, No Root/Bash)", "BIG-IP VE, Multicast Routing"] - platform_id: - description: - - Displays the device platform identifier. - returned: queried - type: str - sample: Z100 - primary_mirror_address: - description: - - Specifies the IP address used for state mirroring. - returned: queried - type: str - sample: 5.5.5.5 - product: - description: - - Displays the software product name. - returned: queried - type: str - sample: BIG-IP - secondary_mirror_address: - description: - - Secondary IP address used for state mirroring. - returned: queried - type: str - sample: 2.2.2.2 - self: - description: - - Whether this device is the one that was queried for information, or not. - returned: queried - type: bool - sample: yes - software_version: - description: - - Displays the software version number. - returned: queried - type: str - sample: 13.1.0.7 - timelimited_modules: - description: - - Displays the licensed modules that are time-limited. - returned: queried - type: list - sample: ["IP Intelligence, 3Yr, ...", "PEM URL Filtering, 3Yr, ..."] - timezone: - description: - - Displays the time zone configured on the device. - returned: queried - type: str - sample: UTC - unicast_addresses: - description: - - Specifies the entire set of unicast addresses used for failover. - returned: queried - type: complex - contains: - effective_ip: - description: - - The IP address that peers can use to reach this unicast address IP. - returned: queried - type: str - sample: 5.4.3.5 - effective_port: - description: - - The port that peers can use to reach this unicast address. - returned: queried - type: int - sample: 1026 - ip: - description: - - The IP address that the failover daemon will listen on for packets from its peers. - returned: queried - type: str - sample: 5.4.3.5 - port: - description: - - The IP port that the failover daemon uses to accept packets from its peers. - returned: queried - type: int - sample: 1026 - sample: hash/dictionary of values -device_groups: - description: Device group related information. - returned: When C(device-groups) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/fasthttp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: fasthttp - autosync_enabled: - description: - - Whether the device group automatically synchronizes configuration data to its members. - returned: queried - type: bool - sample: no - description: - description: - - Description of the device group. - returned: queried - type: str - sample: My device group - devices: - description: - - List of devices that are in the group. Devices are listed by their C(full_path). - returned: queried - type: list - sample: [/Common/bigip02.internal] - full_load_on_sync: - description: - - Specifies that the entire configuration for a device group is sent when configuration - synchronization is performed. - returned: queried - type: bool - sample: yes - incremental_config_sync_size_maximum: - description: - - Specifies the maximum size (in KB) to devote to incremental config sync cached transactions. - returned: queried - type: int - sample: 1024 - network_failover_enabled: - description: - - Specifies whether network failover is used. - returned: queried - type: bool - sample: yes - type: - description: - - Specifies the type of device group. - returned: queried - type: str - sample: sync-only - asm_sync_enabled: - description: - - Specifies whether to synchronize ASM configurations of device group members. - returned: queried - type: bool - sample: yes - sample: hash/dictionary of values -external_monitors: - description: External monitor related information. - returned: When C(external-monitors) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/external - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: external - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: external - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My monitor - destination: - description: - - Specifies the IP address and service port of the resource that is - the destination of this monitor. - returned: queried - type: str - sample: "*:*" - args: - description: - - Specifies any command-line arguments that the script requires. - returned: queried - type: str - sample: arg1 arg2 arg3 - external_program: - description: - - Specifies the name of the file for the monitor to use. - returned: queried - type: str - sample: /Common/arg_example - variables: - description: - - Specifies any variables that the script requires. - type: complex - sample: { "key1": "val", "key_2": "val 2" } - interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when either the resource is down or the status - of the resource is unknown. - returned: queried - type: int - sample: 5 - manual_resume: - description: - - Specifies whether the system automatically changes the status of a - resource to up at the next successful monitor check. - returned: queried - type: bool - sample: yes - time_until_up: - description: - - Specifies the amount of time, in seconds, after the first - successful response before a node is marked up. - returned: queried - type: int - sample: 0 - timeout: - description: - - Specifies the number of seconds the target has in which to respond - to the monitor request. - returned: queried - type: int - sample: 16 - up_interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when the resource is up. - returned: queried - type: int - sample: 0 - sample: hash/dictionary of values -fasthttp_profiles: - description: FastHTTP profile related information. - returned: When C(fasthttp-profiles) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/fasthttp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: fasthttp - client_close_timeout: - description: - - Number of seconds after which the system closes a client connection, when - the system either receives a client FIN packet or sends a FIN packet to the client. - returned: queried - type: int - sample: 5 - oneconnect_idle_timeout_override: - description: - - Number of seconds after which a server-side connection in a OneConnect pool - is eligible for deletion, when the connection has no traffic. - returned: queried - type: int - sample: 0 - oneconnect_maximum_reuse: - description: - - Maximum number of times that the system can re-use a current connection. - returned: queried - type: int - sample: 0 - oneconnect_maximum_pool_size: - description: - - Maximum number of connections to a load balancing pool. - returned: queried - type: int - sample: 2048 - oneconnect_minimum_pool_size: - description: - - Minimum number of connections to a load balancing pool. - returned: queried - type: int - sample: 0 - oneconnect_replenish': - description: - - Specifies, when C(yes), that the system will not keep a steady-state maximum of - connections to the back-end unless the number of connections to the pool have - dropped beneath the C(minimum_pool_size) specified in the profile. - returned: queried - type: bool - sample: yes - oneconnect_ramp_up_increment: - description: - - The increment in which the system makes additional connections available, when - all available connections are in use. - returned: queried - type: int - sample: 4 - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: fasthttp - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My profile - force_http_1_0_response: - description: - - Specifies, when C(yes), that the server sends responses to clients in the HTTP/1.0 - format. - returned: queried - type: bool - sample: no - request_header_insert: - description: - - A string that the system inserts as a header in an HTTP request. If the header - exists already, the system does not replace it. - returned: queried - type: str - sample: "X-F5-Authentication: foo" - http_1_1_close_workarounds: - description: - - Specifies, when C(yes), that the server uses workarounds for HTTP 1.1 close issues. - returned: queried - type: bool - sample: no - idle_timeout: - description: - - Length of time that a connection is idle (has no traffic) before the connection - is eligible for deletion. - returned: queried - type: int - sample: 300 - insert_x_forwarded_for: - description: - - Whether the system inserts the X-Forwarded-For header in an HTTP request with the - client IP address, to use with connection pooling. - returned: queried - type: bool - sample: no - maximum_header_size: - description: - - Maximum amount of HTTP header data that the system buffers before making a load - balancing decision. - returned: queried - type: int - sample: 32768 - maximum_requests: - description: - - Maximum number of requests that the system can receive on a client-side connection, - before the system closes the connection. - returned: queried - type: int - sample: 0 - maximum_segment_size_override: - description: - - Maximum segment size (MSS) override for server-side connections. - returned: queried - type: int - sample: 0 - receive_window_size: - description: - - Amount of data the BIG-IP system can accept without acknowledging the server. - returned: queried - type: int - sample: 0 - reset_on_timeout: - description: - - Specifies, when C(yes), that the system sends a reset packet (RST) in addition to - deleting the connection, when a connection exceeds the idle timeout value. - returned: queried - type: bool - sample: yes - server_close_timeout: - description: - - Number of seconds after which the system closes a client connection, when the system - either receives a server FIN packet or sends a FIN packet to the server. - returned: queried - type: int - sample: 5 - server_sack: - description: - - Whether the BIG-IP system processes Selective ACK (Sack) packets in cookie responses - from the server. - returned: queried - type: bool - sample: no - server_timestamp: - description: - - Whether the BIG-IP system processes timestamp request packets in cookie responses - from the server. - returned: queried - type: bool - sample: no - unclean_shutdown: - description: - - How the system handles closing connections. Values provided may be C(enabled), C(disabled), - or C(fast). - returned: queried - type: str - sample: enabled - sample: hash/dictionary of values -fastl4_profiles: - description: FastL4 profile related information. - returned: When C(fastl4-profiles) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/fastl4 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: fastl4 - client_timeout: - description: - - Specifies late binding client timeout in seconds. - - This is the number of seconds allowed for a client to transmit enough data to - select a server pool. - - If this timeout expires, the timeout-recovery option dictates whether - to drop the connection or fallback to the normal FastL4 load-balancing method - to pick a server pool. - returned: queried - type: int - sample: 30 - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: fastl4 - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My profile - explicit_flow_migration: - description: - - Specifies whether to have the iRule code determine exactly when - the FIX stream drops down to the ePVA hardware. - returned: queried - type: bool - sample: yes - hardware_syn_cookie: - description: - - Enables or disables hardware SYN cookie support when PVA10 is present on the system. - - This option is deprecated in version 13.0.0 and is replaced by C(syn-cookie-enable). - returned: queried - type: bool - sample: no - idle_timeout: - description: - - Specifies the number of seconds that a connection is idle before the connection is - eligible for deletion. - - Values will be in the range of 0 to 4294967295 (inclusive). - - C(0) is equivalent to the TMUI value "immediate". - - C(4294967295) is equivalent to the TMUI value "indefinite". - returned: queried - type: int - sample: 300 - dont_fragment_flag: - description: - - Describes the Don't Fragment (DF) bit setting in the IP Header of - the outgoing TCP packet. - - When C(pmtu), sets the outgoing IP Header DF bit based on IP pmtu - setting(tm.pathmtudiscovery). - - When C(preserve), sets the outgoing Packet's IP Header DF bit to be same as incoming - IP Header DF bit. - - When C(set), sets the outgoing packet's IP Header DF bit. - - When C(clear), clears the outgoing packet's IP Header DF bit. - returned: queried - type: str - sample: pmtu - ip_tos_to_client: - description: - - Specifies an IP Type of Service (ToS) number for the client-side. - - This option specifies the ToS level that the traffic management - system assigns to IP packets when sending them to clients. - returned: queried - type: str or int - sample: 200 - ip_tos_to_server: - description: - - Specifies an IP ToS number for the server side. - - This option specifies the ToS level that the traffic management system assigns - to IP packets when sending them to servers. - returned: queried - type: str or int - sample: pass-through - ttl_mode: - description: - - Describe the outgoing TCP packet's IP Header TTL mode. - - When C(proxy), sets the outgoing IP Header TTL value to 255/64 for ipv4/ipv6 - respectively. - - When C(preserve), sets the outgoing IP Header TTL value to be same as the - incoming IP Header TTL value. - - When C(decrement), sets the outgoing IP Header TTL value to be one less than - the incoming TTL value. - - When C(set), sets the outgoing IP Header TTL value to a specific value(as - specified by C(ttl_v4) or C(ttl_v6). - returned: queried - type: str - sample: preserve - ttl_v4: - description: - - Specify the outgoing packet's IP Header TTL value for IPv4 traffic. - - Maximum value that can be specified is 255. - returned: queried - type: int - sample: 200 - ttl_v6: - description: - - Specify the outgoing packet's IP Header TTL value for IPv6 - traffic. - - Maximum value that can be specified is 255. - returned: queried - type: int - sample: 300 - keep_alive_interval: - description: - - Specifies the keep-alive probe interval, in seconds. - - A value of 0 indicates keep-alive is disabled. - returned: queried - type: int - sample: 10 - late_binding: - description: - - Specifies whether to enable or disable intelligent selection of a - back-end server pool. - returned: queried - type: bool - sample: yes - link_qos_to_client: - description: - - Specifies a Link Quality of Service (QoS) (VLAN priority) number - for the client side. - - This option specifies the QoS level that the system assigns to packets - when sending them to clients. - returned: queried - type: int or string - sample: 7 - link_qos_to_server: - description: - - Specifies a Link QoS (VLAN priority) number for the server side. - - This option specifies the QoS level that the system assigns to - packets when sending them to servers. - returned: queried - type: int or string - sample: 5 - loose_close: - description: - - Specifies that the system closes a loosely-initiated connection - when the system receives the first FIN packet from either the - client or the server. - returned: queried - type: bool - sample: no - loose_init: - description: - - Specifies that the system initializes a connection when it - receives any Transmission Control Protocol (TCP) packet, rather - than requiring a SYN packet for connection initiation. - returned: queried - type: bool - sample: yes - mss_override: - description: - - Specifies a maximum segment size (MSS) override for server - connections. Note that this is also the MSS advertised to a client - when a client first connects. - - C(0) (zero), means the option is disabled. Otherwise, the value will be - between 256 and 9162. - returned: queried - type: int - sample: 500 - priority_to_client: - description: - - Specifies internal packet priority for the client side. - - This option specifies the internal packet priority that the system - assigns to packets when sending them to clients. - returned: queried - type: int or string - sample: 300 - priority_to_server: - description: - - Specifies internal packet priority for the server side. - - This option specifies the internal packet priority that the system - assigns to packets when sending them to servers. - returned: queried - type: int or string - sample: 200 - pva_acceleration: - description: - - Specifies the Packet Velocity(r) ASIC acceleration policy. - returned: queried - type: str - sample: full - pva_dynamic_client_packets: - description: - - Specifies the number of client packets before dynamic ePVA - hardware re-offloading occurs. - - Values will be between 0 and 10. - returned: queried - type: int - sample: 8 - pva_dynamic_server_packets: - description: - - Specifies the number of server packets before dynamic ePVA - hardware re-offloading occurs. - - Values will be between 0 and 10. - returned: queried - type: int - sample: 5 - pva_flow_aging: - description: - - Specifies if automatic aging from ePVA flow cache is enabled or not. - returned: queried - type: bool - sample: yes - pva_flow_evict: - description: - - Specifies if this flow can be evicted upon hash collision with a - new flow learn snoop request. - returned: queried - type: bool - sample: no - pva_offload_dynamic: - description: - - Specifies whether PVA flow dynamic offloading is enabled or not. - returned: queried - type: bool - sample: yes - pva_offload_state: - description: - - Specifies at what stage the ePVA performs hardware offload. - - When C(embryonic), implies at TCP CSYN or the first client UDP packet. - - When C(establish), implies TCP 3WAY handshaking or UDP CS round trip are - confirmed. - returned: queried - type: str - sample: embryonic - reassemble_fragments: - description: - - Specifies whether to reassemble fragments. - returned: queried - type: bool - sample: yes - receive_window: - description: - - Specifies the window size to use, in bytes. - - The maximum is 2^31 for window scale enabling. - returned: queried - type: int - sample: 1000 - reset_on_timeout: - description: - - Specifies whether you want to reset connections on timeout. - returned: queried - type: bool - sample: yes - rtt_from_client: - description: - - Enables or disables the TCP timestamp options to measure the round - trip time to the client. - returned: queried - type: bool - sample: no - rtt_from_server: - description: - - Enables or disables the TCP timestamp options to measure the round - trip time to the server. - returned: queried - type: bool - sample: yes - server_sack: - description: - - Specifies whether to support server sack option in cookie response - by default. - returned: queried - type: bool - sample: no - server_timestamp: - description: - - Specifies whether to support server timestamp option in cookie - response by default. - returned: queried - type: bool - sample: yes - software_syn_cookie: - description: - - Enables or disables software SYN cookie support when PVA10 is not present - on the system. - - This option is deprecated in version 13.0.0 and is replaced by - C(syn_cookie_enabled). - returned: queried - type: bool - sample: yes - syn_cookie_enabled: - description: - - Enables syn-cookies capability on this virtual server. - returned: queried - type: bool - sample: no - syn_cookie_mss: - description: - - Specifies a maximum segment size (MSS) for server connections when - SYN Cookie is enabled. - returned: queried - type: int - sample: 2000 - syn_cookie_whitelist: - description: - - Specifies whether or not to use a SYN Cookie WhiteList when doing - software SYN Cookies. - returned: queried - type: bool - sample: no - tcp_close_timeout: - description: - - Specifies a TCP close timeout in seconds. - returned: queried - type: int - sample: 100 - generate_init_seq_number: - description: - - Specifies whether you want to generate TCP sequence numbers on all - SYNs that conform with RFC1948, and allow timestamp recycling. - returned: queried - type: bool - sample: yes - tcp_handshake_timeout: - description: - - Specifies a TCP handshake timeout in seconds. - returned: queried - type: int - sample: 5 - strip_sack: - description: - - Specifies whether you want to block the TCP SackOK option from - passing to the server on an initiating SYN. - returned: queried - type: bool - sample: yes - tcp_time_wait_timeout: - description: - - Specifies a TCP time_wait timeout in milliseconds. - returned: queried - type: int - sample: 60 - tcp_timestamp_mode: - description: - - Specifies how you want to handle the TCP timestamp. - returned: queried - type: str - sample: preserve - tcp_window_scale_mode: - description: - - Specifies how you want to handle the TCP window scale. - returned: queried - type: str - sample: preserve - timeout_recovery: - description: - - Specifies late binding timeout recovery mode. This is the action - to take when late binding timeout occurs on a connection. - - When C(disconnect), only the L7 iRule actions are acceptable to - pick a server. - - When C(fallback), the normal FastL4 load-balancing methods are acceptable - to pick a server. - returned: queried - type: str - sample: fallback - sample: hash/dictionary of values -gateway_icmp_monitors: - description: Gateway ICMP monitor related information. - returned: When C(gateway-icmp-monitors) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/gateway_icmp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: gateway_icmp - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: gateway_icmp - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My monitor - adaptive: - description: - - Whether adaptive response time monitoring is enabled for this monitor. - returned: queried - type: bool - sample: no - adaptive_divergence_type: - description: - - Specifies whether the adaptive-divergence-value is C(relative) or - C(absolute). - returned: queried - type: str - sample: relative - adaptive_divergence_value: - description: - - Specifies how far from mean latency each monitor probe is allowed - to be. - returned: queried - type: int - sample: 25 - adaptive_limit: - description: - - Specifies the hard limit, in milliseconds, which the probe is not - allowed to exceed, regardless of the divergence value. - returned: queried - type: int - sample: 200 - adaptive_sampling_timespan: - description: - - Specifies the size of the sliding window, in seconds, which - records probe history. - returned: queried - type: int - sample: 300 - destination: - description: - - Specifies the IP address and service port of the resource that is - the destination of this monitor. - returned: queried - type: str - sample: "*:*" - interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when either the resource is down or the status - of the resource is unknown. - returned: queried - type: int - sample: 5 - manual_resume: - description: - - Specifies whether the system automatically changes the status of a - resource to up at the next successful monitor check. - returned: queried - type: bool - sample: yes - time_until_up: - description: - - Specifies the amount of time, in seconds, after the first - successful response before a node is marked up. - returned: queried - type: int - sample: 0 - timeout: - description: - - Specifies the number of seconds the target has in which to respond - to the monitor request. - returned: queried - type: int - sample: 16 - transparent: - description: - - Specifies whether the monitor operates in transparent mode. - returned: queried - type: bool - sample: no - up_interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when the resource is up. - returned: queried - type: int - sample: 0 - sample: hash/dictionary of values -gtm_pools: - description: - - GTM pool related information. - - Every "type" of pool has the exact same list of possible information. Therefore, - the list of information here is presented once instead of 6 times. - returned: When any of C(gtm-pools) or C(gtm-*-pools) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/pool1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: pool1 - alternate_mode: - description: - - The load balancing mode that the system uses to load balance name resolution - requests among the members of the pool. - returned: queried - type: str - sample: drop-packet - dynamic_ratio: - description: - - Whether or not the dynamic ratio load balancing algorithm is enabled for this - pool. - returned: queried - type: bool - sample: yes - enabled: - description: - - Is the pool enabled. - returned: queried - type: bool - disabled: - description: - - Is the pool disabled. - returned: queried - type: bool - fallback_mode: - description: - - Specifies the load balancing mode that the system uses to load balance - name resolution amongst the pool members if the preferred and alternate - modes are unsuccessful in picking a pool. - returned: queried - type: str - load_balancing_mode: - description: - - Specifies the preferred load balancing mode that the system uses to load - balance requests across pool members. - returned: queried - type: str - manual_resume: - description: - - Whether manual resume is enabled for this pool. - returned: queried - type: bool - max_answers_returned: - description: - - Maximum number of available virtual servers that the system lists in a - response. - returned: queried - type: int - members: - description: - - Lists of members (and their configurations) in the pool. - returned: queried - type: complex - partition: - description: - - Partition the pool exists on. - returned: queried - qos_hit_ratio: - description: - - Weight of the Hit Ratio performance factor for the QoS dynamic load - balancing method. - returned: queried - type: int - qos_hops: - description: - - Weight of the Hops performance factor when load balancing mode or fallback mode - is QoS. - returned: queried - type: int - qos_kilobytes_second: - description: - - Weight assigned to Kilobytes per Second performance factor when load balancing - option is QoS. - returned: queried - type: int - qos_lcs: - description: - - Weight assign to the Link Capacity performance factor when load balancing option - is QoS. - returned: queried - type: int - qos_packet_rate: - description: - - Weight assign to the Packet Rate performance factor when load balancing option - is QoS. - returned: queried - type: int - qos_rtt: - description: - - Weight assign to the Round Trip Time performance factor when load balancing option - is QoS. - returned: queried - type: int - qos_topology: - description: - - Weight assign to the Topology performance factor when load balancing option - is QoS. - returned: queried - type: int - qos_vs_capacity: - description: - - Weight assign to the Virtual Server performance factor when load balancing option - is QoS. - returned: queried - type: int - qos_vs_score: - description: - - Weight assign to the Virtual Server Score performance factor when load balancing - option is QoS. - returned: queried - type: int - ttl: - description: - - Number of seconds that the IP address, once found, is valid. - returned: queried - type: int - verify_member_availability: - description: - - Whether or not the system verifies the availability of the members before - sending a connection to them. - returned: queried - type: bool - sample: hash/dictionary of values -gtm_servers: - description: - - GTM server related information. - returned: When C(gtm-servers) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/server1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: server1 - datacenter: - description: - - Full name of the datacenter this server belongs to. - returned: queried - type: str - enabled: - description: - - Whether the server is enabled. - returned: queried - type: bool - disabled: - description: - - Whether the server is disabled. - returned: queried - type: bool - expose_route_domains: - description: - - Allow the GTM server to auto-discover the LTM virtual servers from all - route domains. - returned: queried - type: bool - iq_allow_path: - description: - - Whether the GTM uses this BIG-IP system to conduct a path probe before - delegating traffic to it. - returned: queried - type: bool - iq_allow_service_check: - description: - - Whether the GTM uses this BIG-IP system to conduct a service check probe - before delegating traffic to it. - returned: queried - type: bool - iq_allow_snmp: - description: - - Whether the GTM uses this BIG-IP system to conduct an SNMP probe - before delegating traffic to it. - returned: queried - type: bool - limit_cpu_usage: - description: - - For a server configured as a generic host, specifies the percent of CPU - usage, otherwise has no effect. - returned: queried - type: int - limit_cpu_usage_status: - description: - - Whether C(limit_cpu_usage) is enabled for this server. - returned: queried - type: bool - limit_max_bps: - description: - - Maximum allowable data throughput rate in bits per second for this server. - returned: queried - type: int - limit_max_bps_status: - description: - - Whether C(limit_max_bps) is enabled for this server. - returned: queried - type: bool - limit_max_connections: - description: - - Maximum number of concurrent connections, combined, for this server. - returned: queried - type: int - limit_max_connections_status: - description: - - Whether C(limit_max_connections) is enabled for this server. - type: bool - limit_max_pps: - description: - - Maximum allowable data transfer rate, in packets per second, for this server. - returned: queried - type: int - limit_max_pps_status: - description: - - Whether C(limit_max_pps) is enabled for this server. - returned: queried - type: bool - limit_mem_available: - description: - - For a server configured as a generic host, specifies the available memory - required by the virtual servers on the server. - - If available memory falls below this limit, the system marks the server as - unavailable. - returned: queried - type: int - limit_mem_available_status: - description: - - Whether C(limit_mem_available) is enabled for this server. - returned: queried - type: bool - link_discovery: - description: - - Specifies whether the system auto-discovers the links for this server. - returned: queried - type: str - monitors: - description: - - Specifies health monitors that the system uses to determine whether this - server is available for load balancing. - returned: queried - type: list - sample: ['/Common/https_443', '/Common/icmp'] - monitor_type: - description: - - Whether one or monitors need to pass, or all monitors need to pass. - returned: queried - type: str - sample: and_list - product: - description: - - Specifies the server type. - returned: queried - type: str - prober_fallback: - description: - - The type of prober to use to monitor this servers resources when the - preferred type is not available. - returned: queried - type: str - prober_preference: - description: - - Specifies the type of prober to use to monitor this servers resources. - returned: queried - type: str - virtual_server_discovery: - description: - - Whether the system auto-discovers the virtual servers for this server. - returned: queried - type: str - addresses: - description: - - Specifies the server IP addresses for the server. - returned: queried - type: complex - devices: - description: - - Specifies the names of the devices that represent this server. - returned: queried - type: complex - virtual_servers: - description: - - Virtual servers that are resources for this server. - returned: queried - type: complex - sample: hash/dictionary of values -gtm_wide_ips: - description: - - GTM Wide IP related information. - - Every "type" of wide-ip has the exact same list of possible information. Therefore, - the list of information here is presented once instead of 6 times. - returned: When any of C(gtm-wide-ips) or C(gtm-*-wide-ips) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/wide1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: wide1 - description: - description: - - Description of the wide ip. - returned: queried - type: str - enabled: - description: - - Whether the Wide IP is enabled. - returned: queried - type: bool - disabled: - description: - - Whether the Wide IP is disabled. - returned: queried - type: bool - failure_rcode: - description: - - Specifies the DNS RCODE used when C(failure_rcode_response) is C(yes). - returned: queried - type: int - failure_rcode_response: - description: - - When C(yes), specifies that the system returns a RCODE response to - Wide IP requests after exhausting all load-balancing methods. - returned: queried - type: bool - failure_rcode_ttl: - description: - - Specifies the negative caching TTL of the SOA for the RCODE response. - returned: queried - type: int - last_resort_pool: - description: - - Specifies which pool, as listed in Pool List, for the system to use as - the last resort pool for the wide IP. - returned: queried - type: str - minimal_response: - description: - - Specifies that the system forms the smallest allowable DNS response to - a query. - returned: queried - type: str - persist_cidr_ipv4: - description: - - Specifies the number of bits the system uses to identify IPv4 addresses - when persistence is enabled. - returned: queried - type: int - persist_cidr_ipv6: - description: - - Specifies the number of bits the system uses to identify IPv6 addresses - when persistence is enabled. - returned: queried - type: int - pool_lb_mode: - description: - - Specifies the load balancing method used to select a pool in this wide IP. - returned: queried - type: str - ttl_persistence: - description: - - Specifies, in seconds, the length of time for which the persistence - entry is valid. - returned: queried - type: int - pools: - description: - - Specifies the pools that this wide IP uses for load balancing. - returned: queried - type: complex - sample: hash/dictionary of values -http_monitors: - description: HTTP monitor related information. - returned: When C(http-monitors) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/http - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: http - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: http - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My monitor - adaptive: - description: - - Whether adaptive response time monitoring is enabled for this monitor. - returned: queried - type: bool - sample: no - adaptive_divergence_type: - description: - - Specifies whether the adaptive-divergence-value is C(relative) or - C(absolute). - returned: queried - type: str - sample: relative - adaptive_divergence_value: - description: - - Specifies how far from mean latency each monitor probe is allowed - to be. - returned: queried - type: int - sample: 25 - adaptive_limit: - description: - - Specifies the hard limit, in milliseconds, which the probe is not - allowed to exceed, regardless of the divergence value. - returned: queried - type: int - sample: 200 - adaptive_sampling_timespan: - description: - - Specifies the size of the sliding window, in seconds, which - records probe history. - returned: queried - type: int - sample: 300 - destination: - description: - - Specifies the IP address and service port of the resource that is - the destination of this monitor. - returned: queried - type: str - sample: "*:*" - interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when either the resource is down or the status - of the resource is unknown. - returned: queried - type: int - sample: 5 - ip_dscp: - description: - - Specifies the differentiated services code point (DSCP). - returned: queried - type: int - sample: 0 - manual_resume: - description: - - Specifies whether the system automatically changes the status of a - resource to up at the next successful monitor check. - returned: queried - type: bool - sample: yes - receive_string: - description: - - Specifies the text string that the monitor looks for in the - returned resource. - returned: queried - type: str - sample: check string - receive_disable_string: - description: - - Specifies a text string that the monitor looks for in the returned - resource. If the text string is matched in the returned resource, - the corresponding node or pool member is marked session disabled. - returned: queried - type: str - sample: check disable string - reverse: - description: - - Specifies whether the monitor operates in reverse mode. When the - monitor is in reverse mode, a successful check marks the monitored - object down instead of up. - returned: queried - type: bool - sample: no - send_string: - description: - - Specifies the text string that the monitor sends to the target - object. - returned: queried - type: str - sample: "GET /\\r\\n" - time_until_up: - description: - - Specifies the amount of time, in seconds, after the first - successful response before a node is marked up. - returned: queried - type: int - sample: 0 - timeout: - description: - - Specifies the number of seconds the target has in which to respond - to the monitor request. - returned: queried - type: int - sample: 16 - transparent: - description: - - Specifies whether the monitor operates in transparent mode. - returned: queried - type: bool - sample: no - up_interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when the resource is up. - returned: queried - type: int - sample: 0 - username: - description: - - Specifies the username, if the monitored target requires - authentication. - returned: queried - type: str - sample: user1 - sample: hash/dictionary of values -https_monitors: - description: HTTPS monitor related information. - returned: When C(https-monitors) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/http - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: http - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: http - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My monitor - adaptive: - description: - - Whether adaptive response time monitoring is enabled for this monitor. - returned: queried - type: bool - sample: no - adaptive_divergence_type: - description: - - Specifies whether the adaptive-divergence-value is C(relative) or - C(absolute). - returned: queried - type: str - sample: relative - adaptive_divergence_value: - description: - - Specifies how far from mean latency each monitor probe is allowed - to be. - returned: queried - type: int - sample: 25 - adaptive_limit: - description: - - Specifies the hard limit, in milliseconds, which the probe is not - allowed to exceed, regardless of the divergence value. - returned: queried - type: int - sample: 200 - adaptive_sampling_timespan: - description: - - Specifies the size of the sliding window, in seconds, which - records probe history. - returned: queried - type: int - sample: 300 - destination: - description: - - Specifies the IP address and service port of the resource that is - the destination of this monitor. - returned: queried - type: str - sample: "*:*" - interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when either the resource is down or the status - of the resource is unknown. - returned: queried - type: int - sample: 5 - ip_dscp: - description: - - Specifies the differentiated services code point (DSCP). - returned: queried - type: int - sample: 0 - manual_resume: - description: - - Specifies whether the system automatically changes the status of a - resource to up at the next successful monitor check. - returned: queried - type: bool - sample: yes - receive_string: - description: - - Specifies the text string that the monitor looks for in the - returned resource. - returned: queried - type: str - sample: check string - receive_disable_string: - description: - - Specifies a text string that the monitor looks for in the returned - resource. If the text string is matched in the returned resource, - the corresponding node or pool member is marked session disabled. - returned: queried - type: str - sample: check disable string - reverse: - description: - - Specifies whether the monitor operates in reverse mode. When the - monitor is in reverse mode, a successful check marks the monitored - object down instead of up. - returned: queried - type: bool - sample: no - send_string: - description: - - Specifies the text string that the monitor sends to the target - object. - returned: queried - type: str - sample: "GET /\\r\\n" - ssl_profile: - description: - - Specifies the SSL profile to use for the HTTPS monitor. - returned: queried - type: str - sample: /Common/serverssl - time_until_up: - description: - - Specifies the amount of time, in seconds, after the first - successful response before a node is marked up. - returned: queried - type: int - sample: 0 - timeout: - description: - - Specifies the number of seconds the target has in which to respond - to the monitor request. - returned: queried - type: int - sample: 16 - transparent: - description: - - Specifies whether the monitor operates in transparent mode. - returned: queried - type: bool - sample: no - up_interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when the resource is up. - returned: queried - type: int - sample: 0 - username: - description: - - Specifies the username, if the monitored target requires - authentication. - returned: queried - type: str - sample: user1 - sample: hash/dictionary of values -http_profiles: - description: HTTP profile related information. - returned: When C(http-profiles) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/http - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: http - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: http - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My profile - accept_xff: - description: - - Enables or disables trusting the client IP address, and statistics - from the client IP address, based on the request's X-Forwarded-For - (XFF) headers, if they exist. - returned: queried - type: bool - sample: yes - allow_truncated_redirects: - description: - - Specifies the pass-through behavior when a redirect lacking the - trailing carriage-return and line feed pair at the end of the headers - is parsed. - - When C(no), will silently drop the invalid HTTP. - returned: queried - type: bool - sample: no - excess_client_headers: - description: - - Specifies the pass-through behavior when C(max_header_count) value is - exceeded by the client. - - When C(reject), rejects the connection. - returned: queried - type: str - sample: reject - excess_server_headers: - description: - - Specifies the pass-through behavior when C(max_header_count) value is - exceeded by the server. - - When C(reject), rejects the connection. - returned: queried - type: str - sample: reject - known_methods: - description: - - Optimizes the behavior of a known HTTP method in the list. - - The default methods include the following HTTP/1.1 methods. CONNECT, - DELETE, GET, HEAD, LOCK, OPTIONS, POST, PROPFIND, PUT, TRACE, UNLOCK. - - If a known method is deleted from the C(known_methods) list, the - BIG-IP system applies the C(unknown_method) setting to manage that traffic. - returned: queried - type: list - sample: ['CONNECT', 'DELETE', ...] - max_header_count: - description: - - Specifies the maximum number of headers the system supports. - returned: queried - type: int - sample: 64 - max_header_size: - description: - - Specifies the maximum size in bytes the system allows for all HTTP - request headers combined, including the request line. - returned: queried - type: int - sample: 32768 - max_requests: - description: - - Specifies the number of requests that the system accepts on a per-connection - basis. - returned: queried - type: int - sample: 0 - oversize_client_headers: - description: - - Specifies the pass-through behavior when the C(max_header_size) value - is exceeded by the client. - returned: queried - type: str - sample: reject - oversize_server_headers: - description: - - Specifies the pass-through behavior when the C(max_header_size) value - is exceeded by the server. - returned: queried - type: str - sample: reject - pipeline_action: - description: - - Enables or disables HTTP/1.1 pipelining. - returned: queried - type: str - sample: allow - unknown_method: - description: - - Specifies the behavior (allow, reject, or pass through) when an unknown - HTTP method is parsed. - returned: queried - type: str - sample: allow - default_connect_handling: - description: - - Specifies the behavior of the proxy service when handling outbound requests. - returned: queried - type: str - sample: deny - hsts_include_subdomains: - description: - - When C(yes), applies the HSTS policy to the HSTS host and its subdomains. - returned: queried - type: bool - sample: yes - hsts_enabled: - description: - - When C(yes), enables the HTTP Strict Transport Security settings. - returned: queried - type: bool - sample: yes - insert_x_forwarded_for: - description: - - When C(yes), specifies that the system inserts an X-Forwarded-For header in - an HTTP request with the client IP address, to use with connection pooling. - returned: queried - type: bool - sample: no - lws_max_columns: - description: - - Specifies the maximum column width for any given line, when inserting an HTTP - header in an HTTP request. - returned: queried - type: int - sample: 80 - onconnect_transformations: - description: - - When C(yes), specifies, that the system performs HTTP header transformations - for the purpose of keeping connections open. - returned: queried - type: bool - sample: yes - proxy_mode: - description: - - Specifies the proxy mode for this profile. Either reverse, explicit, or transparent. - returned: queried - type: str - sample: reverse - redirect_rewrite: - description: - - Specifies whether the system rewrites the URIs that are part of HTTP - redirect (3XX) responses - returned: queried - type: str - sample: none - request_chunking: - description: - - Specifies how the system handles HTTP content that is chunked by a client. - returned: queried - type: str - sample: preserve - response_chunking: - description: - - Specifies how the system handles HTTP content that is chunked by a server. - returned: queried - type: str - sample: selective - server_agent_name: - description: - - Specifies the string used as the server name in traffic generated by LTM. - returned: queried - type: str - sample: BigIP - sflow_poll_interval: - description: - - The maximum interval in seconds between two pollings. - returned: queried - type: int - sample: 0 - sflow_sampling_rate: - description: - - Specifies the ratio of packets observed to the samples generated. - returned: queried - type: int - sample: 0 - via_request: - description: - - Specifies whether to Remove, Preserve, or Append Via headers included in - a client request to an origin web server. - returned: queried - type: str - sample: preserve - via_response: - description: - - Specifies whether to Remove, Preserve, or Append Via headers included in - an origin web server response to a client. - returned: queried - type: str - sample: preserve - sample: hash/dictionary of values -iapp_services: - description: iApp v1 service related information. - returned: When C(iapp-services) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/service1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: service1 - device_group: - description: - - The device group the iApp service is part of. - returned: queried - type: str - sample: /Common/dg1 - inherited_device_group: - description: - - Whether the device group is inherited or not. - returned: queried - type: bool - sample: yes - inherited_traffic_group: - description: - - Whether the traffic group is inherited or not. - returned: queried - type: bool - sample: yes - strict_updates: - description: - - Whether strict updates are enabled or not. - returned: queried - type: bool - sample: yes - template_modified: - description: - - Whether template that the service is based on is modified from its - default value, or not. - returned: queried - type: bool - sample: yes - traffic_group: - description: - - Traffic group the service is a part of. - returned: queried - type: str - sample: /Common/tg - tables: - description: - - List of the tabular data used to create the service. - returned: queried - type: complex - sample: [{"name": "basic__snatpool_members"},...] - variables: - description: - - List of the variable data used to create the service. - returned: queried - type: complex - sample: [{"name": "afm__policy"},{"encrypted": "no"},{"value": "/#no_not_use#"},...] - metadata: - description: - - List of the metadata data used to create the service.. - returned: queried - type: complex - sample: [{"name": "var1"},{"persist": "true"},...] - lists: - description: - - List of the lists data used to create the service. - returned: queried - type: complex - sample: [{"name": "irules__irules"},{"value": []},...] - description: - description: - - Description of the service - returned: queried - type: str - sample: My service - sample: hash/dictionary of values -icmp_monitors: - description: ICMP monitor related information. - returned: When C(icmp-monitors) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/icmp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: icmp - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: icmp - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My monitor - adaptive: - description: - - Whether adaptive response time monitoring is enabled for this monitor. - returned: queried - type: bool - sample: no - adaptive_divergence_type: - description: - - Specifies whether the adaptive-divergence-value is C(relative) or - C(absolute). - returned: queried - type: str - sample: relative - adaptive_divergence_value: - description: - - Specifies how far from mean latency each monitor probe is allowed - to be. - returned: queried - type: int - sample: 25 - adaptive_limit: - description: - - Specifies the hard limit, in milliseconds, which the probe is not - allowed to exceed, regardless of the divergence value. - returned: queried - type: int - sample: 200 - adaptive_sampling_timespan: - description: - - Specifies the size of the sliding window, in seconds, which - records probe history. - returned: queried - type: int - sample: 300 - destination: - description: - - Specifies the IP address and service port of the resource that is - the destination of this monitor. - returned: queried - type: str - sample: "*:*" - interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when either the resource is down or the status - of the resource is unknown. - returned: queried - type: int - sample: 5 - manual_resume: - description: - - Specifies whether the system automatically changes the status of a - resource to up at the next successful monitor check. - type: bool - sample: yes - time_until_up: - description: - - Specifies the amount of time, in seconds, after the first - successful response before a node is marked up. - returned: queried - type: int - sample: 0 - timeout: - description: - - Specifies the number of seconds the target has in which to respond - to the monitor request. - returned: queried - type: int - sample: 16 - transparent: - description: - - Specifies whether the monitor operates in transparent mode. - returned: queried - type: bool - sample: no - up_interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when the resource is up. - returned: queried - type: int - sample: 0 - sample: hash/dictionary of values -interfaces: - description: Interface related information. - returned: When C(interfaces) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/interface1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: interface1 - active_media_type: - description: - - Displays the current media setting for the interface. - returned: queried - type: str - sample: 100TX-FD - flow_control: - description: - - Specifies how the system controls the sending of PAUSE frames for - flow control. - returned: queried - type: str - sample: tx-rx - description: - description: - - Description of the interface - returned: queried - type: str - sample: My interface - bundle: - description: - - The bundle capability on the port. - returned: queried - type: str - sample: not-supported - bundle_speed: - description: - - The bundle-speed on the port when bundle capability is - enabled. - returned: queried - type: str - sample: 100G - enabled: - description: - - Whether the interface is enabled or not - returned: queried - type: bool - sample: yes - if_index: - description: - - The index assigned to this interface. - returned: queried - type: int - sample: 32 - mac_address: - description: - - Displays the 6-byte ethernet address in non-case-sensitive - hexadecimal colon notation. - returned: queried - type: str - sample: "00:0b:09:88:00:9a" - media_sfp: - description: - - The settings for an SFP (pluggable) interface. - returned: queried - type: str - sample: auto - lldp_admin: - description: - - Sets the sending or receiving of LLDP packets on that interface. - Should be one of C(disable), C(txonly), C(rxonly) or C(txrx). - returned: queried - type: str - sample: txonly - mtu: - description: - - Displays the Maximum Transmission Unit (MTU) of the interface, - which is the maximum number of bytes in a frame without IP - fragmentation. - returned: queried - type: int - sample: 1500 - prefer_port: - description: - - Indicates which side of a combo port the interface uses, if both - sides of the port have the potential for external links. - returned: queried - type: str - sample: sfp - sflow_poll_interval: - description: - - Specifies the maximum interval in seconds between two - pollings. - returned: queried - type: int - sample: 0 - sflow_poll_interval_global: - description: - - Specifies whether the global interface poll-interval setting - overrides the object-level poll-interval setting. - returned: queried - type: bool - sample: yes - stp_auto_edge_port: - description: - - STP edge port detection. - returned: queried - type: bool - sample: yes - stp_enabled: - description: - - Whether STP is enabled or not. - returned: queried - type: bool - sample: no - stp_link_type: - description: - - Specifies the STP link type for the interface. - returned: queried - type: str - sample: auto - sample: hash/dictionary of values -irules: - description: iRule related information. - returned: When C(irules) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/irul1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: irule1 - ignore_verification: - description: - - Whether the verification of the iRule should be ignored or not. - returned: queried - type: bool - sample: no - checksum: - description: - - Checksum of the iRule as calculated by BIG-IP. - returned: queried - type: str - sample: d41d8cd98f00b204e9800998ecf8427e - definition: - description: - - The actual definition of the iRule. - returned: queried - type: str - sample: when HTTP_REQUEST ... - signature: - description: - - The calculated signature of the iRule. - returned: queried - type: str - sample: WsYy2M6xMqvosIKIEH/FSsvhtWMe6xKOA6i7f... - sample: hash/dictionary of values -ltm_pools: - description: List of LTM (Local Traffic Manager) pools. - returned: When C(ltm-pools) is specified in C(gather_subset). - type: complex - contains: - active_member_count: - description: - - The number of active pool members in the pool. - returned: queried - type: int - sample: 3 - all_avg_queue_entry_age: - description: - - Average queue entry age, for both the pool and its members. - returned: queried - type: int - sample: 5 - all_max_queue_entry_age_ever: - description: - - Maximum queue entry age ever, for both the pool and its members. - returned: queried - type: int - sample: 2 - all_max_queue_entry_age_recently: - description: - - Maximum queue entry age recently, for both the pool and its members. - returned: queried - type: int - sample: 5 - all_num_connections_queued_now: - description: - - Number of connections queued now, for both the pool and its members. - returned: queried - type: int - sample: 20 - all_num_connections_serviced: - description: - - Number of connections serviced, for both the pool and its members. - returned: queried - type: int - sample: 15 - all_queue_head_entry_age: - description: - - Queue head entry age, for both the pool and its members. - returned: queried - type: int - sample: 4 - available_member_count: - description: - - The number of available pool members in the pool. - returned: queried - type: int - sample: 4 - availability_status: - description: - - The availability of the pool. - returned: queried - type: str - sample: offline - allow_nat: - description: - - Whether NATs are automatically enabled or disabled for any connections using this pool. - returned: queried - type: bool - sample: yes - allow_snat: - description: - - Whether SNATs are automatically enabled or disabled for any connections using this pool. - returned: queried - type: bool - sample: yes - client_ip_tos: - description: - - Whether the system sets a Type of Service (ToS) level within a packet sent to the client, - based on the targeted pool. - - Values can range from C(0) to C(255), or be set to C(pass-through) or C(mimic). - returned: queried - type: str - sample: pass-through - client_link_qos: - description: - - Whether the system sets a Quality of Service (QoS) level within a packet sent to the client, - based on the targeted pool. - - Values can range from C(0) to C(7), or be set to C(pass-through). - returned: queried - type: str - sample: pass-through - current_sessions: - description: - - Current sessions. - returned: queried - type: int - sample: 2 - description: - description: - - Description of the pool. - returned: queried - type: str - sample: my pool - enabled_status: - description: - - The enabled-ness of the pool. - returned: queried - type: str - sample: enabled - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/pool1 - ignore_persisted_weight: - description: - - Do not count the weight of persisted connections on pool members when making load balancing decisions. - returned: queried - type: bool - sample: no - lb_method: - description: - - Load balancing method used by the pool. - returned: queried - type: str - sample: round-robin - member_count: - description: - - Total number of members in the pool. - returned: queried - type: int - sample: 50 - metadata: - description: - - Dictionary of arbitrary key/value pairs set on the pool. - returned: queried - type: complex - sample: hash/dictionary of values - minimum_active_members: - description: - - Whether the system load balances traffic according to the priority number assigned to the pool member. - - This parameter is identical to C(priority_group_activation) and is just an alias for it. - returned: queried - type: int - sample: 2 - minimum_up_members: - description: - - The minimum number of pool members that must be up. - returned: queried - type: int - sample: 1 - minimum_up_members_action: - description: - - The action to take if the C(minimum_up_members_checking) is enabled and the number of active pool - members falls below the number specified in C(minimum_up_members). - returned: queried - type: str - sample: failover - minimum_up_members_checking: - description: - - Enables or disables the C(minimum_up_members) feature. - returned: queried - type: bool - sample: no - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: pool1 - pool_avg_queue_entry_age: - description: - - Average queue entry age, for the pool only. - returned: queried - type: int - sample: 5 - pool_max_queue_entry_age_ever: - description: - - Maximum queue entry age ever, for the pool only. - returned: queried - type: int - sample: 2 - pool_max_queue_entry_age_recently: - description: - - Maximum queue entry age recently, for the pool only. - returned: queried - type: int - sample: 5 - pool_num_connections_queued_now: - description: - - Number of connections queued now, for the pool only. - returned: queried - type: int - sample: 20 - pool_num_connections_serviced: - description: - - Number of connections serviced, for the pool only. - returned: queried - type: int - sample: 15 - pool_queue_head_entry_age: - description: - - Queue head entry age, for the pool only. - returned: queried - type: int - sample: 4 - priority_group_activation: - description: - - Whether the system load balances traffic according to the priority number assigned to the pool member. - - This parameter is identical to C(minimum_active_members) and is just an alias for it. - returned: queried - type: int - sample: 2 - queue_depth_limit: - description: - - The maximum number of connections that may simultaneously be queued to go to any member of this pool. - returned: queried - type: int - sample: 3 - queue_on_connection_limit: - description: - - Enable or disable queuing connections when pool member or node connection limits are reached. - returned: queried - type: bool - sample: yes - queue_time_limit: - description: - - Specifies the maximum time, in milliseconds, a connection will remain enqueued. - returned: queried - type: int - sample: 0 - real_session: - description: - - The actual REST API value for the C(session) attribute. - - This is different from the C(state) return value, insofar as the return value - can be considered a generalization of all available sessions, instead of the - specific value of the session. - returned: queried - type: str - sample: monitor-enabled - real_state: - description: - - The actual REST API value for the C(state) attribute. - - This is different from the C(state) return value, insofar as the return value - can be considered a generalization of all available states, instead of the - specific value of the state. - returned: queried - type: str - sample: up - reselect_tries: - description: - - The number of times the system tries to contact a pool member after a passive failure. - returned: queried - type: int - sample: 0 - server_ip_tos: - description: - - The Type of Service (ToS) level to use when sending packets to a server. - returned: queried - type: str - sample: pass-through - server_link_qos: - description: - - The Quality of Service (QoS) level to use when sending packets to a server. - returned: queried - type: str - sample: pass-through - service_down_action: - description: - - The action to take if the service specified in the pool is marked down. - returned: queried - type: str - sample: none - server_side_bits_in: - description: - - Number of server-side ingress bits. - returned: queried - type: int - sample: 1000 - server_side_bits_out: - description: - - Number of server-side egress bits. - returned: queried - type: int - sample: 200 - server_side_current_connections: - description: - - Number of current connections server-side. - returned: queried - type: int - sample: 300 - server_side_max_connections: - description: - - Maximum number of connections server-side. - returned: queried - type: int - sample: 40 - server_side_pkts_in: - description: - - Number of server-side ingress packets. - returned: queried - type: int - sample: 1098384 - server_side_pkts_out: - description: - - Number of server-side egress packets. - returned: queried - type: int - sample: 3484734 - server_side_total_connections: - description: - - Total number of connections. - returned: queried - type: int - sample: 24 - slow_ramp_time: - description: - - The ramp time for the pool. - - This provides the ability to cause a pool member that has just been enabled, - or marked up, to receive proportionally less traffic than other members in the pool. - returned: queried - type: int - sample: 10 - status_reason: - description: - - If there is a problem with the status of the pool, that problem is reported here. - returned: queried - type: str - sample: The children pool member(s) are down. - members: - description: List of LTM (Local Traffic Manager) pools. - returned: when members exist in the pool. - type: complex - contains: - address: - description: IP address of the pool member. - returned: queried - type: str - sample: 1.1.1.1 - connection_limit: - description: The maximum number of concurrent connections allowed for a pool member. - returned: queried - type: int - sample: 0 - description: - description: The description of the pool member. - returned: queried - type: str - sample: pool member 1 - dynamic_ratio: - description: - - A range of numbers that you want the system to use in conjunction with the ratio load balancing method. - returned: queried - type: int - sample: 1 - ephemeral: - description: - - Whether the node backing the pool member is ephemeral or not. - returned: queried - type: bool - sample: yes - fqdn_autopopulate: - description: - - Whether the node should scale to the IP address set returned by DNS. - returned: queried - type: bool - sample: yes - full_path: - description: - - Full name of the resource as known to BIG-IP. - - Includes the port in the name - returned: queried - type: str - sample: "/Common/member:80" - inherit_profile: - description: - - Whether the pool member inherits the encapsulation profile from the parent pool. - returned: queried - type: bool - sample: no - logging: - description: - - Whether the monitor applied should log its actions. - returned: queried - type: bool - sample: no - monitors: - description: - - Monitors active on the pool member. Monitor names are in their "full_path" form. - returned: queried - type: list - sample: ['/Common/http'] - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: "member:80" - partition: - description: - - Partition that the member exists on. - returned: queried - type: str - sample: Common - priority_group: - description: - - The priority group within the pool for this pool member. - returned: queried - type: int - sample: 0 - encapsulation_profile: - description: - - The encapsulation profile to use for the pool member. - returned: queried - type: str - sample: ip4ip4 - rate_limit: - description: - - The maximum number of connections per second allowed for a pool member. - returned: queried - type: bool - sample: no - ratio: - description: - - The weight of the pool for load balancing purposes. - returned: queried - type: int - sample: 1 - session: - description: - - Enables or disables the pool member for new sessions. - returned: queried - type: str - sample: monitor-enabled - state: - description: - - Controls the state of the pool member, overriding any monitors. - returned: queried - type: str - sample: down - total_requests: - description: - - Total requests. - returned: queried - type: int - sample: 8 - sample: hash/dictionary of values -ltm_policies: - description: List of LTM (Local Traffic Manager) policies. - returned: When C(ltm-policies) is specified in C(gather_subset). - type: complex - contains: - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: policy1 - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/policy1 - description: - description: - - Description of the policy. - returned: queried - type: str - sample: My policy - strategy: - description: - - The match strategy for the policy. - returned: queried - type: str - sample: /Common/first-match - requires: - description: - - Aspects of the system required by this policy. - returned: queried - type: list - sample: ['http'] - controls: - description: - - Aspects of the system controlled by this policy. - returned: queried - type: list - sample: ['forwarding'] - status: - description: - - Indicates published or draft policy status. - returned: queried - type: str - sample: draft - rules: - description: - - List of LTM (Local Traffic Manager) policy rules. - returned: when rules are defined in the policy. - type: complex - contains: - actions: - description: - - The actions the policy will take when a match is encountered. - returned: when actions are defined in the rule. - type: complex - contains: - http_reply: - description: - - Indicate if the action will affects a reply to a given HTTP request. - returned: when defined in the action. - type: bool - sample: yes - redirect: - description: - - This action will redirect a request. - returned: when defined in the action. - type: bool - sample: no - request: - description: - - This policy action is performed on connection requests. - returned: when defined in the action. - type: bool - sample: no - location: - description: - - This action will come from the given location. - returned: when defined in the action. - type: str - sample: "tcl:https://[getfield [HTTP::host] \\\":\\\" 1][HTTP::uri]" - sample: hash/dictionary of values - conditions: - description: - - The conditions that a policy will match on. - returned: when conditions are defined in the rule. - type: complex - contains: - case_insensitive: - description: - - The value matched on is case insensitive. - returned: when defined in the condition. - type: bool - sample: no - case_sensitive: - description: - - The value matched on is case sensitive. - returned: when defined in the condition. - type: bool - sample: yes - contains_string: - description: - - The value matches if it contains a certain string. - returned: when defined in the condition. - type: bool - sample: yes - external: - description: - - The value matched on is from the external side of a connection. - returned: when defined in the condition. - type: bool - sample: yes - http_basic_auth: - description: - - This condition matches on basic HTTP authorization. - returned: when defined in the condition. - type: bool - sample: no - http_host: - description: - - This condition matches on an HTTP host. - returned: when defined in the condition. - type: bool - sample: yes - http_uri: - description: - - This condition matches on an HTTP URI. - returned: when defined in the condition. - type: bool - sample: no - request: - description: - - This policy will match on a request. - returned: when defined in the condition. - type: bool - sample: yes - username: - description: - - Matches on a username. - returned: when defined in the condition. - type: bool - sample: yes - all: - description: - - Matches all. - returned: when defined in the condition. - type: bool - sample: yes - values: - description: - - The specified values will be matched on. - returned: when defined in the condition. - type: list - sample: ['foo.bar.com', 'baz.cool.com'] - sample: hash/dictionary of values - sample: hash/dictionary of values - sample: hash/dictionary of values -nodes: - description: Node related information. - returned: When C(nodes) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/5.6.7.8 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: 5.6.7.8 - ratio: - description: - - Fixed size ratio used for node during C(Ratio) load balancing. - returned: queried - type: int - sample: 10 - description: - description: - - Description of the node. - returned: queried - type: str - sample: My node - connection_limit: - description: - - Maximum number of connections that node can handle. - returned: queried - type: int - sample: 100 - address: - description: - - IP address of the node. - returned: queried - type: str - sample: 2.3.4.5 - dynamic_ratio: - description: - - Dynamic ratio number for the node used when doing C(Dynamic Ratio) load balancing. - returned: queried - type: int - sample: 200 - rate_limit: - description: - - Maximum number of connections per second allowed for node. - returned: queried - type: int - sample: 1000 - monitor_status: - description: - - Status of the node as reported by the monitor(s) associated with it. - - This value is also used in determining node C(state). - returned: queried - type: str - sample: down - session_status: - description: - - This value is also used in determining node C(state). - returned: queried - type: str - sample: enabled - availability_status: - description: - - The availability of the node. - returned: queried - type: str - sample: offline - enabled_status: - description: - - The enabled-ness of the node. - returned: queried - type: str - sample: enabled - status_reason: - description: - - If there is a problem with the status of the node, that problem is reported here. - returned: queried - type: str - sample: /Common/https_443 No successful responses received... - monitor_rule: - description: - - A string representation of the full monitor rule. - returned: queried - type: str - sample: /Common/https_443 and /Common/icmp - monitors: - description: - - A list of the monitors identified in the C(monitor_rule). - returned: queried - type: list - sample: ['/Common/https_443', '/Common/icmp'] - monitor_type: - description: - - The C(monitor_type) field related to the C(bigip_node) module, for this nodes - monitors. - returned: queried - type: str - sample: and_list - sample: hash/dictionary of values -oneconnect_profiles: - description: OneConnect profile related information. - returned: When C(oneconnect-profiles) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/oneconnect - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: oneconnect - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: oneconnect - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My profile - idle_timeout_override: - description: - - Specifies the number of seconds that a connection is idle before - the connection flow is eligible for deletion. - returned: queried - type: int - sample: 1000 - limit_type: - description: - - When C(none), simultaneous in-flight requests and responses over TCP - connections to a pool member are counted toward the limit. - - When C(idle), idle connections will be dropped as the TCP connection - limit is reached. - - When C(strict), the TCP connection limit is honored with no - exceptions. This means that idle connections will prevent new TCP - connections from being made until they expire, even if they could - otherwise be reused. - returned: queried - type: str - sample: idle - max_age: - description: - - Specifies the maximum age, in number of seconds, of a connection - in the connection reuse pool. - returned: queried - type: int - sample: 100 - max_reuse: - description: - - Specifies the maximum number of times that a server connection can - be reused. - returned: queried - type: int - sample: 1000 - max_size: - description: - - Specifies the maximum number of connections that the system holds - in the connection reuse pool. - - If the pool is already full, then the server connection closes after - the response is completed. - returned: queried - type: int - sample: 1000 - share_pools: - description: - - Indicates that connections may be shared not only within a virtual - server, but also among similar virtual servers. - returned: queried - type: bool - sample: yes - source_mask: - description: - - Specifies a source IP mask. - - If no mask is provided, the value C(any6) is used. - returned: queried - type: str - sample: 255.255.255.0 - sample: hash/dictionary of values -partitions: - description: Partition related information. - returned: When C(partitions) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: Common - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: Common - description: - description: - - Description of the partition. - returned: queried - type: str - sample: Tenant 1 - default_route_domain: - description: - - ID of the route domain that is associated with the IP addresses that reside - in the partition. - returned: queried - type: int - sample: 0 - sample: hash/dictionary of values -provision_info: - description: Module provisioning related information. - returned: When C(provision-info) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: asm - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: asm - cpu_ratio: - description: - - Ratio of CPU allocated to this module. - - Only relevant if C(level) was specified as C(custom). Otherwise, this value - will be reported as C(0). - returned: queried - type: int - sample: 0 - disk_ratio: - description: - - Ratio of disk allocated to this module. - - Only relevant if C(level) was specified as C(custom). Otherwise, this value - will be reported as C(0). - returned: queried - type: int - sample: 0 - memory_ratio: - description: - - Ratio of memory allocated to this module. - - Only relevant if C(level) was specified as C(custom). Otherwise, this value - will be reported as C(0). - returned: queried - type: int - sample: 0 - level: - description: - - Provisioned level of the module on BIG-IP. - - Valid return values can include C(none), C(minimum), C(nominal), C(dedicated) - and C(custom). - returned: queried - type: int - sample: 0 - sample: hash/dictionary of values -self_ips: - description: Self-IP related information. - returned: When C(self-ips) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/self1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: self1 - description: - description: - - Description of the Self-IP. - returned: queried - type: str - sample: My self-ip - netmask: - description: - - Netmask portion of the IP address. In dotted notation. - returned: queried - type: str - sample: 255.255.255.0 - netmask_cidr: - description: - - Netmask portion of the IP address. In CIDR notation. - returned: queried - type: int - sample: 24 - floating: - description: - - Whether the Self-IP is a floating address or not. - returned: queried - type: bool - sample: yes - traffic_group: - description: - - Traffic group the Self-IP is associated with. - returned: queried - type: str - sample: /Common/traffic-group-local-only - service_policy: - description: - - Service policy assigned to the Self-IP. - returned: queried - type: str - sample: /Common/service1 - vlan: - description: - - VLAN associated with the Self-IP. - returned: queried - type: str - sample: /Common/vlan1 - allow_access_list: - description: - - List of protocols and optionally their ports that are allowed to access the - Self-IP. Also known as port-lockdown in the web interface. - - Items in the list are in the format of "protocol:port". Some items may not - have a port associated with them and in those cases the port is C(0). - returned: queried - type: list - sample: ['tcp:80', 'egp:0'] - traffic_group_inherited: - description: - - Whether or not the traffic group is inherited. - returned: queried - type: bool - sample: no - sample: hash/dictionary of values -server_ssl_profiles: - description: Server SSL related information. - returned: When C(server-ssl-profiles) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: serverssl - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: serverssl - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My profile - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: serverssl - alert_timeout: - description: - - Maximum time period in seconds to keep the SSL - session active after alert message is sent, or indefinite. - returned: queried - type: str - sample: 100 - allow_expired_crl: - description: - - Use the specified CRL file even if it has expired. - returned: queried - type: bool - sample: yes - authentication_frequency: - description: - - Specifies the frequency of authentication. - returned: queried - type: str - sample: once - authenticate_depth: - description: - - The client certificate chain maximum traversal depth - returned: queried - type: int - sample: 9 - authenticate_name: - description: - - Common Name (CN) that is embedded in a server certificate. - - The system authenticates a server based on the specified CN. - returned: queried - type: str - sample: foo - bypass_on_client_cert_fail: - description: - - Enables or disables SSL forward proxy bypass on failing to get - client certificate that server asks for. - returned: queried - type: bool - sample: yes - bypass_on_handshake_alert: - description: - - Enables or disables SSL forward proxy bypass on receiving - handshake_failure, protocol_version or unsupported_extension alert - message during the serverside SSL handshake. - returned: queried - type: bool - sample: no - c3d_ca_cert: - description: - - Name of the certificate file that is used as the - certification authority certificate when SSL client certificate - constrained delegation is enabled. - returned: queried - type: str - sample: /Common/cacert.crt - c3d_ca_key: - description: - - Name of the key file that is used as the - certification authority key when SSL client certificate - constrained delegation is enabled. - returned: queried - type: str - sample: /Common/default.key - c3d_cert_extension_includes: - description: - - Extensions of the client certificates to be included - in the generated certificates using SSL client certificate - constrained delegation. - returned: queried - type: list - sample: [ "basic-constraints", "extended-key-usage", ... ] - c3d_cert_lifespan: - description: - - Lifespan of the certificate generated using the SSL - client certificate constrained delegation. - returned: queried - type: int - sample: 24 - ca_file: - description: - - Certificate authority file name. - returned: queried - type: str - sample: default.crt - cache_size: - description: - - The SSL session cache size. - returned: queried - type: int - sample: 262144 - cache_timeout: - description: - - The SSL session cache timeout value, which is the usable - lifetime seconds of negotiated SSL session IDs. - returned: queried - type: int - sample: 86400 - cert: - description: - - The name of the certificate installed on the traffic - management system for the purpose of terminating or initiating an - SSL connection. - returned: queried - type: str - sample: /Common/default.crt - chain: - description: - - Specifies or builds a certificate chain file that a client can use - to authenticate the profile. - returned: queried - type: str - sample: /Common/default.crt - cipher_group: - description: - - Specifies a cipher group. - returned: queried - type: str - ciphers: - description: - - Specifies a cipher name. - returned: queried - type: str - sample: DEFAULT - crl_file: - description: - - Specifies the certificate revocation list file name. - returned: queried - type: str - expire_cert_response_control: - description: - - Specifies the BIGIP action when the server certificate has - expired. - returned: queried - type: str - sample: drop - handshake_timeout: - description: - - Specifies the handshake timeout in seconds. - returned: queried - type: str - sample: 10 - key: - description: - - Specifies the key file name. Specifies the name of the key - installed on the traffic management system for the purpose of - terminating or initiating an SSL connection. - returned: queried - type: str - sample: /Common/default.key - max_active_handshakes: - description: - - Specifies the maximum number allowed SSL active handshakes. - returned: queried - type: str - sample: 100 - mod_ssl_methods: - description: - - Enables or disables ModSSL methods. - returned: queried - type: bool - sample: yes - mode: - description: - - Enables or disables SSL processing. - returned: queried - type: bool - sample: no - ocsp: - description: - - Specifies the name of ocsp profile for purpose of validating - status of server certificate. - returned: queried - type: str - options: - description: - - Enables options, including some industry-related workarounds. - returned: queried - type: list - sample: [ "netscape-reuse-cipher-change-bug", "dont-insert-empty-fragments" ] - peer_cert_mode: - description: - - Specifies the peer certificate mode. - returned: queried - type: str - sample: ignore - proxy_ssl: - description: - - Allows further modification of application traffic within - an SSL tunnel while still allowing the server to perform necessary - authorization, authentication, auditing steps. - returned: queried - type: bool - sample: yes - proxy_ssl_passthrough: - description: - - Allows Proxy SSL to passthrough the traffic when ciphersuite negotiated - between the client and server is not supported. - returned: queried - type: bool - sample: yes - renegotiate_period: - description: - - Number of seconds from the initial connect time - after which the system renegotiates an SSL session. - returned: queried - type: str - sample: indefinite - renegotiate_size: - description: - - Specifies a throughput size, in megabytes, of SSL renegotiation. - returned: queried - type: str - sample: indefinite - renegotiation: - description: - - Whether renegotiations are enabled. - returned: queried - type: bool - sample: yes - retain_certificate: - description: - - APM module requires storing certificate in SSL session. When C(no), - certificate will not be stored in SSL session. - returned: queried - type: bool - sample: no - generic_alert: - description: - - Enables or disables generic-alert. - returned: queried - type: bool - sample: yes - secure_renegotiation: - description: - - Specifies the secure renegotiation mode. - returned: queried - type: str - sample: require - server_name: - description: - - Server name to be included in SNI (server name - indication) extension during SSL handshake in ClientHello. - returned: queried - type: str - session_mirroring: - description: - - Enables or disables the mirroring of sessions to high availability - peer. - returned: queried - type: bool - sample: yes - session_ticket: - description: - - Enables or disables session-ticket. - returned: queried - type: bool - sample: no - sni_default: - description: - - When C(yes), this profile is the default SSL profile when the server - name in a client connection does not match any configured server - names, or a client connection does not specify any server name at - all. - returned: queried - type: bool - sample: yes - sni_require: - description: - - When C(yes), connections to a server that does not support SNI - extension will be rejected. - returned: queried - type: bool - sample: no - ssl_c3d: - description: - - Enables or disables SSL Client certificate constrained delegation. - returned: queried - type: bool - sample: yes - ssl_forward_proxy_enabled: - description: - - Enables or disables ssl-forward-proxy feature. - returned: queried - type: bool - sample: no - ssl_sign_hash: - description: - - Specifies SSL sign hash algorithm which is used to sign and verify - SSL Server Key Exchange and Certificate Verify messages for the - specified SSL profiles. - returned: queried - type: str - sample: sha1 - ssl_forward_proxy_bypass: - description: - - Enables or disables ssl-forward-proxy-bypass feature. - returned: queried - type: bool - sample: yes - strict_resume: - description: - - Enables or disables the resumption of SSL sessions after an - unclean shutdown. - returned: queried - type: bool - sample: no - unclean_shutdown: - description: - - Specifies, when C(yes), that the SSL profile performs unclean - shutdowns of all SSL connections, which means that underlying TCP - connections are closed without exchanging the required SSL - shutdown alerts. - returned: queried - type: bool - sample: yes - untrusted_cert_response_control: - description: - - Specifies the BIGIP action when the server certificate has - untrusted CA. - returned: queried - type: str - sample: drop - sample: hash/dictionary of values -software_hotfixes: - description: List of software hotfixes. - returned: When C(software-hotfixes) is specified in C(gather_subset). - type: complex - contains: - name: - description: - - Name of the image. - returned: queried - type: str - sample: Hotfix-BIGIP-13.0.0.3.0.1679-HF3.iso - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: Hotfix-BIGIP-13.0.0.3.0.1679-HF3.iso - build: - description: - - Build number of the image. - - This is usually a sub-string of the C(name). - returned: queried - type: str - sample: 3.0.1679 - checksum: - description: - - MD5 checksum of the image. - - Note that this is the checksum that is stored inside the ISO. It is not - the actual checksum of the ISO. - returned: queried - type: str - sample: df1ec715d2089d0fa54c0c4284656a98 - product: - description: - - Product contained in the ISO. - returned: queried - type: str - sample: BIG-IP - id: - description: - - ID component of the image. - - This is usually a sub-string of the C(name). - returned: queried - type: str - sample: HF3 - title: - description: - - Human friendly name of the image. - returned: queried - type: str - sample: Hotfix Version 3.0.1679 - verified: - description: - - Whether or not the system has verified this image. - returned: queried - type: bool - sample: yes - version: - description: - - Version of software contained in the image. - - This is a sub-string of the C(name). - returned: queried - type: str - sample: 13.0.0 - sample: hash/dictionary of values -software_images: - description: List of software images. - returned: When C(software-images) is specified in C(gather_subset). - type: complex - contains: - name: - description: - - Name of the image. - returned: queried - type: str - sample: BIGIP-13.1.0.7-0.0.1.iso - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: BIGIP-13.1.0.7-0.0.1.iso - build: - description: - - Build number of the image. - - This is usually a sub-string of the C(name). - returned: queried - type: str - sample: 0.0.1 - build_date: - description: - - Date of the build. - returned: queried - type: str - sample: "2018-05-05T15:26:30" - checksum: - description: - - MD5 checksum of the image. - - Note that this is the checksum that is stored inside the ISO. It is not - the actual checksum of the ISO. - returned: queried - type: str - sample: df1ec715d2089d0fa54c0c4284656a98 - file_size: - description: - - Size, in megabytes, of the image. - returned: queried - type: int - sample: 1938 - last_modified: - description: - - Last modified date of the ISO. - returned: queried - type: str - sample: "2018-05-05T15:26:30" - product: - description: - - Product contained in the ISO. - returned: queried - type: str - sample: BIG-IP - verified: - description: - - Whether or not the system has verified this image. - returned: queried - type: bool - sample: yes - version: - description: - - Version of software contained in the image. - - This is a sub-string of the C(name). - returned: queried - type: str - sample: 13.1.0.7 - sample: hash/dictionary of values -software_volumes: - description: List of software volumes. - returned: When C(software-volumes) is specified in C(gather_subset). - type: complex - contains: - active: - description: - - Whether the volume is currently active or not. - - An active volume contains the currently running version of software. - returned: queried - type: bool - sample: yes - base_build: - description: - - Base build version of the software installed in the volume. - - When a hotfix is installed, this refers to the base version of software - that the hotfix requires. - returned: queried - type: str - sample: 0.0.6 - build: - description: - - Build version of the software installed in the volume. - returned: queried - type: str - sample: 0.0.6 - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: HD1.1 - default_boot_location: - description: - - Whether this volume is the default boot location or not. - returned: queried - type: bool - sample: yes - name: - description: - - Relative name of the resource in BIG-IP. - - This usually matches the C(full_name). - returned: queried - type: str - sample: HD1.1 - product: - description: - - The F5 product installed in this slot. - - This should always be BIG-IP. - returned: queried - type: str - sample: BIG-IP - status: - description: - - Status of the software installed, or being installed, in the volume. - - When C(complete), indicates that the software has completed installing. - returned: queried - type: str - sample: complete - version: - description: - - Version of software installed in the volume, excluding the C(build) number. - returned: queried - type: str - sample: 13.1.0.4 - sample: hash/dictionary of values -ssl_certs: - description: SSL certificate related information. - returned: When C(ssl-certs) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/cert1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: cert1 - key_type: - description: - - Specifies the type of cryptographic key associated with this certificate. - returned: queried - type: str - sample: rsa-private - key_size: - description: - - Specifies the size (in bytes) of the file associated with this file object. - returned: queried - type: int - sample: 2048 - system_path: - description: - - Path on the BIG-IP where the cert can be found. - returned: queried - type: str - sample: /config/ssl/ssl.crt/f5-irule.crt - sha1_checksum: - description: - - SHA1 checksum of the certificate. - returned: queried - type: str - sample: 1306e84e1e6a2da53816cefe1f684b80d6be1e3e - subject: - description: - - Specifies X509 information of the certificate's subject. - returned: queried - type: str - sample: "emailAddress=support@f5.com,CN=..." - last_update_time: - description: - - Specifies the last time at which the file-object was - updated/modified. - returned: queried - type: str - sample: "2018-05-15T21:11:15Z" - issuer: - description: - - Specifies X509 information of the certificate's issuer. - returned: queried - type: str - sample: "emailAddress=support@f5.com,...CN=support.f5.com," - is_bundle: - description: - - Specifies whether the certificate file is a bundle (that is, - whether it contains more than one certificate). - returned: queried - type: bool - sample: no - fingerprint: - description: - - Displays the SHA-256 fingerprint of the certificate. - returned: queried - type: str - sample: "SHA256/88:A3:05:...:59:01:EA:5D:B0" - expiration_date: - description: - - Specifies a string representation of the expiration date of the - certificate. - returned: queried - type: str - sample: "Aug 13 21:21:29 2031 GMT" - expiration_timestamp: - description: - - Specifies the date at which this certificate expires. Stored as a - POSIX time. - returned: queried - type: int - sample: 1944422489 - create_time: - description: - - Specifies the time at which the file-object was created. - returned: queried - type: str - sample: "2018-05-15T21:11:15Z" - sample: hash/dictionary of values -ssl_keys: - description: SSL certificate related information. - returned: When C(ssl-certs) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/key1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: key1 - key_type: - description: - - Specifies the cryptographic type of the key in question. That is, - which algorithm this key is compatible with. - returned: queried - type: str - sample: rsa-private - key_size: - description: - - Specifies the size of the cryptographic key associated with this - file object, in bits. - returned: queried - type: int - sample: 2048 - security_type: - description: - - Specifies the type of security used to handle or store the key. - returned: queried - type: str - sample: normal - system_path: - description: - - The path on the filesystem where the key is stored. - returned: queried - type: str - sample: /config/ssl/ssl.key/default.key - sha1_checksum: - description: - - The SHA1 checksum of the key. - returned: queried - type: str - sample: 1fcf7de3dd8e834d613099d8e10b2060cd9ecc9f - sample: hash/dictionary of values -system_db: - description: System DB related information. - returned: When C(system-db) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: vendor.wwwurl - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: vendor.wwwurl - default: - description: - - Default value of the key. - returned: queried - type: str - sample: www.f5.com - scf_config: - description: - - Whether the database key would be found in an SCF config or not. - returned: queried - type: str - sample: false - value: - description: - - The value of the key - returned: queried - type: str - sample: www.f5.com - value_range: - description: - - The accepted range of values for the key - returned: queried - type: str - sample: string - sample: hash/dictionary of values -system_info: - description: Traffic group related information. - returned: When C(traffic-groups) is specified in C(gather_subset). - type: complex - contains: - base_mac_address: - description: - - Media Access Control address (MAC address) of the device. - returned: queried - type: str - sample: "fa:16:3e:c3:42:6f" - marketing_name: - description: - - Marketing name of the device platform. - returned: queried - type: str - sample: BIG-IP Virtual Edition - time: - description: - - Mapping of the current time information to specific time-named keys. - returned: queried - type: complex - contains: - day: - description: - - The current day of the month, in numeric form. - returned: queried - type: int - sample: 7 - hour: - description: - - The current hour of the day in 24-hour form. - returned: queried - type: int - sample: 18 - minute: - description: - - The current minute of the hour. - returned: queried - type: int - sample: 16 - month: - description: - - The current month, in numeric form. - returned: queried - type: int - sample: 6 - second: - description: - - The current second of the minute. - returned: queried - type: int - sample: 51 - year: - description: - - The current year in 4-digit form. - returned: queried - type: int - sample: 2018 - hardware_information: - description: - - Information related to the hardware (drives and CPUs) of the system. - type: complex - returned: queried - contains: - model: - description: - - The model of the hardware. - returned: queried - type: str - sample: Virtual Disk - name: - description: - - The name of the hardware. - returned: queried - type: str - sample: HD1 - type: - description: - - The type of hardware. - returned: queried - type: str - sample: physical-disk - versions: - description: - - Hardware specific properties. - returned: queried - type: complex - contains: - name: - description: - - Name of the property. - returned: queried - type: str - sample: Size - version: - description: - - Value of the property. - returned: queried - type: str - sample: 154.00G - package_edition: - description: - - Displays the software edition. - returned: queried - type: str - sample: Point Release 7 - package_version: - description: - - A string combining the C(product_build) and C(product_build_date). - returned: queried - type: str - sample: "Build 0.0.1 - Tue May 15 15:26:30 PDT 2018" - product_code: - description: - - Code identifying the product. - returned: queried - type: str - sample: BIG-IP - product_build: - description: - - Build version of the release version. - returned: queried - type: str - sample: 0.0.1 - product_version: - description: - - Major product version of the running software. - returned: queried - type: str - sample: 13.1.0.7 - product_built: - description: - - Unix timestamp of when the product was built. - returned: queried - type: int - sample: 180515152630 - product_build_date: - description: - - Human readable build date. - returned: queried - type: str - sample: "Tue May 15 15:26:30 PDT 2018" - product_changelist: - description: - - Changelist that product branches from. - returned: queried - type: int - sample: 2557198 - product_jobid: - description: - - ID of the job that built the product version. - returned: queried - type: int - sample: 1012030 - chassis_serial: - description: - - Serial of the chassis. - returned: queried - type: str - sample: 11111111-2222-3333-444444444444 - host_board_part_revision: - description: - - Revision of the host board. - returned: queried - type: str - host_board_serial: - description: - - Serial of the host board. - returned: queried - type: str - platform: - description: - - Platform identifier. - returned: queried - type: str - sample: Z100 - switch_board_part_revision: - description: - - Switch board revision. - returned: queried - type: str - switch_board_serial: - description: - - Serial of the switch board. - returned: queried - type: str - uptime: - description: - - Time, in seconds, since the system booted. - returned: queried - type: int - sample: 603202 - sample: hash/dictionary of values -tcp_monitors: - description: TCP monitor related information. - returned: When C(tcp-monitors) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/tcp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: tcp - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: tcp - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My monitor - adaptive: - description: - - Whether adaptive response time monitoring is enabled for this monitor. - returned: queried - type: bool - sample: no - adaptive_divergence_type: - description: - - Specifies whether the adaptive-divergence-value is C(relative) or - C(absolute). - returned: queried - type: str - sample: relative - adaptive_divergence_value: - description: - - Specifies how far from mean latency each monitor probe is allowed - to be. - returned: queried - type: int - sample: 25 - adaptive_limit: - description: - - Specifies the hard limit, in milliseconds, which the probe is not - allowed to exceed, regardless of the divergence value. - returned: queried - type: int - sample: 200 - adaptive_sampling_timespan: - description: - - Specifies the size of the sliding window, in seconds, which - records probe history. - returned: queried - type: int - sample: 300 - destination: - description: - - Specifies the IP address and service port of the resource that is - the destination of this monitor. - returned: queried - type: str - sample: "*:*" - interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when either the resource is down or the status - of the resource is unknown. - returned: queried - type: int - sample: 5 - ip_dscp: - description: - - Specifies the differentiated services code point (DSCP). - returned: queried - type: int - sample: 0 - manual_resume: - description: - - Specifies whether the system automatically changes the status of a - resource to up at the next successful monitor check. - returned: queried - type: bool - sample: yes - reverse: - description: - - Specifies whether the monitor operates in reverse mode. When the - monitor is in reverse mode, a successful check marks the monitored - object down instead of up. - returned: queried - type: bool - sample: no - time_until_up: - description: - - Specifies the amount of time, in seconds, after the first - successful response before a node is marked up. - returned: queried - type: int - sample: 0 - timeout: - description: - - Specifies the number of seconds the target has in which to respond - to the monitor request. - returned: queried - type: int - sample: 16 - transparent: - description: - - Specifies whether the monitor operates in transparent mode. - returned: queried - type: bool - sample: no - up_interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when the resource is up. - returned: queried - type: int - sample: 0 - sample: hash/dictionary of values -tcp_half_open_monitors: - description: TCP Half-open monitor related information. - returned: When C(tcp-half-open-monitors) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/tcp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: tcp - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: tcp - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My monitor - destination: - description: - - Specifies the IP address and service port of the resource that is - the destination of this monitor. - returned: queried - type: str - sample: "*:*" - interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when either the resource is down or the status - of the resource is unknown. - returned: queried - type: int - sample: 5 - manual_resume: - description: - - Specifies whether the system automatically changes the status of a - resource to up at the next successful monitor check. - returned: queried - type: bool - sample: yes - time_until_up: - description: - - Specifies the amount of time, in seconds, after the first - successful response before a node is marked up. - returned: queried - type: int - sample: 0 - timeout: - description: - - Specifies the number of seconds the target has in which to respond - to the monitor request. - returned: queried - type: int - sample: 16 - transparent: - description: - - Specifies whether the monitor operates in transparent mode. - returned: queried - type: bool - sample: no - up_interval: - description: - - Specifies, in seconds, the frequency at which the system issues - the monitor check when the resource is up. - returned: queried - type: int - sample: 0 - sample: hash/dictionary of values -tcp_profiles: - description: TCP profile related information. - returned: When C(tcp-profiles) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: tcp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: /Common/tcp - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: tcp - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My profile - abc: - description: - - Appropriate Byte Counting (RFC 3465) - - When C(yes), increases the congestion window by basing the increase - amount on the number of previously unacknowledged bytes that each ACK covers. - returned: queried - type: bool - sample: yes - ack_on_push: - description: - - Specifies, when C(yes), significantly improved performance to Microsoft - Windows and MacOS peers who are writing out on a very small send buffer. - returned: queried - type: bool - sample: no - auto_proxy_buffer: - description: - - Specifies, C(yes), that the system uses the network measurements to set - the optimal proxy buffer size. - returned: queried - type: bool - sample: yes - auto_receive_window: - description: - - Specifies, when C(yes), that the system uses the network measurements to - set the optimal receive window size. - returned: queried - type: bool - sample: no - auto_send_buffer: - description: - - Specifies, when C(yes), that the system uses the network measurements to - set the optimal send buffer size. - returned: queried - type: bool - sample: yes - close_wait: - description: - - Specifies the length of time that a TCP connection remains in the LAST-ACK - state before quitting. - - In addition to a numeric value, the value of this fact may also be one of - C(immediate) or C(indefinite). - - When C(immediate), specifies that the TCP connection closes immediately - after entering the LAST-ACK state. - - When C(indefinite), specifies that TCP connections in the LAST-ACK state - do not close until they meet the maximum retransmissions timeout. - returned: queried - type: str - sample: indefinite - congestion_metrics_cache: - description: - - Specifies, when C(yes), that the system uses a cache for storing congestion - metrics. - - Subsequently, because these metrics are already known and cached, the initial - slow-start ramp for previously-encountered peers improves. - returned: queried - type: bool - sample: yes - congestion_metrics_cache_timeout: - description: - - Specifies the number of seconds for which entries in the congestion metrics - cache are valid. - returned: queried - type: int - sample: 0 - congestion_control: - description: - - Specifies the algorithm to use to share network resources among competing - users to reduce congestion. - - Return values may include, C(high-speed), C(cdg), C(chd), C(none), C(cubic), - C(illinois), C(new-reno), C(reno), C(scalable), C(vegas), C(westwood), and - C(woodside). - returned: queried - type: str - sample: high-speed - deferred_accept: - description: - - Specifies, when C(yes), that the system defers allocation of the connection - chain context until the system has received the payload from the client. - - Enabling this setting is useful in dealing with 3-way handshake denial-of-service - attacks. - returned: queried - type: bool - sample: yes - delay_window_control: - description: - - Specifies that the system uses an estimate of queuing delay as a measure of - congestion to control, in addition to the normal loss-based control, the amount - of data sent. - returned: queried - type: bool - sample: yes - delayed_acks: - description: - - Specifies, when checked (enabled), that the system can send fewer than one ACK - (acknowledgment) segment per data segment received. - returned: queried - type: bool - sample: yes - dsack: - description: - - D-SACK (RFC 2883) - - Specifies, when C(yes), the use of the selective ACK (SACK) option to acknowledge - duplicate segments. - returned: queried - type: bool - sample: yes - early_retransmit: - description: - - Specifies, when C(yes), that the system uses early retransmit (as specified in - RFC 5827) to reduce the recovery time for connections that are receive- buffer - or user-data limited. - returned: queried - type: bool - sample: yes - explicit_congestion_notification: - description: - - Specifies, when C(yes), that the system uses the TCP flags CWR (congestion window - reduction) and ECE (ECN-Echo) to notify its peer of congestion and congestion - counter-measures. - returned: queried - type: bool - sample: yes - enhanced_loss_recovery: - description: - - Specifies whether the system uses enhanced loss recovery to recover from random - packet losses more effectively. - returned: queried - type: bool - sample: yes - fast_open: - description: - - Specifies, when C(yes), that the system supports TCP Fast Open, which reduces - latency by allowing a client to include the first packet of data with the SYN - returned: queried - type: bool - sample: yes - fast_open_cookie_expiration: - description: - - Specifies the number of seconds that a Fast Open Cookie delivered to a client - is valid for SYN packets from that client. - returned: queried - type: int - sample: 1000 - fin_wait_1: - description: - - Specifies the length of time that a TCP connection is in the FIN-WAIT-1 or - CLOSING state before quitting. - returned: queried - type: str - sample: indefinite - fin_wait_2: - description: - - Specifies the length of time that a TCP connection is in the FIN-WAIT-2 state - before quitting. - returned: queried - type: str - sample: 100 - idle_timeout: - description: - - Specifies the length of time that a connection is idle (has no traffic) before - the connection is eligible for deletion. - returned: queried - type: str - sample: 300 - initial_congestion_window_size: - description: - - Specifies the initial congestion window size for connections to this destination. - returned: queried - type: int - sample: 3 - initial_receive_window_size: - description: - - Specifies the initial receive window size for connections to this destination. - returned: queried - type: int - sample: 5 - dont_fragment_flag: - description: - - Specifies the Don't Fragment (DF) bit setting in the IP Header of the outgoing - TCP packet. - returned: queried - type: str - sample: pmtu - ip_tos: - description: - - Specifies the L3 Type of Service (ToS) level that the system inserts in TCP - packets destined for clients. - returned: queried - type: str - sample: mimic - time_to_live: - description: - - Specifies the outgoing TCP packet's IP Header TTL mode. - returned: queried - type: str - sample: proxy - time_to_live_v4: - description: - - Specifies the outgoing packet's IP Header TTL value for IPv4 traffic. - returned: queried - type: int - sample: 255 - time_to_live_v6: - description: - - Specifies the outgoing packet's IP Header TTL value for IPv6 traffic. - returned: queried - type: int - sample: 64 - keep_alive_interval: - description: - - Specifies how frequently the system sends data over an idle TCP - connection, to determine whether the connection is still valid. - returned: queried - type: str - sample: 50 - limited_transmit_recovery: - description: - - Specifies, when C(yes), that the system uses limited transmit recovery - revisions for fast retransmits (as specified in RFC 3042) to reduce - the recovery time for connections on a lossy network. - returned: queried - type: bool - sample: yes - link_qos: - description: - - Specifies the L2 Quality of Service (QoS) level that the system inserts - in TCP packets destined for clients. - returned: queried - type: str - sample: 200 - max_segment_retrans: - description: - - Specifies the maximum number of times that the system resends data segments. - returned: queried - type: int - sample: 8 - max_syn_retrans: - description: - - Specifies the maximum number of times that the system resends a SYN - packet when it does not receive a corresponding SYN-ACK. - returned: queried - type: int - sample: 3 - max_segment_size: - description: - - Specifies the largest amount of data that the system can receive in a - single TCP segment, not including the TCP and IP headers. - returned: queried - type: int - sample: 1460 - md5_signature: - description: - - Specifies, when C(yes), to use RFC2385 TCP-MD5 signatures to protect - TCP traffic against intermediate tampering. - returned: queried - type: bool - sample: yes - minimum_rto: - description: - - Specifies the minimum length of time the system waits for - acknowledgements of data sent before resending the data. - returned: queried - type: int - sample: 1000 - multipath_tcp: - description: - - Specifies, when C(yes), that the system accepts Multipath TCP (MPTCP) - connections, which allow multiple client-side flows to connect to a - single server-side flow. - returned: queried - type: bool - sample: yes - mptcp_checksum: - description: - - Specifies, when C(yes), that the system calculates the checksum for - MPTCP connections. - returned: queried - type: bool - sample: no - mptcp_checksum_verify: - description: - - Specifies, when C(yes), that the system verifies the checksum for - MPTCP connections. - returned: queried - type: bool - sample: no - mptcp_fallback: - description: - - Specifies an action on fallback, that is, when MPTCP transitions - to regular TCP, because something prevents MPTCP from working correctly. - returned: queried - type: str - sample: reset - mptcp_fast_join: - description: - - Specifies, when C(yes), a FAST join, allowing data to be sent on the - MP_JOIN_SYN, which can allow a server response to occur in parallel - with the JOIN. - returned: queried - type: bool - sample: no - mptcp_idle_timeout: - description: - - Specifies the number of seconds that an MPTCP connection is idle - before the connection is eligible for deletion. - returned: queried - type: int - sample: 300 - mptcp_join_max: - description: - - Specifies the highest number of MPTCP connections that can join to - a given connection. - returned: queried - type: int - sample: 5 - mptcp_make_after_break: - description: - - Specifies that make-after-break functionality is supported, allowing - for long-lived MPTCP sessions. - returned: queried - type: bool - sample: no - mptcp_no_join_dss_ack: - description: - - Specifies, when checked (enabled), that no DSS option is sent on the - JOIN ACK. - returned: queried - type: bool - sample: no - mptcp_rto_max: - description: - - Specifies the number of RTOs (retransmission timeouts) before declaring - the subflow dead. - returned: queried - type: int - sample: 5 - mptcp_retransmit_min: - description: - - Specifies the minimum value (in msec) of the retransmission timer for - these MPTCP flows. - returned: queried - type: int - sample: 1000 - mptcp_subflow_max: - description: - - Specifies the maximum number of MPTCP subflows for a single flow. - returned: queried - type: int - sample: 6 - mptcp_timeout: - description: - - Specifies, in seconds, the timeout value to discard long-lived sessions - that do not have an active flow. - returned: queried - type: int - sample: 3600 - nagle_algorithm: - description: - - Specifies whether the system applies Nagle's algorithm to reduce the - number of short segments on the network. - returned: queried - type: bool - sample: no - pkt_loss_ignore_burst: - description: - - Specifies the probability of performing congestion control when - multiple packets are lost, even if the Packet Loss Ignore Rate was - not exceeded. - returned: queried - type: int - sample: 0 - pkt_loss_ignore_rate: - description: - - Specifies the threshold of packets lost per million at which the - system performs congestion control. - returned: queried - type: int - sample: 0 - proxy_buffer_high: - description: - - Specifies the proxy buffer level, in bytes, at which the receive window - is closed. - returned: queried - type: int - sample: 49152 - proxy_buffer_low: - description: - - Specifies the proxy buffer level, in bytes, at which the receive window - is opened. - returned: queried - type: int - sample: 32768 - proxy_max_segment: - description: - - Specifies, when C(yes), that the system attempts to advertise the same - maximum segment size (MSS) to the server-side connection as that of the - client-side connection. - returned: queried - type: bool - sample: yes - proxy_options: - description: - - Specifies, when C(yes), that the system advertises an option (such as - time stamps) to the server only when the option is negotiated with the - client. - returned: queried - type: bool - sample: no - push_flag: - description: - - Specifies how the BIG-IP system receives ACKs. - returned: queried - type: str - sample: default - rate_pace: - description: - - Specifies, when C(yes), that the system paces the egress packets to - avoid dropping packets, allowing for optimum goodput. - returned: queried - type: bool - sample: yes - rate_pace_max_rate: - description: - - Specifies the maximum rate in bytes per second to which the system - paces TCP data transmission. - returned: queried - type: int - sample: 0 - receive_window: - description: - - Specifies the maximum advertised RECEIVE window size. - returned: queried - type: int - sample: 65535 - reset_on_timeout: - description: - - Specifies, when C(yes), that the system sends a reset packet (RST) - in addition to deleting the connection, when a connection exceeds - the idle timeout value. - returned: queried - type: bool - sample: yes - retransmit_threshold: - description: - - Specifies the number of duplicate ACKs (retransmit threshold) to start - fast recovery. - returned: queried - type: int - sample: 3 - selective_acks: - description: - - Specifies, when C(yes), that the system processes data using - selective ACKs (SACKs) whenever possible, to improve system performance. - returned: queried - type: bool - sample: yes - selective_nack: - description: - - Specifies, when C(yes), that the system processes data using a selective - negative acknowledgment (SNACK) whenever possible, to improve system - performance. - returned: queried - type: bool - sample: yes - send_buffer: - description: - - Specifies the SEND window size. - returned: queried - type: int - sample: 65535 - slow_start: - description: - - Specifies, when C(yes), that the system uses Slow-Start Congestion - Avoidance as described in RFC3390 in order to ramp up traffic without - causing excessive congestion on the link. - returned: queried - type: bool - sample: yes - syn_cookie_enable: - description: - - Specifies the default (if no DoS profile is associated) number of - embryonic connections that are allowed on any virtual server, - before SYN Cookie challenges are enabled for that virtual server. - returned: queried - type: bool - sample: yes - syn_cookie_white_list: - description: - - Specifies whether or not to use a SYN Cookie WhiteList when doing - software SYN Cookies. - returned: queried - type: bool - sample: no - syn_retrans_to_base: - description: - - Specifies the initial RTO (Retransmission TimeOut) base multiplier - for SYN retransmissions. - returned: queried - type: int - sample: 3000 - tail_loss_probe: - description: - - Specifies, when C(yes), that the system uses Tail Loss Probe to - reduce the number of retransmission timeouts. - returned: queried - type: bool - sample: yes - time_wait_recycle: - description: - - Specifies, when C(yes), that connections in a TIME-WAIT state are - reused when the system receives a SYN packet, indicating a request - for a new connection. - returned: queried - type: bool - sample: yes - time_wait: - description: - - Specifies the length of time that a TCP connection remains in the - TIME-WAIT state before entering the CLOSED state. - returned: queried - type: str - sample: 2000 - timestamps: - description: - - Specifies, when C(yes), that the system uses the timestamps extension - for TCP (as specified in RFC 1323) to enhance high-speed network performance. - returned: queried - type: bool - sample: yes - verified_accept: - description: - - Specifies, when C(yes), that the system can actually communicate with - the server before establishing a client connection. - returned: queried - type: bool - sample: yes - zero_window_timeout: - description: - - Specifies the timeout in milliseconds for terminating a connection - with an effective zero length TCP transmit window. - returned: queried - type: str - sample: 2000 - sample: hash/dictionary of values -traffic_groups: - description: Traffic group related information. - returned: When C(traffic-groups) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/tg1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: tg1 - description: - description: - - Description of the traffic group. - returned: queried - type: str - sample: My traffic group - auto_failback_enabled: - description: - - Specifies whether the traffic group fails back to the default - device. - returned: queried - type: bool - sample: yes - auto_failback_time: - description: - - Specifies the time required to fail back. - returned: queried - type: int - sample: 60 - ha_load_factor: - description: - - Specifies a number for this traffic group that represents the load - this traffic group presents to the system relative to other - traffic groups. - returned: queried - type: int - sample: 1 - ha_order: - description: - - This list of devices specifies the order in which the devices will - become active for the traffic group when a failure occurs. - returned: queried - type: list - sample: ['/Common/device1', '/Common/device2'] - is_floating: - description: - - Indicates whether the traffic group can fail over to other devices - in the device group. - returned: queried - type: bool - sample: no - mac_masquerade_address: - description: - - Specifies a MAC address for the traffic group. - returned: queried - type: str - sample: "00:98:76:54:32:10" - sample: hash/dictionary of values -trunks: - description: Trunk related information. - returned: When C(trunks) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/trunk1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: trunk1 - description: - description: - - Description of the Trunk. - returned: queried - type: str - sample: My trunk - media_speed: - description: - - Speed of the media attached to the trunk. - returned: queried - type: int - sample: 10000 - lacp_mode: - description: - - The operation mode for LACP. - returned: queried - type: str - sample: passive - lacp_enabled: - description: - - Whether LACP is enabled or not. - returned: queried - type: bool - sample: yes - stp_enabled: - description: - - Whether Spanning Tree Protocol (STP) is enabled or not. - returned: queried - type: bool - sample: yes - operational_member_count: - description: - - Number of working members associated with the trunk. - returned: queried - type: int - sample: 1 - media_status: - description: - - Whether the media that is part of the trunk is up or not. - returned: queried - type: bool - sample: yes - link_selection_policy: - description: - - The LACP policy that the trunk uses to determine which member link can handle - new traffic. - returned: queried - type: str - sample: maximum-bandwidth - lacp_timeout: - description: - - The rate at which the system sends the LACP control packets. - returned: queried - type: int - sample: 10 - interfaces: - description: - - The list of interfaces that are part of the trunk. - returned: queried - type: list - sample: ['1.2', '1.3'] - distribution_hash: - description: - - The basis for the has that the system uses as the frame distribution algorithm. - - The system uses this hash to determine which interface to use for forwarding - traffic. - returned: queried - type: str - sample: src-dst-ipport - configured_member_count: - description: - - The number of configured members that are associated with the trunk. - returned: queried - type: int - sample: 1 - sample: hash/dictionary of values -udp_profiles: - description: UDP profile related information. - returned: When C(udp-profiles) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: udp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: /Common/udp - parent: - description: - - Profile from which this profile inherits settings. - returned: queried - type: str - sample: udp - description: - description: - - Description of the resource. - returned: queried - type: str - sample: My profile - allow_no_payload: - description: - - Allow the passage of datagrams that contain header information, but no essential data. - returned: queried - type: bool - sample: yes - buffer_max_bytes: - description: - - Ingress buffer byte limit. Maximum allowed value is 16777215. - returned: queried - type: int - sample: 655350 - buffer_max_packets: - description: - - Ingress buffer packet limit. Maximum allowed value is 255. - returned: queried - type: int - sample: 0 - datagram_load_balancing: - description: - - Load balance UDP datagram by datagram - returned: queried - type: bool - sample: yes - idle_timeout: - description: - - Number of seconds that a connection is idle before - the connection is eligible for deletion. - - In addition to a number, may be one of the values C(indefinite), or - C(immediate). - returned: queried - type: bool - sample: 200 - ip_df_mode: - description: - - Describes the Don't Fragment (DF) bit setting in the outgoing UDP - packet. - - May be one of C(pmtu), C(preserve), C(set), or C(clear). - - When C(pmtu), sets the outgoing UDP packet DF big based on the ip - pmtu setting. - - When C(preserve), preserves the incoming UDP packet Don't Fragment bit. - - When C(set), sets the outgoing UDP packet DF bit. - - When C(clear), clears the outgoing UDP packet DF bit. - returned: queried - type: str - sample: pmtu - ip_tos_to_client: - description: - - The Type of Service level that the traffic management - system assigns to UDP packets when sending them to clients. - - May be numeric, or the values C(pass-through) or C(mimic). - returned: queried - type: str - sample: mimic - ip_ttl_mode: - description: - - The outgoing UDP packet's TTL mode. - - Valid modes are C(proxy), C(preserve), C(decrement), and C(set). - - When C(proxy), set the IP TTL of ipv4 to the default value of 255 and - ipv6 to the default value of 64. - - When C(preserve), set the IP TTL to the original packet TTL value. - - When C(decrement), set the IP TTL to the original packet TTL value minus 1. - - When C(set), set the IP TTL with the specified values in C(ip_ttl_v4) and - C(ip_ttl_v6) values in the same profile. - returned: queried - type: str - sample: proxy - ip_ttl_v4: - description: - - IPv4 TTL. - returned: queried - type: int - sample: 10 - ip_ttl_v6: - description: - - IPv6 TTL. - returned: queried - type: int - sample: 100 - link_qos_to_client: - description: - - The Quality of Service level that the system assigns to - UDP packets when sending them to clients. - - May be either numeric, or the value C(pass-through). - returned: queried - type: str - sample: pass-through - no_checksum: - description: - - Whether the checksum processing is enabled or disabled. - - Note that if the datagram is IPv6, the system always performs - checksum processing. - returned: queried - type: bool - sample: yes - proxy_mss: - description: - - When C(yes), specifies that the system advertises the same mss - to the server as was negotiated with the client. - returned: queried - type: bool - sample: yes - sample: hash/dictionary of values -users: - description: Details of the users on the system. - returned: When C(users) is specified in C(gather_subset). - type: complex - contains: - description: - description: - - Description of the resource. - returned: queried - type: str - sample: Admin user - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: admin - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: admin - partition_access: - description: - - Partition that user has access to, including user role. - returned: queried - type: complex - contains: - name: - description: - - Name of partition - returned: queried - type: str - sample: all-partitions - role: - description: - - Role allowed to user on partition. - returned: queried - type: str - sample: auditor - shell: - description: - - The shell assigned to the user account. - returned: queried - type: str - sample: tmsh -vcmp_guests: - description: vCMP related information. - returned: When C(vcmp-guests) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: guest1 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: guest1 - allowed_slots: - description: - - List of slots that the guest is allowed to be assigned to. - returned: queried - type: list - sample: [0, 1, 3] - assigned_slots: - description: - - Slots that the guest is assigned to. - returned: queried - type: list - sample: [0] - boot_priority: - description: - - Specifies boot priority of the guest. Lower number means earlier to boot. - returned: queried - type: int - sample: 65535 - cores_per_slot: - description: - - Number of cores that the system allocates to the guest. - returned: queried - type: int - sample: 2 - hostname: - description: - - FQDN assigned to the guest. - returned: queried - type: str - sample: guest1.localdomain - hotfix_image: - description: - - hotfix image to install onto any of this guest's newly created virtual disks. - returned: queried - type: str - sample: Hotfix-BIGIP-12.1.3.4-0.0.2-hf1.iso - initial_image: - description: - - Software image to install onto any of this guest's newly created virtual disks. - returned: queried - type: str - sample: BIGIP-12.1.3.4-0.0.2.iso - mgmt_route: - description: - - Management gateway IP address for the guest. - returned: queried - type: str - sample: 2.2.2.1 - mgmt_address: - description: - - Management IP address configuration for the guest. - returned: queried - type: str - sample: 2.3.2.3 - mgmt_network: - description: - - Accessibility of this vCMP guest's management network. - returned: queried - type: str - sample: bridged - vlans: - description: - - List of VLANs on which the guest is either enabled or disabled. - returned: queried - type: list - sample: ['/Common/vlan1', '/Common/vlan2'] - min_number_of_slots: - description: - - Specifies the minimum number of slots that the guest must be assigned to. - returned: queried - type: int - sample: 2 - number_of_slots: - description: - - Specifies the number of slots the guest should be assigned to. - - This number is always greater than, or equal to, C(min_number_of_slots). - returned: queried - type: int - sample: 2 - ssl_mode: - description: - - The SSL hardware allocation mode for the guest. - returned: queried - type: str - sample: shared - state: - description: - - Specifies the state of the guest. - - May be one of C(configured), C(provisioned), or C(deployed). - - Each state implies the actions of all states before it. - returned: queried - type: str - sample: provisioned - virtual_disk: - description: - - The filename of the virtual disk to use for this guest. - returned: queried - type: str - sample: guest1.img - sample: hash/dictionary of values -virtual_addresses: - description: Virtual address related information. - returned: When C(virtual-addresses) is specified in C(gather_subset). - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/2.3.4.5 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: 2.3.4.5 - address: - description: - - The virtual IP address. - returned: queried - type: str - sample: 2.3.4.5 - arp_enabled: - description: - - Whether or not ARP is enabled for the specified virtual address. - returned: queried - type: bool - sample: yes - auto_delete_enabled: - description: - - Indicates if the virtual address will be deleted automatically on - deletion of the last associated virtual server or not. - returned: queried - type: bool - sample: no - connection_limit: - description: - - Concurrent connection limit for one or more virtual - servers. - returned: queried - type: int - sample: 0 - description: - description: - - The description of the virtual address. - returned: queried - type: str - sample: My virtual address - enabled: - description: - - Whether the virtual address is enabled or not. - returned: queried - type: bool - sample: yes - icmp_echo: - description: - - Whether the virtual address should reply to ICMP echo requests. - returned: queried - type: bool - sample: yes - floating: - description: - - Property derived from traffic-group. A floating virtual - address is a virtual address for a VLAN that serves as a shared - address by all devices of a BIG-IP traffic-group. - returned: queried - type: bool - sample: yes - netmask: - description: - - Netmask of the virtual address. - returned: queried - type: str - sample: 255.255.255.255 - route_advertisement: - description: - - Specifies the route advertisement setting for the virtual address. - returned: queried - type: bool - sample: no - traffic_group: - description: - - Traffic group on which the virtual address is active. - returned: queried - type: str - sample: /Common/traffic-group-1 - spanning: - description: - - Whether or not spanning is enabled for the specified virtual address. - returned: queried - type: bool - sample: no - inherited_traffic_group: - description: - - Indicates if the traffic-group is inherited from the parent folder. - returned: queried - type: bool - sample: no - sample: hash/dictionary of values -virtual_servers: - description: Virtual address related information. - returned: When C(virtual-addresses) is specified in C(gather_subset). - type: complex - contains: - availability_status: - description: - - The availability of the virtual server. - returned: queried - type: str - sample: offline - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/2.3.4.5 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: 2.3.4.5 - auto_lasthop: - description: - - When enabled, allows the system to send return traffic to the MAC address - that transmitted the request, even if the routing table points to a different - network or interface. - returned: queried - type: str - sample: default - bw_controller_policy: - description: - - The bandwidth controller for the system to use to enforce a throughput policy - for incoming network traffic. - returned: queried - type: str - sample: /Common/bw1 - client_side_bits_in: - description: - - Number of client-side ingress bits. - returned: queried - type: int - sample: 1000 - client_side_bits_out: - description: - - Number of client-side egress bits. - returned: queried - type: int - sample: 200 - client_side_current_connections: - description: - - Number of current connections client-side. - returned: queried - type: int - sample: 300 - client_side_evicted_connections: - description: - - Number of evicted connections client-side. - returned: queried - type: int - sample: 100 - client_side_max_connections: - description: - - Maximum number of connections client-side. - returned: queried - type: int - sample: 40 - client_side_pkts_in: - description: - - Number of client-side ingress packets. - returned: queried - type: int - sample: 1098384 - client_side_pkts_out: - description: - - Number of client-side egress packets. - returned: queried - type: int - sample: 3484734 - client_side_slow_killed: - description: - - Number of slow connections killed, client-side. - returned: queried - type: int - sample: 234 - client_side_total_connections: - description: - - Total number of connections. - returned: queried - type: int - sample: 24 - cmp_enabled: - description: - - Whether or not clustered multi-processor (CMP) acceleration is enabled. - returned: queried - type: bool - sample: yes - cmp_mode: - description: - - The clustered-multiprocessing mode. - returned: queried - type: str - sample: all-cpus - connection_limit: - description: - - Maximum number of concurrent connections you want to allow for the virtual server. - returned: queried - type: int - sample: 100 - description: - description: - - The description of the virtual server. - returned: queried - type: str - sample: My virtual - enabled: - description: - - Whether or not the virtual is enabled. - returned: queried - type: bool - sample: yes - ephemeral_bits_in: - description: - - Number of ephemeral ingress bits. - returned: queried - type: int - sample: 1000 - ephemeral_bits_out: - description: - - Number of ephemeral egress bits. - returned: queried - type: int - sample: 200 - ephemeral_current_connections: - description: - - Number of ephemeral current connections. - returned: queried - type: int - sample: 300 - ephemeral_evicted_connections: - description: - - Number of ephemeral evicted connections. - returned: queried - type: int - sample: 100 - ephemeral_max_connections: - description: - - Maximum number of ephemeral connections. - returned: queried - type: int - sample: 40 - ephemeral_pkts_in: - description: - - Number of ephemeral ingress packets. - returned: queried - type: int - sample: 1098384 - ephemeral_pkts_out: - description: - - Number of ephemeral egress packets. - returned: queried - type: int - sample: 3484734 - ephemeral_slow_killed: - description: - - Number of ephemeral slow connections killed. - returned: queried - type: int - sample: 234 - ephemeral_total_connections: - description: - - Total number of ephemeral connections. - returned: queried - type: int - sample: 24 - total_software_accepted_syn_cookies: - description: - - SYN Cookies Total Software Accepted. - returned: queried - type: int - sample: 0 - total_hardware_accepted_syn_cookies: - description: - - SYN Cookies Total Hardware Accepted. - returned: queried - type: int - sample: 0 - total_hardware_syn_cookies: - description: - - SYN Cookies Total Hardware - returned: queried - type: int - sample: 0 - hardware_syn_cookie_instances: - description: - - Hardware SYN Cookie Instances - returned: queried - type: int - sample: 0 - total_software_rejected_syn_cookies: - description: - - Total Software Rejected - returned: queried - type: int - sample: 0 - software_syn_cookie_instances: - description: - - Software SYN Cookie Instances - returned: queried - type: int - sample: 0 - current_syn_cache: - description: - - Current SYN Cache - returned: queried - type: int - sample: 0 - max_conn_duration: - description: - - Max Conn Duration/msec - returned: queried - type: int - sample: 0 - mean_conn_duration: - description: - - Mean Conn Duration/msec - returned: queried - type: int - sample: 0 - min_conn_duration: - description: - - Min Conn Duration/msec - returned: queried - type: int - sample: 0 - cpu_usage_ratio_last_5_min: - description: - - CPU Usage Ratio (%) Last 5 Minutes - returned: queried - type: int - sample: 0 - cpu_usage_ratio_last_5_sec: - description: - - CPU Usage Ratio (%) Last 5 Seconds - returned: queried - type: int - sample: 0 - cpu_usage_ratio_last_1_min: - description: - - CPU Usage Ratio (%) Last 1 Minute - returned: queried - type: int - sample: 0 - syn_cache_overflow: - description: - - SYN Cache Overflow - returned: queried - type: int - sample: 0 - total_software_syn_cookies: - description: - - Total Software - returned: queried - type: int - sample: 0 - syn_cookies_status: - description: - - SYN Cookies Status - returned: queried - type: str - sample: not-activated - fallback_persistence_profile: - description: - - Fallback persistence profile for the virtual server to use - when the default persistence profile is not available. - returned: queried - type: str - sample: /Common/fallback1 - persistence_profile: - description: - - The persistence profile you want the system to use as the default - for this virtual server. - returned: queried - type: str - sample: /Common/persist1 - translate_port: - description: - - Enables or disables port translation. - returned: queried - type: bool - sample: yes - translate_address: - description: - - Enables or disables address translation for the virtual server. - returned: queried - type: bool - sample: yes - vlans: - description: - - List of VLANs on which the virtual server is either enabled or disabled. - returned: queried - type: list - sample: ['/Common/vlan1', '/Common/vlan2'] - destination: - description: - - Name of the virtual address and service on which the virtual server - listens for connections. - returned: queried - type: str - sample: /Common/2.2.3.3%1:76 - last_hop_pool: - description: - - Name of the last hop pool that you want the virtual - server to use to direct reply traffic to the last hop router. - returned: queried - type: str - sample: /Common/pool1 - nat64_enabled: - description: - - Whether or not NAT64 is enabled. - returned: queried - type: bool - sample: yes - source_port_behavior: - description: - - Specifies whether the system preserves the source port of the connection. - returned: queried - type: str - sample: preserve - ip_intelligence_policy: - description: - - IP Intelligence policy assigned to the virtual - returned: queried - type: str - sample: /Common/ip1 - protocol: - description: - - IP protocol for which you want the virtual server to direct traffic. - returned: queried - type: str - sample: tcp - default_pool: - description: - - Pool name that you want the virtual server to use as the default pool. - returned: queried - type: str - sample: /Common/pool1 - rate_limit_mode: - description: - - Indicates whether the rate limit is applied per virtual object, - per source address, per destination address, or some combination - thereof. - returned: queried - type: str - sample: object - rate_limit_source_mask: - description: - - Specifies a mask, in bits, to be applied to the source address as - part of the rate limiting. - returned: queried - type: int - sample: 0 - rate_limit: - description: - - Maximum number of connections per second allowed for a virtual server. - returned: queried - type: int - sample: 34 - snat_type: - description: - - Specifies the type of source address translation associated - with the specified virtual server. - returned: queried - type: str - sample: none - snat_pool: - description: - - Specifies the name of a LSN or SNAT pool used by the specified virtual server. - returned: queried - type: str - sample: /Common/pool1 - status_reason: - description: - - If there is a problem with the status of the virtual, that problem is reported here. - returned: queried - type: str - sample: The children pool member(s) either don't have service checking... - gtm_score: - description: - - Specifies a score that is associated with the virtual server. - returned: queried - type: int - sample: 0 - rate_class: - description: - - Name of an existing rate class that you want the - virtual server to use to enforce a throughput policy for incoming - network traffic. - returned: queried - type: str - rate_limit_destination_mask: - description: - - Specifies a mask, in bits, to be applied to the destination - address as part of the rate limiting. - returned: queried - type: int - sample: 32 - source_address: - description: - - Specifies an IP address or network from which the virtual server - will accept traffic. - returned: queried - type: str - sample: 0.0.0./0 - authentication_profile: - description: - - Specifies a list of authentication profile names, separated by - spaces, that the virtual server uses to manage authentication. - returned: queried - type: list - sample: ['/Common/ssl_drldp'] - connection_mirror_enabled: - description: - - Whether or not connection mirroring is enabled. - returned: queried - type: bool - sample: yes - irules: - description: - - List of iRules that customize the virtual server to direct and manage traffic. - returned: queried - type: list - sample: ['/Common/rule1', /Common/rule2'] - security_log_profiles: - description: - - Specifies the log profile applied to the virtual server. - returned: queried - type: list - sample: ['/Common/global-network', '/Common/local-dos'] - type: - description: - - Virtual server type. - returned: queried - type: str - sample: standard - destination_address: - description: - - Address portion of the C(destination). - returned: queried - type: str - sample: 2.3.3.2 - destination_port: - description: - - Port potion of the C(destination). - returned: queried - type: int - sample: 80 - profiles: - description: - - List of the profiles attached to the virtual. - type: complex - contains: - context: - description: - - Which side of the connection the profile affects; either C(all), - C(client-side) or C(server-side). - returned: queried - type: str - sample: client-side - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: /Common/tcp - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: tcp - total_requests: - description: - - Total requests. - returned: queried - type: int - sample: 8 - sample: hash/dictionary of values -vlans: - description: List of VLAN information. - returned: When C(vlans) is specified in C(gather_subset). - type: complex - contains: - auto_lasthop: - description: - - Allows the system to send return traffic to the MAC address that transmitted the - request, even if the routing table points to a different network or interface. - returned: queried - type: str - sample: enabled - cmp_hash_algorithm: - description: - - Specifies how the traffic on the VLAN will be disaggregated. - returned: queried - type: str - sample: default - description: - description: - - Description of the VLAN. - returned: queried - type: str - sample: My vlan - failsafe_action: - description: - - Action for the system to take when the fail-safe mechanism is triggered. - returned: queried - type: str - sample: reboot - failsafe_enabled: - description: - - Whether failsafe is enabled or not. - returned: queried - type: bool - sample: yes - failsafe_timeout: - description: - - Number of seconds that an active unit can run without detecting network traffic - on this VLAN before it starts a failover. - returned: queried - type: int - sample: 90 - if_index: - description: - - Index assigned to this VLAN. It is a unique identifier assigned for all objects - displayed in the SNMP IF-MIB. - returned: queried - type: int - sample: 176 - learning_mode: - description: - - Whether switch ports placed in the VLAN are configured for switch learning, - forwarding only, or dropped. - returned: queried - type: str - sample: enable-forward - interfaces: - description: - - List of tagged or untagged interfaces and trunks that you want to configure for the VLAN. - returned: queried - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: queried - type: str - sample: 1.3 - name: - description: - - Relative name of the resource in BIG-IP. - returned: queried - type: str - sample: 1.3 - tagged: - description: - - Whether the interface is tagged or not. - returned: queried - type: bool - sample: no - mtu: - description: - - Specific maximum transition unit (MTU) for the VLAN. - returned: queried - type: int - sample: 1500 - sflow_poll_interval: - description: - - Maximum interval in seconds between two pollings. - returned: queried - type: int - sample: 0 - sflow_poll_interval_global: - description: - - Whether the global VLAN poll-interval setting, overrides the object-level - poll-interval setting. - returned: queried - type: bool - sample: no - sflow_sampling_rate: - description: - - Ratio of packets observed to the samples generated. - returned: queried - type: int - sample: 0 - sflow_sampling_rate_global: - description: - - Whether the global VLAN sampling-rate setting, overrides the object-level - sampling-rate setting. - returned: queried - type: bool - sample: yes - source_check_enabled: - description: - - Specifies that only connections that have a return route in the routing table are accepted. - returned: queried - type: bool - sample: yes - true_mac_address: - description: - - Media access control (MAC) address for the lowest-numbered interface assigned to this VLAN. - returned: queried - type: str - sample: "fa:16:3e:10:da:ff" - tag: - description: - - Tag number for the VLAN. - returned: queried - type: int - sample: 30 - sample: hash/dictionary of values -''' - -import datetime -import math -import re -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_netmask -from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE -from ansible.module_utils.six import iteritems -from ansible.module_utils.six import string_types -from collections import namedtuple -from distutils.version import LooseVersion - -try: - from library.module_utils.network.f5.bigip import F5RestClient - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import AnsibleF5Parameters - from library.module_utils.network.f5.common import f5_argument_spec - from library.module_utils.network.f5.common import fq_name - from library.module_utils.network.f5.common import flatten_boolean - from library.module_utils.network.f5.common import transform_name - from library.module_utils.network.f5.ipaddress import is_valid_ip - from library.module_utils.network.f5.icontrol import modules_provisioned - from library.module_utils.network.f5.icontrol import tmos_version - from library.module_utils.network.f5.urls import parseStats -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.bigip import F5RestClient - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import fq_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import flatten_boolean - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import transform_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.ipaddress import is_valid_ip - from ansible_collections.f5networks.f5_modules.plugins.module_utils.icontrol import modules_provisioned - from ansible_collections.f5networks.f5_modules.plugins.module_utils.icontrol import tmos_version - from ansible_collections.community.general.plugins.module_utils.network.f5.urls import parseStats - - -class BaseManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = kwargs.get('client', None) - self.kwargs = kwargs - - # A list of modules currently provisioned on the device. - # - # This list is used by different fact managers to check to see - # if they should even attempt to gather information. If the module is - # not provisioned, then it is likely that the REST API will not - # return valid data. - # - # For example, ASM (at the time of this writing 13.x/14.x) will - # raise an exception if you attempt to query its APIs if it is - # not provisioned. An example error message is shown below. - # - # { - # "code": 400, - # "message": "java.net.ConnectException: Connection refused (Connection refused)", - # "referer": "172.18.43.40", - # "restOperationId": 18164160, - # "kind": ":resterrorresponse" - # } - # - # This list is provided to the specific fact manager by the - # master ModuleManager of this module. - self.provisioned_modules = [] - - def exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - return results - - -class Parameters(AnsibleF5Parameters): - @property - def gather_subset(self): - if isinstance(self._values['gather_subset'], string_types): - self._values['gather_subset'] = [self._values['gather_subset']] - elif not isinstance(self._values['gather_subset'], list): - raise F5ModuleError( - "The specified gather_subset must be a list." - ) - tmp = list(set(self._values['gather_subset'])) - tmp.sort() - self._values['gather_subset'] = tmp - - return self._values['gather_subset'] - - -class BaseParameters(Parameters): - @property - def enabled(self): - return flatten_boolean(self._values['enabled']) - - @property - def disabled(self): - return flatten_boolean(self._values['disabled']) - - def _remove_internal_keywords(self, resource): - resource.pop('kind', None) - resource.pop('generation', None) - resource.pop('selfLink', None) - resource.pop('isSubcollection', None) - resource.pop('fullPath', None) - - def to_return(self): - result = {} - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - - -class AsmPolicyStatsParameters(BaseParameters): - api_map = { - - } - - returnables = [ - 'policies', - 'policies_active', - 'policies_attached', - 'policies_inactive', - 'policies_unattached', - ] - - @property - def policies(self): - if self._values['policies'] is None or len(self._values['policies']) == 0: - return None - return len(self._values['policies']) - - @property - def policies_active(self): - if self._values['policies'] is None or len(self._values['policies']) == 0: - return None - return len([x for x in self._values['policies'] if x['active'] is True]) - - @property - def policies_inactive(self): - if self._values['policies'] is None or len(self._values['policies']) == 0: - return None - return len([x for x in self._values['policies'] if x['active'] is not True]) - - @property - def policies_attached(self): - if self._values['policies'] is None or len(self._values['policies']) == 0: - return None - return len([x for x in self._values['policies'] if x['active'] is True and len(x['virtualServers']) > 0]) - - @property - def policies_unattached(self): - if self._values['policies'] is None or len(self._values['policies']) == 0: - return None - return len([x for x in self._values['policies'] if x['active'] is True and len(x['virtualServers']) == 0]) - - -class AsmPolicyStatsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(AsmPolicyStatsFactManager, self).__init__(**kwargs) - self.want = AsmPolicyStatsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(asm_policy_stats=facts) - return result - - def _exec_module(self): - if 'asm' not in self.provisioned_modules: - return [] - facts = self.read_facts() - results = facts.to_return() - return results - - def read_facts(self): - collection = self.read_collection_from_device() - params = AsmPolicyStatsParameters(params=collection) - return params - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/asm/policies".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return dict( - policies=response['items'] - ) - - -class AsmPolicyFactParameters(BaseParameters): - api_map = { - 'hasParent': 'has_parent', - 'protocolIndependent': 'protocol_independent', - 'virtualServers': 'virtual_servers', - 'allowedResponseCodes': 'allowed_response_codes', - 'learningMode': 'learning_mode', - 'enforcementMode': 'enforcement_mode', - 'customXffHeaders': 'custom_xff_headers', - 'caseInsensitive': 'case_insensitive', - 'stagingSettings': 'staging_settings', - 'applicationLanguage': 'application_language', - 'trustXff': 'trust_xff', - 'geolocation-enforcement': 'geolocation_enforcement', - 'disallowedLocations': 'disallowed_locations', - 'signature-settings': 'signature_settings', - 'header-settings': 'header_settings', - 'cookie-settings': 'cookie_settings', - 'policy-builder': 'policy_builder', - 'disallowed-geolocations': 'disallowed_geolocations', - 'whitelist-ips': 'whitelist_ips', - 'fullPath': 'full_path', - 'csrf-protection': 'csrf_protection', - } - - returnables = [ - 'full_path', - 'name', - 'policy_id', - 'active', - 'protocol_independent', - 'has_parent', - 'type', - 'virtual_servers', - 'allowed_response_codes', - 'description', - 'learning_mode', - 'enforcement_mode', - 'custom_xff_headers', - 'case_insensitive', - 'signature_staging', - 'place_signatures_in_staging', - 'enforcement_readiness_period', - 'path_parameter_handling', - 'trigger_asm_irule_event', - 'inspect_http_uploads', - 'mask_credit_card_numbers_in_request', - 'maximum_http_header_length', - 'use_dynamic_session_id_in_url', - 'maximum_cookie_header_length', - 'application_language', - 'trust_xff', - 'disallowed_geolocations', - 'csrf_urls', - 'csrf_protection_enabled', - 'csrf_protection_ssl_only', - 'csrf_protection_expiration_time_in_seconds', - ] - - def _morph_keys(self, key_map, item): - for k, v in iteritems(key_map): - item[v] = item.pop(k, None) - result = self._filter_params(item) - return result - - @property - def active(self): - return flatten_boolean(self._values['active']) - - @property - def case_insensitive(self): - return flatten_boolean(self._values['case_insensitive']) - - @property - def has_parent(self): - return flatten_boolean(self._values['has_parent']) - - @property - def policy_id(self): - if self._values['id'] is None: - return None - return self._values['id'] - - @property - def signature_staging(self): - if 'staging_settings' in self._values: - if self._values['staging_settings'] is None: - return None - if 'signatureStaging' in self._values['staging_settings']: - return flatten_boolean(self._values['staging_settings']['signatureStaging']) - if 'signature_settings' in self._values: - if self._values['signature_settings'] is None: - return None - if 'signatureStaging' in self._values['signature_settings']: - return flatten_boolean(self._values['signature_settings']['signatureStaging']) - - @property - def place_signatures_in_staging(self): - if 'staging_settings' in self._values: - if self._values['staging_settings'] is None: - return None - if 'placeSignaturesInStaging' in self._values['staging_settings']: - return flatten_boolean(self._values['staging_settings']['placeSignaturesInStaging']) - if 'signature_settings' in self._values: - if self._values['signature_settings'] is None: - return None - if 'signatureStaging' in self._values['signature_settings']: - return flatten_boolean(self._values['signature_settings']['placeSignaturesInStaging']) - - @property - def enforcement_readiness_period(self): - if 'staging_settings' in self._values: - if self._values['staging_settings'] is None: - return None - if 'enforcementReadinessPeriod' in self._values['staging_settings']: - return self._values['staging_settings']['enforcementReadinessPeriod'] - if 'general' in self._values: - if self._values['general'] is None: - return None - if 'signatureStaging' in self._values['general']: - return self._values['general']['enforcementReadinessPeriod'] - - @property - def path_parameter_handling(self): - if 'attributes' in self._values: - if self._values['attributes'] is None: - return None - if 'pathParameterHandling' in self._values['attributes']: - return self._values['attributes']['pathParameterHandling'] - if 'general' in self._values: - if self._values['general'] is None: - return None - if 'pathParameterHandling' in self._values['general']: - return self._values['general']['pathParameterHandling'] - - @property - def trigger_asm_irule_event(self): - if 'attributes' in self._values: - if self._values['attributes'] is None: - return None - if 'triggerAsmIruleEvent' in self._values['attributes']: - return self._values['attributes']['triggerAsmIruleEvent'] - if 'general' in self._values: - if self._values['general'] is None: - return None - if 'triggerAsmIruleEvent' in self._values['general']: - return self._values['general']['triggerAsmIruleEvent'] - - @property - def inspect_http_uploads(self): - if 'attributes' in self._values: - if self._values['attributes'] is None: - return None - if 'inspectHttpUploads' in self._values['attributes']: - return flatten_boolean(self._values['attributes']['inspectHttpUploads']) - if 'antivirus' in self._values: - if self._values['antivirus'] is None: - return None - if 'inspectHttpUploads' in self._values['antivirus']: - return flatten_boolean(self._values['antivirus']['inspectHttpUploads']) - - @property - def mask_credit_card_numbers_in_request(self): - if 'attributes' in self._values: - if self._values['attributes'] is None: - return None - if 'maskCreditCardNumbersInRequest' in self._values['attributes']: - return flatten_boolean(self._values['attributes']['maskCreditCardNumbersInRequest']) - if 'general' in self._values: - if self._values['general'] is None: - return None - if 'maskCreditCardNumbersInRequest' in self._values['general']: - return flatten_boolean(self._values['general']['maskCreditCardNumbersInRequest']) - - @property - def maximum_http_header_length(self): - if 'attributes' in self._values: - if self._values['attributes'] is None: - return None - if 'maximumHttpHeaderLength' in self._values['attributes']: - if self._values['attributes']['maximumHttpHeaderLength'] == 'any': - return 'any' - return int(self._values['attributes']['maximumHttpHeaderLength']) - - if 'header_settings' in self._values: - if self._values['header_settings'] is None: - return None - if 'maximumHttpHeaderLength' in self._values['header_settings']: - if self._values['header_settings']['maximumHttpHeaderLength'] == 'any': - return 'any' - return int(self._values['header_settings']['maximumHttpHeaderLength']) - - @property - def use_dynamic_session_id_in_url(self): - if 'attributes' in self._values: - if self._values['attributes'] is None: - return None - if 'useDynamicSessionIdInUrl' in self._values['attributes']: - return flatten_boolean(self._values['attributes']['useDynamicSessionIdInUrl']) - if 'general' in self._values: - if self._values['general'] is None: - return None - if 'useDynamicSessionIdInUrl' in self._values['general']: - return flatten_boolean(self._values['general']['useDynamicSessionIdInUrl']) - - @property - def maximum_cookie_header_length(self): - if 'attributes' in self._values: - if self._values['attributes'] is None: - return None - if 'maximumCookieHeaderLength' in self._values['attributes']: - if self._values['attributes']['maximumCookieHeaderLength'] == 'any': - return 'any' - return int(self._values['attributes']['maximumCookieHeaderLength']) - if 'cookie_settings' in self._values: - if self._values['cookie_settings'] is None: - return None - if 'maximumCookieHeaderLength' in self._values['cookie_settings']: - if self._values['cookie_settings']['maximumCookieHeaderLength'] == 'any': - return 'any' - return int(self._values['cookie_settings']['maximumCookieHeaderLength']) - - @property - def trust_xff(self): - if 'trust_xff' in self._values: - if self._values['trust_xff'] is None: - return None - return flatten_boolean(self._values['trust_xff']) - if 'general' in self._values: - if self._values['general'] is None: - return None - if 'trustXff' in self._values['general']: - return flatten_boolean(self._values['general']['trustXff']) - - @property - def custom_xff_headers(self): - if 'custom_xff_headers' in self._values: - if self._values['custom_xff_headers'] is None: - return None - return self._values['custom_xff_headers'] - if 'general' in self._values: - if self._values['general'] is None: - return None - if 'customXffHeaders' in self._values['general']: - return self._values['general']['customXffHeaders'] - - @property - def allowed_response_codes(self): - if 'allowed_response_codes' in self._values: - if self._values['allowed_response_codes'] is None: - return None - return self._values['allowed_response_codes'] - if 'general' in self._values: - if self._values['general'] is None: - return None - if 'allowedResponseCodes' in self._values['general']: - return self._values['general']['allowedResponseCodes'] - - @property - def learning_mode(self): - if 'policy_builder' in self._values: - if self._values['policy_builder'] is None: - return None - if 'learningMode' in self._values['policy_builder']: - return self._values['policy_builder']['learningMode'] - - @property - def disallowed_locations(self): - if 'geolocation_enforcement' in self._values: - if self._values['geolocation_enforcement'] is None: - return None - return self._values['geolocation_enforcement']['disallowedLocations'] - - @property - def disallowed_geolocations(self): - if 'disallowed_geolocations' in self._values: - if self._values['disallowed_geolocations'] is None: - return None - return self._values['disallowed_geolocations'] - - @property - def csrf_protection_enabled(self): - if 'csrf_protection' in self._values: - return flatten_boolean(self._values['csrf_protection']['enabled']) - - @property - def csrf_protection_ssl_only(self): - if 'csrf_protection' in self._values: - if 'sslOnly' in self._values['csrf_protection']: - return flatten_boolean(self._values['csrf_protection']['sslOnly']) - - @property - def csrf_protection_expiration_time_in_seconds(self): - if 'csrf_protection' in self._values: - if 'expirationTimeInSeconds' in self._values['csrf_protection']: - if self._values['csrf_protection']['expirationTimeInSeconds'] is None: - return None - if self._values['csrf_protection']['expirationTimeInSeconds'] == 'disabled': - return 'disabled' - return int(self._values['csrf_protection']['expirationTimeInSeconds']) - - def format_csrf_collection(self, items): - result = list() - key_map = { - 'requiredParameters': 'csrf_url_required_parameters', - 'url': 'csrf_url', - 'method': 'csrf_url_method', - 'enforcementAction': 'csrf_url_enforcement_action', - 'id': 'csrf_url_id', - 'wildcardOrder': 'csrf_url_wildcard_order', - 'parametersList': 'csrf_url_parameters_list' - } - for item in items: - self._remove_internal_keywords(item) - item.pop('lastUpdateMicros') - output = self._morph_keys(key_map, item) - result.append(output) - return result - - @property - def csrf_urls(self): - if 'csrfUrls' in self._values: - if self._values['csrfUrls'] is None: - return None - return self._values['csrfUrls'] - if 'csrf-urls' in self._values: - if self._values['csrf-urls'] is None: - return None - return self.format_csrf_collection(self._values['csrf-urls']) - - @property - def protocol_independent(self): - return flatten_boolean(self._values['protocol_independent']) - - -# TODO include: web-scraping,ip-intelligence,session-tracking, -# TODO login-enforcement,data-guard,redirection-protection,vulnerability-assessment, parentPolicyReference - - -class AsmPolicyFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(AsmPolicyFactManager, self).__init__(**kwargs) - self.want = AsmPolicyFactParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(asm_policies=facts) - return result - - def _exec_module(self): - if 'asm' not in self.provisioned_modules: - return [] - manager = self.get_manager() - return manager._exec_module() - - def get_manager(self): - if self.version_is_less_than_13(): - return AsmPolicyFactManagerV12(**self.kwargs) - else: - return AsmPolicyFactManagerV13(**self.kwargs) - - def version_is_less_than_13(self): - version = tmos_version(self.client) - if LooseVersion(version) < LooseVersion('13.0.0'): - return True - else: - return False - - def read_facts(self): - results = [] - collection = self.increment_read() - for resource in collection: - params = AsmPolicyFactParameters(params=resource) - results.append(params) - return results - - def increment_read(self): - n = 0 - result = [] - while True: - items = self.read_collection_from_device(skip=n) - if not items: - break - result.extend(items) - n = n + 10 - return result - - -class AsmPolicyFactManagerV12(AsmPolicyFactManager): - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_collection_from_device(self, skip=0): - uri = "https://{0}:{1}/mgmt/tm/asm/policies".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - - to_expand = 'policy-builder,geolocation-enforcement,csrf-protection' - query = '?$top=10&$skip={0}&$expand={1}'.format(skip, to_expand) - - resp = self.client.api.get(uri + query) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - if 'items' not in response: - return None - return response['items'] - - -class AsmPolicyFactManagerV13(AsmPolicyFactManager): - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_collection_from_device(self, skip=0): - uri = "https://{0}:{1}/mgmt/tm/asm/policies".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - to_expand = 'general,signature-settings,header-settings,cookie-settings,antivirus,' \ - 'policy-builder,csrf-protection,csrf-urls' - query = '?$top=10&$skip={0}&$expand={1}'.format(skip, to_expand) - resp = self.client.api.get(uri + query) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - if 'items' not in response: - return None - - return response['items'] - - -class AsmServerTechnologyFactParameters(BaseParameters): - api_map = { - 'serverTechnologyName': 'server_technology_name', - 'serverTechnologyReferences': 'server_technology_references', - } - - returnables = [ - 'id', - 'server_technology_name', - 'server_technology_references', - ] - - -class AsmServerTechnologyFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(AsmServerTechnologyFactManager, self).__init__(**kwargs) - self.want = AsmServerTechnologyFactParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(asm_server_technologies=facts) - return result - - def _exec_module(self): - results = [] - if 'asm' not in self.provisioned_modules: - return results - if self.version_is_less_than_13(): - return results - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['server_technology_name']) - return results - - def version_is_less_than_13(self): - version = tmos_version(self.client) - if LooseVersion(version) < LooseVersion('13.0.0'): - return True - else: - return False - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = AsmServerTechnologyFactParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/asm/server-technologies".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class AsmSignatureSetsFactParameters(BaseParameters): - api_map = { - 'isUserDefined': 'is_user_defined', - 'assignToPolicyByDefault': 'assign_to_policy_by_default', - 'defaultAlarm': 'default_alarm', - 'defaultBlock': 'default_block', - 'defaultLearn': 'default_learn', - } - - returnables = [ - 'name', - 'id', - 'type', - 'category', - 'is_user_defined', - 'assign_to_policy_by_default', - 'default_alarm', - 'default_block', - 'default_learn', - ] - - @property - def is_user_defined(self): - return flatten_boolean(self._values['is_user_defined']) - - @property - def assign_to_policy_by_default(self): - return flatten_boolean(self._values['assign_to_policy_by_default']) - - @property - def default_alarm(self): - return flatten_boolean(self._values['default_alarm']) - - @property - def default_block(self): - return flatten_boolean(self._values['default_block']) - - @property - def default_learn(self): - return flatten_boolean(self._values['default_learn']) - -# TODO: add the following: filter, systems, signatureReferences - - -class AsmSignatureSetsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(AsmSignatureSetsFactManager, self).__init__(**kwargs) - self.want = AsmSignatureSetsFactParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(asm_signature_sets=facts) - return result - - def _exec_module(self): - results = [] - if 'asm' not in self.provisioned_modules: - return results - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['name']) - return results - - def read_facts(self): - results = [] - collection = self.increment_read() - for resource in collection: - params = AsmSignatureSetsFactParameters(params=resource) - results.append(params) - return results - - def increment_read(self): - n = 0 - result = [] - while True: - items = self.read_collection_from_device(skip=n) - if not items: - break - result.extend(items) - n = n + 5 - return result - - def read_collection_from_device(self, skip=0): - uri = "https://{0}:{1}/mgmt/tm/asm/signature-sets".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - query = '?$top=5&$skip={0}'.format(skip) - resp = self.client.api.get(uri + query) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - if 'items' not in response: - return None - - return response['items'] - - -class ClientSslProfilesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'alertTimeout': 'alert_timeout', - 'allowNonSsl': 'allow_non_ssl', - 'authenticateDepth': 'authenticate_depth', - 'authenticate': 'authenticate_frequency', - 'caFile': 'ca_file', - 'cacheSize': 'cache_size', - 'cacheTimeout': 'cache_timeout', - 'cert': 'certificate_file', - 'chain': 'chain_file', - 'crlFile': 'crl_file', - 'defaultsFrom': 'parent', - 'modSslMethods': 'modssl_methods', - 'peerCertMode': 'peer_certification_mode', - 'sniRequire': 'sni_require', - 'strictResume': 'strict_resume', - 'mode': 'profile_mode_enabled', - 'renegotiateMaxRecordDelay': 'renegotiation_maximum_record_delay', - 'renegotiatePeriod': 'renegotiation_period', - 'serverName': 'server_name', - 'sessionTicket': 'session_ticket', - 'sniDefault': 'sni_default', - 'uncleanShutdown': 'unclean_shutdown', - 'retainCertificate': 'retain_certificate', - 'secureRenegotiation': 'secure_renegotiation_mode', - 'handshakeTimeout': 'handshake_timeout', - 'certExtensionIncludes': 'forward_proxy_certificate_extension_include', - 'certLifespan': 'forward_proxy_certificate_lifespan', - 'certLookupByIpaddrPort': 'forward_proxy_lookup_by_ipaddr_port', - 'sslForwardProxy': 'forward_proxy_enabled', - 'proxyCaPassphrase': 'forward_proxy_ca_passphrase', - 'proxyCaCert': 'forward_proxy_ca_certificate_file', - 'proxyCaKey': 'forward_proxy_ca_key_file' - } - - returnables = [ - 'full_path', - 'name', - 'alert_timeout', - 'allow_non_ssl', - 'authenticate_depth', - 'authenticate_frequency', - 'ca_file', - 'cache_size', - 'cache_timeout', - 'certificate_file', - 'chain_file', - 'ciphers', - 'crl_file', - 'parent', - 'description', - 'modssl_methods', - 'peer_certification_mode', - 'sni_require', - 'sni_default', - 'strict_resume', - 'profile_mode_enabled', - 'renegotiation_maximum_record_delay', - 'renegotiation_period', - 'renegotiation', - 'server_name', - 'session_ticket', - 'unclean_shutdown', - 'retain_certificate', - 'secure_renegotiation_mode', - 'handshake_timeout', - 'forward_proxy_certificate_extension_include', - 'forward_proxy_certificate_lifespan', - 'forward_proxy_lookup_by_ipaddr_port', - 'forward_proxy_enabled', - 'forward_proxy_ca_passphrase', - 'forward_proxy_ca_certificate_file', - 'forward_proxy_ca_key_file' - ] - - @property - def alert_timeout(self): - if self._values['alert_timeout'] is None: - return None - if self._values['alert_timeout'] == 'indefinite': - return 0 - return int(self._values['alert_timeout']) - - @property - def renegotiation_maximum_record_delay(self): - if self._values['renegotiation_maximum_record_delay'] is None: - return None - if self._values['renegotiation_maximum_record_delay'] == 'indefinite': - return 0 - return int(self._values['renegotiation_maximum_record_delay']) - - @property - def renegotiation_period(self): - if self._values['renegotiation_period'] is None: - return None - if self._values['renegotiation_period'] == 'indefinite': - return 0 - return int(self._values['renegotiation_period']) - - @property - def handshake_timeout(self): - if self._values['handshake_timeout'] is None: - return None - if self._values['handshake_timeout'] == 'indefinite': - return 0 - return int(self._values['handshake_timeout']) - - @property - def allow_non_ssl(self): - if self._values['allow_non_ssl'] is None: - return None - if self._values['allow_non_ssl'] == 'disabled': - return 'no' - return 'yes' - - @property - def forward_proxy_enabled(self): - if self._values['forward_proxy_enabled'] is None: - return None - if self._values['forward_proxy_enabled'] == 'disabled': - return 'no' - return 'yes' - - @property - def renegotiation(self): - if self._values['renegotiation'] is None: - return None - if self._values['renegotiation'] == 'disabled': - return 'no' - return 'yes' - - @property - def forward_proxy_lookup_by_ipaddr_port(self): - if self._values['forward_proxy_lookup_by_ipaddr_port'] is None: - return None - if self._values['forward_proxy_lookup_by_ipaddr_port'] == 'disabled': - return 'no' - return 'yes' - - @property - def unclean_shutdown(self): - if self._values['unclean_shutdown'] is None: - return None - if self._values['unclean_shutdown'] == 'disabled': - return 'no' - return 'yes' - - @property - def session_ticket(self): - if self._values['session_ticket'] is None: - return None - if self._values['session_ticket'] == 'disabled': - return 'no' - return 'yes' - - @property - def retain_certificate(self): - if self._values['retain_certificate'] is None: - return None - if self._values['retain_certificate'] == 'true': - return 'yes' - return 'no' - - @property - def server_name(self): - if self._values['server_name'] in [None, 'none']: - return None - return self._values['server_name'] - - @property - def forward_proxy_ca_certificate_file(self): - if self._values['forward_proxy_ca_certificate_file'] in [None, 'none']: - return None - return self._values['forward_proxy_ca_certificate_file'] - - @property - def forward_proxy_ca_key_file(self): - if self._values['forward_proxy_ca_key_file'] in [None, 'none']: - return None - return self._values['forward_proxy_ca_key_file'] - - @property - def authenticate_frequency(self): - if self._values['authenticate_frequency'] is None: - return None - return self._values['authenticate_frequency'] - - @property - def ca_file(self): - if self._values['ca_file'] in [None, 'none']: - return None - return self._values['ca_file'] - - @property - def certificate_file(self): - if self._values['certificate_file'] in [None, 'none']: - return None - return self._values['certificate_file'] - - @property - def chain_file(self): - if self._values['chain_file'] in [None, 'none']: - return None - return self._values['chain_file'] - - @property - def crl_file(self): - if self._values['crl_file'] in [None, 'none']: - return None - return self._values['crl_file'] - - @property - def ciphers(self): - if self._values['ciphers'] in [None, 'none']: - return None - return self._values['ciphers'].split(' ') - - @property - def modssl_methods(self): - if self._values['modssl_methods'] is None: - return None - if self._values['modssl_methods'] == 'disabled': - return 'no' - return 'yes' - - @property - def strict_resume(self): - if self._values['strict_resume'] is None: - return None - if self._values['strict_resume'] == 'disabled': - return 'no' - return 'yes' - - @property - def profile_mode_enabled(self): - if self._values['profile_mode_enabled'] is None: - return None - if self._values['profile_mode_enabled'] == 'disabled': - return 'no' - return 'yes' - - @property - def sni_require(self): - if self._values['sni_require'] is None: - return None - if self._values['sni_require'] == 'false': - return 'no' - return 'yes' - - @property - def sni_default(self): - if self._values['sni_default'] is None: - return None - if self._values['sni_default'] == 'false': - return 'no' - return 'yes' - - -class ClientSslProfilesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(ClientSslProfilesFactManager, self).__init__(**kwargs) - self.want = ClientSslProfilesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(client_ssl_profiles=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = ClientSslProfilesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/client-ssl".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class DeviceGroupsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'autoSync': 'autosync_enabled', - 'asmSync': 'asm_sync_enabled', - 'devicesReference': 'devices', - 'fullLoadOnSync': 'full_load_on_sync', - 'incrementalConfigSyncSizeMax': 'incremental_config_sync_size_maximum', - 'networkFailover': 'network_failover_enabled' - } - - returnables = [ - 'full_path', - 'name', - 'autosync_enabled', - 'description', - 'devices', - 'full_load_on_sync', - 'incremental_config_sync_size_maximum', - 'network_failover_enabled', - 'type', - 'asm_sync_enabled' - ] - - @property - def network_failover_enabled(self): - if self._values['network_failover_enabled'] is None: - return None - if self._values['network_failover_enabled'] == 'enabled': - return 'yes' - return 'no' - - @property - def asm_sync_enabled(self): - if self._values['asm_sync_enabled'] is None: - return None - if self._values['asm_sync_enabled'] == 'disabled': - return 'no' - return 'yes' - - @property - def autosync_enabled(self): - if self._values['autosync_enabled'] is None: - return None - if self._values['autosync_enabled'] == 'disabled': - return 'no' - return 'yes' - - @property - def full_load_on_sync(self): - if self._values['full_load_on_sync'] is None: - return None - if self._values['full_load_on_sync'] == 'true': - return 'yes' - return 'no' - - @property - def devices(self): - if self._values['devices'] is None or 'items' not in self._values['devices']: - return None - result = [x['fullPath'] for x in self._values['devices']['items']] - result.sort() - return result - - -class DeviceGroupsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(DeviceGroupsFactManager, self).__init__(**kwargs) - self.want = DeviceGroupsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(device_groups=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = DeviceGroupsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/cm/device-group/?expandSubcollections=true".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class DevicesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'activeModules': 'active_modules', - 'baseMac': 'base_mac_address', - 'chassisId': 'chassis_id', - 'chassisType': 'chassis_type', - 'configsyncIp': 'configsync_address', - 'failoverState': 'failover_state', - 'managementIp': 'management_address', - 'marketingName': 'marketing_name', - 'multicastIp': 'multicast_address', - 'optionalModules': 'optional_modules', - 'platformId': 'platform_id', - 'mirrorIp': 'primary_mirror_address', - 'mirrorSecondaryIp': 'secondary_mirror_address', - 'version': 'software_version', - 'timeLimitedModules': 'timelimited_modules', - 'timeZone': 'timezone', - 'unicastAddress': 'unicast_addresses', - 'selfDevice': 'self' - } - - returnables = [ - 'full_path', - 'name', - 'active_modules', - 'base_mac_address', - 'build', - 'chassis_id', - 'chassis_type', - 'comment', - 'configsync_address', - 'contact', - 'description', - 'edition', - 'failover_state', - 'hostname', - 'location', - 'management_address', - 'marketing_name', - 'multicast_address', - 'optional_modules', - 'platform_id', - 'primary_mirror_address', - 'product', - 'secondary_mirror_address', - 'self', - 'software_version', - 'timelimited_modules', - 'timezone', - 'unicast_addresses', - ] - - @property - def active_modules(self): - if self._values['active_modules'] is None: - return None - result = [] - for x in self._values['active_modules']: - parts = x.split('|') - result += parts[2:] - return list(set(result)) - - @property - def self(self): - result = flatten_boolean(self._values['self']) - return result - - @property - def configsync_address(self): - if self._values['configsync_address'] in [None, 'none']: - return None - return self._values['configsync_address'] - - @property - def primary_mirror_address(self): - if self._values['primary_mirror_address'] in [None, 'any6']: - return None - return self._values['primary_mirror_address'] - - @property - def secondary_mirror_address(self): - if self._values['secondary_mirror_address'] in [None, 'any6']: - return None - return self._values['secondary_mirror_address'] - - @property - def unicast_addresses(self): - if self._values['unicast_addresses'] is None: - return None - result = [] - - for addr in self._values['unicast_addresses']: - tmp = {} - for key in ['effectiveIp', 'effectivePort', 'ip', 'port']: - if key in addr: - renamed_key = self.convert(key) - tmp[renamed_key] = addr.get(key, None) - if tmp: - result.append(tmp) - if result: - return result - - def convert(self, name): - s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) - return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() - - -class DevicesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(DevicesFactManager, self).__init__(**kwargs) - self.want = DevicesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(devices=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = DevicesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/cm/device".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class ExternalMonitorsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'adaptiveDivergenceType': 'adaptive_divergence_type', - 'adaptiveDivergenceValue': 'adaptive_divergence_value', - 'adaptiveLimit': 'adaptive_limit', - 'adaptiveSamplingTimespan': 'adaptive_sampling_timespan', - 'manualResume': 'manual_resume', - 'timeUntilUp': 'time_until_up', - 'upInterval': 'up_interval', - 'run': 'external_program', - 'apiRawValues': 'variables', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'args', - 'destination', - 'external_program', - 'interval', - 'manual_resume', - 'time_until_up', - 'timeout', - 'up_interval', - 'variables', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def manual_resume(self): - return flatten_boolean(self._values['manual_resume']) - - @property - def variables(self): - if self._values['variables'] is None: - return None - result = {} - for k, v in iteritems(self._values['variables']): - k = k.replace('userDefined ', '').strip() - result[k] = v - return result - - -class ExternalMonitorsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(ExternalMonitorsFactManager, self).__init__(**kwargs) - self.want = ExternalMonitorsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(external_monitors=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = ExternalMonitorsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/external".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class FastHttpProfilesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'clientCloseTimeout': 'client_close_timeout', - 'connpoolIdleTimeoutOverride': 'oneconnect_idle_timeout_override', - 'connpoolMaxReuse': 'oneconnect_maximum_reuse', - 'connpoolMaxSize': 'oneconnect_maximum_pool_size', - 'connpoolMinSize': 'oneconnect_minimum_pool_size', - 'connpoolReplenish': 'oneconnect_replenish', - 'connpoolStep': 'oneconnect_ramp_up_increment', - 'defaultsFrom': 'parent', - 'forceHttp_10Response': 'force_http_1_0_response', - 'headerInsert': 'request_header_insert', - 'http_11CloseWorkarounds': 'http_1_1_close_workarounds', - 'idleTimeout': 'idle_timeout', - 'insertXforwardedFor': 'insert_x_forwarded_for', - 'maxHeaderSize': 'maximum_header_size', - 'maxRequests': 'maximum_requests', - 'mssOverride': 'maximum_segment_size_override', - 'receiveWindowSize': 'receive_window_size', - 'resetOnTimeout': 'reset_on_timeout', - 'serverCloseTimeout': 'server_close_timeout', - 'serverSack': 'server_sack', - 'serverTimestamp': 'server_timestamp', - 'uncleanShutdown': 'unclean_shutdown' - } - - returnables = [ - 'full_path', - 'name', - 'client_close_timeout', - 'oneconnect_idle_timeout_override', - 'oneconnect_maximum_reuse', - 'oneconnect_maximum_pool_size', - 'oneconnect_minimum_pool_size', - 'oneconnect_replenish', - 'oneconnect_ramp_up_increment', - 'parent', - 'description', - 'force_http_1_0_response', - 'request_header_insert', - 'http_1_1_close_workarounds', - 'idle_timeout', - 'insert_x_forwarded_for', - 'maximum_header_size', - 'maximum_requests', - 'maximum_segment_size_override', - 'receive_window_size', - 'reset_on_timeout', - 'server_close_timeout', - 'server_sack', - 'server_timestamp', - 'unclean_shutdown' - ] - - @property - def request_header_insert(self): - if self._values['request_header_insert'] in [None, 'none']: - return None - return self._values['request_header_insert'] - - @property - def server_timestamp(self): - return flatten_boolean(self._values['server_timestamp']) - - @property - def server_sack(self): - return flatten_boolean(self._values['server_sack']) - - @property - def reset_on_timeout(self): - return flatten_boolean(self._values['reset_on_timeout']) - - @property - def insert_x_forwarded_for(self): - return flatten_boolean(self._values['insert_x_forwarded_for']) - - @property - def http_1_1_close_workarounds(self): - return flatten_boolean(self._values['http_1_1_close_workarounds']) - - @property - def force_http_1_0_response(self): - return flatten_boolean(self._values['force_http_1_0_response']) - - @property - def oneconnect_replenish(self): - return flatten_boolean(self._values['oneconnect_replenish']) - - @property - def idle_timeout(self): - if self._values['idle_timeout'] is None: - return None - elif self._values['idle_timeout'] == 'immediate': - return 0 - elif self._values['idle_timeout'] == 'indefinite': - return 4294967295 - return int(self._values['idle_timeout']) - - -class FastHttpProfilesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(FastHttpProfilesFactManager, self).__init__(**kwargs) - self.want = FastHttpProfilesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(fasthttp_profiles=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = FastHttpProfilesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/fasthttp".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class FastL4ProfilesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'clientTimeout': 'client_timeout', - 'defaultsFrom': 'parent', - 'explicitFlowMigration': 'explicit_flow_migration', - 'hardwareSynCookie': 'hardware_syn_cookie', - 'idleTimeout': 'idle_timeout', - 'ipDfMode': 'dont_fragment_flag', - 'ipTosToClient': 'ip_tos_to_client', - 'ipTosToServer': 'ip_tos_to_server', - 'ipTtlMode': 'ttl_mode', - 'ipTtlV4': 'ttl_v4', - 'ipTtlV6': 'ttl_v6', - 'keepAliveInterval': 'keep_alive_interval', - 'lateBinding': 'late_binding', - 'linkQosToClient': 'link_qos_to_client', - 'linkQosToServer': 'link_qos_to_server', - 'looseClose': 'loose_close', - 'looseInitialization': 'loose_init', - 'mssOverride': 'mss_override', - 'priorityToClient': 'priority_to_client', - 'priorityToServer': 'priority_to_server', - 'pvaAcceleration': 'pva_acceleration', - 'pvaDynamicClientPackets': 'pva_dynamic_client_packets', - 'pvaDynamicServerPackets': 'pva_dynamic_server_packets', - 'pvaFlowAging': 'pva_flow_aging', - 'pvaFlowEvict': 'pva_flow_evict', - 'pvaOffloadDynamic': 'pva_offload_dynamic', - 'pvaOffloadState': 'pva_offload_state', - 'reassembleFragments': 'reassemble_fragments', - 'receiveWindowSize': 'receive_window', - 'resetOnTimeout': 'reset_on_timeout', - 'rttFromClient': 'rtt_from_client', - 'rttFromServer': 'rtt_from_server', - 'serverSack': 'server_sack', - 'serverTimestamp': 'server_timestamp', - 'softwareSynCookie': 'software_syn_cookie', - 'synCookieEnable': 'syn_cookie_enabled', - 'synCookieMss': 'syn_cookie_mss', - 'synCookieWhitelist': 'syn_cookie_whitelist', - 'tcpCloseTimeout': 'tcp_close_timeout', - 'tcpGenerateIsn': 'generate_init_seq_number', - 'tcpHandshakeTimeout': 'tcp_handshake_timeout', - 'tcpStripSack': 'strip_sack', - 'tcpTimeWaitTimeout': 'tcp_time_wait_timeout', - 'tcpTimestampMode': 'tcp_timestamp_mode', - 'tcpWscaleMode': 'tcp_window_scale_mode', - 'timeoutRecovery': 'timeout_recovery', - } - - returnables = [ - 'full_path', - 'name', - 'client_timeout', - 'parent', - 'description', - 'explicit_flow_migration', - 'hardware_syn_cookie', - 'idle_timeout', - 'dont_fragment_flag', - 'ip_tos_to_client', - 'ip_tos_to_server', - 'ttl_mode', - 'ttl_v4', - 'ttl_v6', - 'keep_alive_interval', - 'late_binding', - 'link_qos_to_client', - 'link_qos_to_server', - 'loose_close', - 'loose_init', - 'mss_override', # Maximum Segment Size Override - 'priority_to_client', - 'priority_to_server', - 'pva_acceleration', - 'pva_dynamic_client_packets', - 'pva_dynamic_server_packets', - 'pva_flow_aging', - 'pva_flow_evict', - 'pva_offload_dynamic', - 'pva_offload_state', - 'reassemble_fragments', - 'receive_window', - 'reset_on_timeout', - 'rtt_from_client', - 'rtt_from_server', - 'server_sack', - 'server_timestamp', - 'software_syn_cookie', - 'syn_cookie_enabled', - 'syn_cookie_mss', - 'syn_cookie_whitelist', - 'tcp_close_timeout', - 'generate_init_seq_number', - 'tcp_handshake_timeout', - 'strip_sack', - 'tcp_time_wait_timeout', - 'tcp_timestamp_mode', - 'tcp_window_scale_mode', - 'timeout_recovery', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def strip_sack(self): - return flatten_boolean(self._values['strip_sack']) - - @property - def generate_init_seq_number(self): - return flatten_boolean(self._values['generate_init_seq_number']) - - @property - def syn_cookie_whitelist(self): - return flatten_boolean(self._values['syn_cookie_whitelist']) - - @property - def syn_cookie_enabled(self): - return flatten_boolean(self._values['syn_cookie_enabled']) - - @property - def software_syn_cookie(self): - return flatten_boolean(self._values['software_syn_cookie']) - - @property - def server_timestamp(self): - return flatten_boolean(self._values['server_timestamp']) - - @property - def server_sack(self): - return flatten_boolean(self._values['server_sack']) - - @property - def rtt_from_server(self): - return flatten_boolean(self._values['rtt_from_server']) - - @property - def rtt_from_client(self): - return flatten_boolean(self._values['rtt_from_client']) - - @property - def reset_on_timeout(self): - return flatten_boolean(self._values['reset_on_timeout']) - - @property - def explicit_flow_migration(self): - return flatten_boolean(self._values['explicit_flow_migration']) - - @property - def reassemble_fragments(self): - return flatten_boolean(self._values['reassemble_fragments']) - - @property - def pva_flow_aging(self): - return flatten_boolean(self._values['pva_flow_aging']) - - @property - def pva_flow_evict(self): - return flatten_boolean(self._values['pva_flow_evict']) - - @property - def pva_offload_dynamic(self): - return flatten_boolean(self._values['pva_offload_dynamic']) - - @property - def hardware_syn_cookie(self): - return flatten_boolean(self._values['hardware_syn_cookie']) - - @property - def loose_close(self): - return flatten_boolean(self._values['loose_close']) - - @property - def loose_init(self): - return flatten_boolean(self._values['loose_init']) - - @property - def late_binding(self): - return flatten_boolean(self._values['late_binding']) - - @property - def tcp_handshake_timeout(self): - if self._values['tcp_handshake_timeout'] is None: - return None - elif self._values['tcp_handshake_timeout'] == 'immediate': - return 0 - elif self._values['tcp_handshake_timeout'] == 'indefinite': - return 4294967295 - return int(self._values['tcp_handshake_timeout']) - - @property - def idle_timeout(self): - if self._values['idle_timeout'] is None: - return None - elif self._values['idle_timeout'] == 'immediate': - return 0 - elif self._values['idle_timeout'] == 'indefinite': - return 4294967295 - return int(self._values['idle_timeout']) - - @property - def tcp_close_timeout(self): - if self._values['tcp_close_timeout'] is None: - return None - elif self._values['tcp_close_timeout'] == 'immediate': - return 0 - elif self._values['tcp_close_timeout'] == 'indefinite': - return 4294967295 - return int(self._values['tcp_close_timeout']) - - @property - def keep_alive_interval(self): - if self._values['keep_alive_interval'] is None: - return None - elif self._values['keep_alive_interval'] == 'disabled': - return 0 - return int(self._values['keep_alive_interval']) - - @property - def ip_tos_to_client(self): - if self._values['ip_tos_to_client'] is None: - return None - try: - return int(self._values['ip_tos_to_client']) - except ValueError: - return self._values['ip_tos_to_client'] - - @property - def ip_tos_to_server(self): - if self._values['ip_tos_to_server'] is None: - return None - try: - return int(self._values['ip_tos_to_server']) - except ValueError: - return self._values['ip_tos_to_server'] - - @property - def link_qos_to_client(self): - if self._values['link_qos_to_client'] is None: - return None - try: - return int(self._values['link_qos_to_client']) - except ValueError: - return self._values['link_qos_to_client'] - - @property - def link_qos_to_server(self): - if self._values['link_qos_to_server'] is None: - return None - try: - return int(self._values['link_qos_to_server']) - except ValueError: - return self._values['link_qos_to_server'] - - @property - def priority_to_client(self): - if self._values['priority_to_client'] is None: - return None - try: - return int(self._values['priority_to_client']) - except ValueError: - return self._values['priority_to_client'] - - @property - def priority_to_server(self): - if self._values['priority_to_server'] is None: - return None - try: - return int(self._values['priority_to_server']) - except ValueError: - return self._values['priority_to_server'] - - -class FastL4ProfilesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(FastL4ProfilesFactManager, self).__init__(**kwargs) - self.want = FastL4ProfilesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(fastl4_profiles=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = FastL4ProfilesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/fastl4".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GatewayIcmpMonitorsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'adaptiveDivergenceType': 'adaptive_divergence_type', - 'adaptiveDivergenceValue': 'adaptive_divergence_value', - 'adaptiveLimit': 'adaptive_limit', - 'adaptiveSamplingTimespan': 'adaptive_sampling_timespan', - 'manualResume': 'manual_resume', - 'timeUntilUp': 'time_until_up', - 'upInterval': 'up_interval', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'adaptive', - 'adaptive_divergence_type', - 'adaptive_divergence_value', - 'adaptive_limit', - 'adaptive_sampling_timespan', - 'destination', - 'interval', - 'manual_resume', - 'time_until_up', - 'timeout', - 'transparent', - 'up_interval', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def transparent(self): - return flatten_boolean(self._values['transparent']) - - @property - def manual_resume(self): - return flatten_boolean(self._values['manual_resume']) - - @property - def adaptive(self): - return flatten_boolean(self._values['adaptive']) - - -class GatewayIcmpMonitorsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GatewayIcmpMonitorsFactManager, self).__init__(**kwargs) - self.want = GatewayIcmpMonitorsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gateway_icmp_monitors=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GatewayIcmpMonitorsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/gateway-icmp".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmXPoolsParameters(BaseParameters): - api_map = { - 'alternateMode': 'alternate_mode', - 'dynamicRatio': 'dynamic_ratio', - 'fallbackMode': 'fallback_mode', - 'fullPath': 'full_path', - 'loadBalancingMode': 'load_balancing_mode', - 'manualResume': 'manual_resume', - 'maxAnswersReturned': 'max_answers_returned', - 'qosHitRatio': 'qos_hit_ratio', - 'qosHops': 'qos_hops', - 'qosKilobytesSecond': 'qos_kilobytes_second', - 'qosLcs': 'qos_lcs', - 'qosPacketRate': 'qos_packet_rate', - 'qosRtt': 'qos_rtt', - 'qosTopology': 'qos_topology', - 'qosVsCapacity': 'qos_vs_capacity', - 'qosVsScore': 'qos_vs_score', - 'verifyMemberAvailability': 'verify_member_availability', - 'membersReference': 'members' - } - - returnables = [ - 'alternate_mode', - 'dynamic_ratio', - 'enabled', - 'disabled', - 'fallback_mode', - 'full_path', - 'load_balancing_mode', - 'manual_resume', - 'max_answers_returned', - 'members', - 'name', - 'partition', - 'qos_hit_ratio', - 'qos_hops', - 'qos_kilobytes_second', - 'qos_lcs', - 'qos_packet_rate', - 'qos_rtt', - 'qos_topology', - 'qos_vs_capacity', - 'qos_vs_score', - 'ttl', - 'verify_member_availability', - ] - - @property - def verify_member_availability(self): - return flatten_boolean(self._values['verify_member_availability']) - - @property - def dynamic_ratio(self): - return flatten_boolean(self._values['dynamic_ratio']) - - @property - def max_answers_returned(self): - if self._values['max_answers_returned'] is None: - return None - return int(self._values['max_answers_returned']) - - @property - def members(self): - result = [] - if self._values['members'] is None or 'items' not in self._values['members']: - return result - for item in self._values['members']['items']: - self._remove_internal_keywords(item) - if 'disabled' in item: - item['disabled'] = flatten_boolean(item['disabled']) - item['enabled'] = flatten_boolean(not item['disabled']) - if 'enabled' in item: - item['enabled'] = flatten_boolean(item['enabled']) - item['disabled'] = flatten_boolean(not item['enabled']) - if 'fullPath' in item: - item['full_path'] = item.pop('fullPath') - if 'memberOrder' in item: - item['member_order'] = int(item.pop('memberOrder')) - # Cast some attributes to integer - for x in ['order', 'preference', 'ratio', 'service']: - if x in item: - item[x] = int(item[x]) - result.append(item) - return result - - @property - def qos_hit_ratio(self): - if self._values['qos_hit_ratio'] is None: - return None - return int(self._values['qos_hit_ratio']) - - @property - def qos_hops(self): - if self._values['qos_hops'] is None: - return None - return int(self._values['qos_hops']) - - @property - def qos_kilobytes_second(self): - if self._values['qos_kilobytes_second'] is None: - return None - return int(self._values['qos_kilobytes_second']) - - @property - def qos_lcs(self): - if self._values['qos_lcs'] is None: - return None - return int(self._values['qos_lcs']) - - @property - def qos_packet_rate(self): - if self._values['qos_packet_rate'] is None: - return None - return int(self._values['qos_packet_rate']) - - @property - def qos_rtt(self): - if self._values['qos_rtt'] is None: - return None - return int(self._values['qos_rtt']) - - @property - def qos_topology(self): - if self._values['qos_topology'] is None: - return None - return int(self._values['qos_topology']) - - @property - def qos_vs_capacity(self): - if self._values['qos_vs_capacity'] is None: - return None - return int(self._values['qos_vs_capacity']) - - @property - def qos_vs_score(self): - if self._values['qos_vs_score'] is None: - return None - return int(self._values['qos_vs_score']) - - @property - def availability_state(self): - if self._values['stats'] is None: - return None - try: - result = self._values['stats']['status']['availabilityState'] - return result['description'] - except AttributeError: - return None - - @property - def enabled_state(self): - if self._values['stats'] is None: - return None - try: - result = self._values['stats']['status']['enabledState'] - return result['description'] - except AttributeError: - return None - - @property - def availability_status(self): - # This fact is a combination of the availability_state and enabled_state - # - # The purpose of the fact is to give a higher-level view of the availability - # of the pool, that can be used in playbooks. If you need further detail, - # consider using the following facts together. - # - # - availability_state - # - enabled_state - # - if self.enabled_state == 'enabled': - if self.availability_state == 'offline': - return 'red' - elif self.availability_state == 'available': - return 'green' - elif self.availability_state == 'unknown': - return 'blue' - else: - return 'none' - else: - # disabled - return 'black' - - @property - def manual_resume(self): - return flatten_boolean(self._values['manual_resume']) - - -class GtmAPoolsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmAPoolsFactManager, self).__init__(**kwargs) - self.want = GtmXPoolsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_a_pools=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXPoolsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/pool/a".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - query = "?expandSubcollections=true" - resp = self.client.api.get(uri + query) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmAaaaPoolsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmAaaaPoolsFactManager, self).__init__(**kwargs) - self.want = GtmXPoolsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_aaaa_pools=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXPoolsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/pool/aaaa".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - query = "?expandSubcollections=true" - resp = self.client.api.get(uri + query) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmCnamePoolsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmCnamePoolsFactManager, self).__init__(**kwargs) - self.want = GtmXPoolsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_cname_pools=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXPoolsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/pool/cname".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - query = "?expandSubcollections=true" - resp = self.client.api.get(uri + query) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmMxPoolsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmMxPoolsFactManager, self).__init__(**kwargs) - self.want = GtmXPoolsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_mx_pools=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXPoolsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/pool/mx".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - query = "?expandSubcollections=true" - resp = self.client.api.get(uri + query) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmNaptrPoolsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmNaptrPoolsFactManager, self).__init__(**kwargs) - self.want = GtmXPoolsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_naptr_pools=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXPoolsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/pool/naptr".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - query = "?expandSubcollections=true" - resp = self.client.api.get(uri + query) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmSrvPoolsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmSrvPoolsFactManager, self).__init__(**kwargs) - self.want = GtmXPoolsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_srv_pools=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXPoolsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/pool/srv".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - query = "?expandSubcollections=true" - resp = self.client.api.get(uri + query) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmServersParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'exposeRouteDomains': 'expose_route_domains', - 'iqAllowPath': 'iq_allow_path', - 'iqAllowServiceCheck': 'iq_allow_service_check', - 'iqAllowSnmp': 'iq_allow_snmp', - 'limitCpuUsage': 'limit_cpu_usage', - 'limitCpuUsageStatus': 'limit_cpu_usage_status', - 'limitMaxBps': 'limit_max_bps', - 'limitMaxBpsStatus': 'limit_max_bps_status', - 'limitMaxConnections': 'limit_max_connections', - 'limitMaxConnectionsStatus': 'limit_max_connections_status', - 'limitMaxPps': 'limit_max_pps', - 'limitMaxPpsStatus': 'limit_max_pps_status', - 'limitMemAvail': 'limit_mem_available', - 'limitMemAvailStatus': 'limit_mem_available_status', - 'linkDiscovery': 'link_discovery', - 'proberFallback': 'prober_fallback', - 'proberPreference': 'prober_preference', - 'virtualServerDiscovery': 'virtual_server_discovery', - 'devicesReference': 'devices', - 'virtualServersReference': 'virtual_servers', - 'monitor': 'monitors', - } - - returnables = [ - 'datacenter', - 'enabled', - 'disabled', - 'expose_route_domains', - 'iq_allow_path', - 'full_path', - 'iq_allow_service_check', - 'iq_allow_snmp', - 'limit_cpu_usage', - 'limit_cpu_usage_status', - 'limit_max_bps', - 'limit_max_bps_status', - 'limit_max_connections', - 'limit_max_connections_status', - 'limit_max_pps', - 'limit_max_pps_status', - 'limit_mem_available', - 'limit_mem_available_status', - 'link_discovery', - 'monitors', - 'monitor_type', - 'name', - 'product', - 'prober_fallback', - 'prober_preference', - 'virtual_server_discovery', - 'addresses', - 'devices', - 'virtual_servers', - ] - - @property - def monitors(self): - if self._values['monitors'] is None: - return [] - try: - result = re.findall(r'/\w+/[^\s}]+', self._values['monitors']) - return result - except Exception: - return [self._values['monitors']] - - @property - def monitor_type(self): - if self._values['monitors'] is None: - return None - pattern = r'min\s+\d+\s+of' - matches = re.search(pattern, self._values['monitors']) - if matches: - return 'm_of_n' - else: - return 'and_list' - - @property - def limit_mem_available_status(self): - return flatten_boolean(self._values['limit_mem_available_status']) - - @property - def limit_max_pps_status(self): - return flatten_boolean(self._values['limit_max_pps_status']) - - @property - def limit_max_connections_status(self): - return flatten_boolean(self._values['limit_max_connections_status']) - - @property - def limit_max_bps_status(self): - return flatten_boolean(self._values['limit_max_bps_status']) - - @property - def limit_cpu_usage_status(self): - return flatten_boolean(self._values['limit_cpu_usage_status']) - - @property - def iq_allow_service_check(self): - return flatten_boolean(self._values['iq_allow_service_check']) - - @property - def iq_allow_snmp(self): - return flatten_boolean(self._values['iq_allow_snmp']) - - @property - def expose_route_domains(self): - return flatten_boolean(self._values['expose_route_domains']) - - @property - def iq_allow_path(self): - return flatten_boolean(self._values['iq_allow_path']) - - @property - def product(self): - if self._values['product'] is None: - return None - if self._values['product'] in ['single-bigip', 'redundant-bigip']: - return 'bigip' - return self._values['product'] - - @property - def devices(self): - result = [] - if self._values['devices'] is None or 'items' not in self._values['devices']: - return result - for item in self._values['devices']['items']: - self._remove_internal_keywords(item) - if 'fullPath' in item: - item['full_path'] = item.pop('fullPath') - result.append(item) - return result - - @property - def virtual_servers(self): - result = [] - if self._values['virtual_servers'] is None or 'items' not in self._values['virtual_servers']: - return result - for item in self._values['virtual_servers']['items']: - self._remove_internal_keywords(item) - if 'disabled' in item: - if item['disabled'] in BOOLEANS_TRUE: - item['disabled'] = flatten_boolean(item['disabled']) - item['enabled'] = flatten_boolean(not item['disabled']) - if 'enabled' in item: - if item['enabled'] in BOOLEANS_TRUE: - item['enabled'] = flatten_boolean(item['enabled']) - item['disabled'] = flatten_boolean(not item['enabled']) - if 'fullPath' in item: - item['full_path'] = item.pop('fullPath') - if 'limitMaxBps' in item: - item['limit_max_bps'] = int(item.pop('limitMaxBps')) - if 'limitMaxBpsStatus' in item: - item['limit_max_bps_status'] = item.pop('limitMaxBpsStatus') - if 'limitMaxConnections' in item: - item['limit_max_connections'] = int(item.pop('limitMaxConnections')) - if 'limitMaxConnectionsStatus' in item: - item['limit_max_connections_status'] = item.pop('limitMaxConnectionsStatus') - if 'limitMaxPps' in item: - item['limit_max_pps'] = int(item.pop('limitMaxPps')) - if 'limitMaxPpsStatus' in item: - item['limit_max_pps_status'] = item.pop('limitMaxPpsStatus') - if 'translationAddress' in item: - item['translation_address'] = item.pop('translationAddress') - if 'translationPort' in item: - item['translation_port'] = int(item.pop('translationPort')) - result.append(item) - return result - - @property - def limit_cpu_usage(self): - if self._values['limit_cpu_usage'] is None: - return None - return int(self._values['limit_cpu_usage']) - - @property - def limit_max_bps(self): - if self._values['limit_max_bps'] is None: - return None - return int(self._values['limit_max_bps']) - - @property - def limit_max_connections(self): - if self._values['limit_max_connections'] is None: - return None - return int(self._values['limit_max_connections']) - - @property - def limit_max_pps(self): - if self._values['limit_max_pps'] is None: - return None - return int(self._values['limit_max_pps']) - - @property - def limit_mem_available(self): - if self._values['limit_mem_available'] is None: - return None - return int(self._values['limit_mem_available']) - - -class GtmServersFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmServersFactManager, self).__init__(**kwargs) - self.want = GtmServersParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_servers=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmServersParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/server".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmXWideIpsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'failureRcode': 'failure_rcode', - 'failureRcodeResponse': 'failure_rcode_response', - 'failureRcodeTtl': 'failure_rcode_ttl', - 'lastResortPool': 'last_resort_pool', - 'minimalResponse': 'minimal_response', - 'persistCidrIpv4': 'persist_cidr_ipv4', - 'persistCidrIpv6': 'persist_cidr_ipv6', - 'poolLbMode': 'pool_lb_mode', - 'ttlPersistence': 'ttl_persistence' - } - - returnables = [ - 'full_path', - 'description', - 'enabled', - 'disabled', - 'failure_rcode', - 'failure_rcode_response', - 'failure_rcode_ttl', - 'last_resort_pool', - 'minimal_response', - 'name', - 'persist_cidr_ipv4', - 'persist_cidr_ipv6', - 'pool_lb_mode', - 'ttl_persistence', - 'pools', - ] - - @property - def pools(self): - result = [] - if self._values['pools'] is None: - return [] - for pool in self._values['pools']: - del pool['nameReference'] - for x in ['order', 'ratio']: - if x in pool: - pool[x] = int(pool[x]) - result.append(pool) - return result - - @property - def failure_rcode_response(self): - return flatten_boolean(self._values['failure_rcode_response']) - - @property - def failure_rcode_ttl(self): - if self._values['failure_rcode_ttl'] is None: - return None - return int(self._values['failure_rcode_ttl']) - - @property - def persist_cidr_ipv4(self): - if self._values['persist_cidr_ipv4'] is None: - return None - return int(self._values['persist_cidr_ipv4']) - - @property - def persist_cidr_ipv6(self): - if self._values['persist_cidr_ipv6'] is None: - return None - return int(self._values['persist_cidr_ipv6']) - - @property - def ttl_persistence(self): - if self._values['ttl_persistence'] is None: - return None - return int(self._values['ttl_persistence']) - - -class GtmAWideIpsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmAWideIpsFactManager, self).__init__(**kwargs) - self.want = GtmXWideIpsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_a_wide_ips=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXWideIpsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/wideip/a".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmAaaaWideIpsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmAaaaWideIpsFactManager, self).__init__(**kwargs) - self.want = GtmXWideIpsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_aaaa_wide_ips=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXWideIpsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/wideip/aaaa".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmCnameWideIpsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmCnameWideIpsFactManager, self).__init__(**kwargs) - self.want = GtmXWideIpsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_cname_wide_ips=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXWideIpsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/wideip/cname".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmMxWideIpsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmMxWideIpsFactManager, self).__init__(**kwargs) - self.want = GtmXWideIpsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_mx_wide_ips=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXWideIpsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/wideip/mx".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmNaptrWideIpsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmNaptrWideIpsFactManager, self).__init__(**kwargs) - self.want = GtmXWideIpsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_naptr_wide_ips=facts) - return result - - def _exec_module(self): - results = [] - if 'gtm' not in self.provisioned_modules: - return [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXWideIpsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/wideip/naptr".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class GtmSrvWideIpsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(GtmSrvWideIpsFactManager, self).__init__(**kwargs) - self.want = GtmXWideIpsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(gtm_srv_wide_ips=facts) - return result - - def _exec_module(self): - if 'gtm' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = GtmXWideIpsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/gtm/wideip/srv".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class HttpMonitorsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'adaptiveDivergenceType': 'adaptive_divergence_type', - 'adaptiveDivergenceValue': 'adaptive_divergence_value', - 'adaptiveLimit': 'adaptive_limit', - 'adaptiveSamplingTimespan': 'adaptive_sampling_timespan', - 'ipDscp': 'ip_dscp', - 'manualResume': 'manual_resume', - 'recv': 'receive_string', - 'recvDisable': 'receive_disable_string', - 'send': 'send_string', - 'timeUntilUp': 'time_until_up', - 'upInterval': 'up_interval', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'adaptive', - 'adaptive_divergence_type', - 'adaptive_divergence_value', - 'adaptive_limit', - 'adaptive_sampling_timespan', - 'destination', - 'interval', - 'ip_dscp', - 'manual_resume', - 'receive_string', - 'receive_disable_string', - 'reverse', - 'send_string', - 'time_until_up', - 'timeout', - 'transparent', - 'up_interval', - 'username', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def transparent(self): - return flatten_boolean(self._values['transparent']) - - @property - def reverse(self): - return flatten_boolean(self._values['reverse']) - - @property - def manual_resume(self): - return flatten_boolean(self._values['manual_resume']) - - @property - def adaptive(self): - return flatten_boolean(self._values['adaptive']) - - -class HttpMonitorsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(HttpMonitorsFactManager, self).__init__(**kwargs) - self.want = HttpMonitorsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(http_monitors=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = HttpMonitorsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/http".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = response['items'] - return result - - -class HttpsMonitorsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'adaptiveDivergenceType': 'adaptive_divergence_type', - 'adaptiveDivergenceValue': 'adaptive_divergence_value', - 'adaptiveLimit': 'adaptive_limit', - 'adaptiveSamplingTimespan': 'adaptive_sampling_timespan', - 'ipDscp': 'ip_dscp', - 'manualResume': 'manual_resume', - 'recv': 'receive_string', - 'recvDisable': 'receive_disable_string', - 'send': 'send_string', - 'sslProfile': 'ssl_profile', - 'timeUntilUp': 'time_until_up', - 'upInterval': 'up_interval', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'adaptive', - 'adaptive_divergence_type', - 'adaptive_divergence_value', - 'adaptive_limit', - 'adaptive_sampling_timespan', - 'destination', - 'interval', - 'ip_dscp', - 'manual_resume', - 'receive_string', - 'receive_disable_string', - 'reverse', - 'send_string', - 'ssl_profile', - 'time_until_up', - 'timeout', - 'transparent', - 'up_interval', - 'username', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def transparent(self): - return flatten_boolean(self._values['transparent']) - - @property - def reverse(self): - return flatten_boolean(self._values['reverse']) - - @property - def manual_resume(self): - return flatten_boolean(self._values['manual_resume']) - - @property - def adaptive(self): - return flatten_boolean(self._values['adaptive']) - - -class HttpsMonitorsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(HttpsMonitorsFactManager, self).__init__(**kwargs) - self.want = HttpsMonitorsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(https_monitors=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = HttpsMonitorsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/https".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = response['items'] - return result - - -class HttpProfilesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'acceptXff': 'accept_xff', - 'explicitProxy': 'explicit_proxy', - 'insertXforwardedFor': 'insert_x_forwarded_for', - 'lwsWidth': 'lws_max_columns', - 'oneconnectTransformations': 'onconnect_transformations', - 'proxyType': 'proxy_mode', - 'redirectRewrite': 'redirect_rewrite', - 'requestChunking': 'request_chunking', - 'responseChunking': 'response_chunking', - 'serverAgentName': 'server_agent_name', - 'viaRequest': 'via_request', - 'viaResponse': 'via_response', - 'pipeline': 'pipeline_action', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'accept_xff', - 'allow_truncated_redirects', - 'excess_client_headers', - 'excess_server_headers', - 'known_methods', - 'max_header_count', - 'max_header_size', - 'max_requests', - 'oversize_client_headers', - 'oversize_server_headers', - 'pipeline_action', - 'unknown_method', - 'default_connect_handling', - 'hsts_include_subdomains', - 'hsts_enabled', - 'insert_x_forwarded_for', - 'lws_max_columns', - 'onconnect_transformations', - 'proxy_mode', - 'redirect_rewrite', - 'request_chunking', - 'response_chunking', - 'server_agent_name', - 'sflow_poll_interval', - 'sflow_sampling_rate', - 'via_request', - 'via_response', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def accept_xff(self): - return flatten_boolean(self._values['accept_xff']) - - @property - def excess_client_headers(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['excessClientHeaders'] is None: - return None - return self._values['enforcement']['excessClientHeaders'] - - @property - def excess_server_headers(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['excessServerHeaders'] is None: - return None - return self._values['enforcement']['excessServerHeaders'] - - @property - def known_methods(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['knownMethods'] is None: - return None - return self._values['enforcement']['knownMethods'] - - @property - def max_header_count(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['maxHeaderCount'] is None: - return None - return self._values['enforcement']['maxHeaderCount'] - - @property - def max_header_size(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['maxHeaderSize'] is None: - return None - return self._values['enforcement']['maxHeaderSize'] - - @property - def max_requests(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['maxRequests'] is None: - return None - return self._values['enforcement']['maxRequests'] - - @property - def oversize_client_headers(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['oversizeClientHeaders'] is None: - return None - return self._values['enforcement']['oversizeClientHeaders'] - - @property - def oversize_server_headers(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['oversizeServerHeaders'] is None: - return None - return self._values['enforcement']['oversizeServerHeaders'] - - @property - def allow_truncated_redirects(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['truncatedRedirects'] is None: - return None - return flatten_boolean(self._values['enforcement']['truncatedRedirects']) - - @property - def unknown_method(self): - if self._values['enforcement'] is None: - return None - if self._values['enforcement']['unknownMethod'] is None: - return None - return self._values['enforcement']['unknownMethod'] - - @property - def default_connect_handling(self): - if self._values['explicit_proxy'] is None: - return None - if self._values['explicit_proxy']['defaultConnectHandling'] is None: - return None - return self._values['explicit_proxy']['defaultConnectHandling'] - - @property - def hsts_include_subdomains(self): - if self._values['hsts'] is None: - return None - if self._values['hsts']['includeSubdomains'] is None: - return None - return flatten_boolean(self._values['hsts']['includeSubdomains']) - - @property - def hsts_enabled(self): - if self._values['hsts'] is None: - return None - if self._values['hsts']['mode'] is None: - return None - return flatten_boolean(self._values['hsts']['mode']) - - @property - def hsts_max_age(self): - if self._values['hsts'] is None: - return None - if self._values['hsts']['mode'] is None: - return None - return self._values['hsts']['maximumAge'] - - @property - def insert_x_forwarded_for(self): - if self._values['insert_x_forwarded_for'] is None: - return None - return flatten_boolean(self._values['insert_x_forwarded_for']) - - @property - def onconnect_transformations(self): - if self._values['onconnect_transformations'] is None: - return None - return flatten_boolean(self._values['onconnect_transformations']) - - @property - def sflow_poll_interval(self): - if self._values['sflow'] is None: - return None - if self._values['sflow']['pollInterval'] is None: - return None - return self._values['sflow']['pollInterval'] - - @property - def sflow_sampling_rate(self): - if self._values['sflow'] is None: - return None - if self._values['sflow']['samplingRate'] is None: - return None - return self._values['sflow']['samplingRate'] - - -class HttpProfilesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(HttpProfilesFactManager, self).__init__(**kwargs) - self.want = HttpProfilesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(http_profiles=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = HttpProfilesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/http".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class IappServicesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'deviceGroup': 'device_group', - 'inheritedDevicegroup': 'inherited_device_group', - 'inheritedTrafficGroup': 'inherited_traffic_group', - 'strictUpdates': 'strict_updates', - 'templateModified': 'template_modified', - 'trafficGroup': 'traffic_group', - } - - returnables = [ - 'full_path', - 'name', - 'device_group', - 'inherited_device_group', - 'inherited_traffic_group', - 'strict_updates', - 'template_modified', - 'traffic_group', - 'tables', - 'variables', - 'metadata', - 'lists', - 'description', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def inherited_device_group(self): - return flatten_boolean(self._values['inherited_device_group']) - - @property - def inherited_traffic_group(self): - return flatten_boolean(self._values['inherited_traffic_group']) - - @property - def strict_updates(self): - return flatten_boolean(self._values['strict_updates']) - - @property - def template_modified(self): - return flatten_boolean(self._values['template_modified']) - - -class IappServicesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(IappServicesFactManager, self).__init__(**kwargs) - self.want = IappServicesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(iapp_services=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = IappServicesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/application/service".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class IapplxPackagesParameters(BaseParameters): - api_map = { - 'packageName': 'package_name', - } - - returnables = [ - 'name', - 'version', - 'release', - 'arch', - 'package_name', - 'tags', - ] - - -class IapplxPackagesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(IapplxPackagesFactManager, self).__init__(**kwargs) - self.want = IapplxPackagesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(iapplx_packages=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['name']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = IapplxPackagesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - params = dict(operation='QUERY') - uri = "https://{0}:{1}/mgmt/shared/iapp/package-management-tasks".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.post(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - status = self.wait_for_task(response['id']) - if status == 'FINISHED': - uri = "https://{0}:{1}/mgmt/shared/iapp/package-management-tasks/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - response['id'] - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - else: - raise F5ModuleError( - "An error occurred querying iAppLX packages." - ) - result = response['queryResponse'] - return result - - def wait_for_task(self, task_id): - uri = "https://{0}:{1}/mgmt/shared/iapp/package-management-tasks/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - task_id - ) - for x in range(0, 60): - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if response['status'] in ['FINISHED', 'FAILED']: - return response['status'] - time.sleep(1) - return response['status'] - - -class IcmpMonitorsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'adaptiveDivergenceType': 'adaptive_divergence_type', - 'adaptiveDivergenceValue': 'adaptive_divergence_value', - 'adaptiveLimit': 'adaptive_limit', - 'adaptiveSamplingTimespan': 'adaptive_sampling_timespan', - 'manualResume': 'manual_resume', - 'timeUntilUp': 'time_until_up', - 'upInterval': 'up_interval', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'adaptive', - 'adaptive_divergence_type', - 'adaptive_divergence_value', - 'adaptive_limit', - 'adaptive_sampling_timespan', - 'destination', - 'interval', - 'manual_resume', - 'time_until_up', - 'timeout', - 'transparent', - 'up_interval', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def transparent(self): - return flatten_boolean(self._values['transparent']) - - @property - def manual_resume(self): - return flatten_boolean(self._values['manual_resume']) - - @property - def adaptive(self): - return flatten_boolean(self._values['adaptive']) - - -class IcmpMonitorsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(IcmpMonitorsFactManager, self).__init__(**kwargs) - self.want = IcmpMonitorsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(icmp_monitors=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = IcmpMonitorsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/icmp".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class InterfacesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'mediaActive': 'active_media_type', - 'flowControl': 'flow_control', - 'bundleSpeed': 'bundle_speed', - 'ifIndex': 'if_index', - 'macAddress': 'mac_address', - 'mediaSfp': 'media_sfp', - 'lldpAdmin': 'lldp_admin', - 'preferPort': 'prefer_port', - 'stpAutoEdgePort': 'stp_auto_edge_port', - 'stp': 'stp_enabled', - 'stpLinkType': 'stp_link_type' - } - - returnables = [ - 'full_path', - 'name', - 'active_media_type', - 'flow_control', - 'description', - 'bundle', - 'bundle_speed', - 'enabled', - 'if_index', - 'mac_address', - 'media_sfp', - 'lldp_admin', - 'mtu', - 'prefer_port', - 'sflow_poll_interval', - 'sflow_poll_interval_global', - 'stp_auto_edge_port', - 'stp_enabled', - 'stp_link_type' - ] - - @property - def stp_auto_edge_port(self): - return flatten_boolean(self._values['stp_auto_edge_port']) - - @property - def stp_enabled(self): - return flatten_boolean(self._values['stp_enabled']) - - @property - def sflow_poll_interval_global(self): - if self._values['sflow'] is None: - return None - if 'pollIntervalGlobal' in self._values['sflow']: - return self._values['sflow']['pollIntervalGlobal'] - - @property - def sflow_poll_interval(self): - if self._values['sflow'] is None: - return None - if 'pollInterval' in self._values['sflow']: - return self._values['sflow']['pollInterval'] - - @property - def mac_address(self): - if self._values['mac_address'] in [None, 'none']: - return None - return self._values['mac_address'] - - @property - def enabled(self): - return flatten_boolean(self._values['enabled']) - - -class InterfacesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(InterfacesFactManager, self).__init__(**kwargs) - self.want = InterfacesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(interfaces=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = InterfacesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/net/interface".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class InternalDataGroupsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path' - } - - returnables = [ - 'full_path', - 'name', - 'type', - 'records' - ] - - -class InternalDataGroupsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(InternalDataGroupsFactManager, self).__init__(**kwargs) - self.want = InternalDataGroupsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(internal_data_groups=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = InternalDataGroupsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/data-group/internal".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class IrulesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'ignoreVerification': 'ignore_verification', - } - - returnables = [ - 'full_path', - 'name', - 'ignore_verification', - 'checksum', - 'definition', - 'signature' - ] - - @property - def checksum(self): - if self._values['apiAnonymous'] is None: - return None - pattern = r'definition-checksum\s(?P\w+)' - matches = re.search(pattern, self._values['apiAnonymous']) - if matches: - return matches.group('checksum') - - @property - def definition(self): - if self._values['apiAnonymous'] is None: - return None - pattern = r'(definition-(checksum|signature)\s[\w=\/+]+)' - result = re.sub(pattern, '', self._values['apiAnonymous']).strip() - if result: - return result - - @property - def signature(self): - if self._values['apiAnonymous'] is None: - return None - pattern = r'definition-signature\s(?P[\w=\/+]+)' - matches = re.search(pattern, self._values['apiAnonymous']) - if matches: - return matches.group('signature') - - @property - def ignore_verification(self): - if self._values['ignore_verification'] is None: - return 'no' - return 'yes' - - -class IrulesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(IrulesFactManager, self).__init__(**kwargs) - self.want = IrulesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(irules=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = IrulesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/rule".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class LtmPoolsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'allowNat': 'allow_nat', - 'allowSnat': 'allow_snat', - 'ignorePersistedWeight': 'ignore_persisted_weight', - 'ipTosToClient': 'client_ip_tos', - 'ipTosToServer': 'server_ip_tos', - 'linkQosToClient': 'client_link_qos', - 'linkQosToServer': 'server_link_qos', - 'loadBalancingMode': 'lb_method', - 'minActiveMembers': 'minimum_active_members', - 'minUpMembers': 'minimum_up_members', - 'minUpMembersAction': 'minimum_up_members_action', - 'minUpMembersChecking': 'minimum_up_members_checking', - 'queueDepthLimit': 'queue_depth_limit', - 'queueOnConnectionLimit': 'queue_on_connection_limit', - 'queueTimeLimit': 'queue_time_limit', - 'reselectTries': 'reselect_tries', - 'serviceDownAction': 'service_down_action', - 'slowRampTime': 'slow_ramp_time', - 'monitor': 'monitors', - } - - returnables = [ - 'full_path', - 'name', - 'allow_nat', - 'allow_snat', - 'description', - 'ignore_persisted_weight', - 'client_ip_tos', - 'server_ip_tos', - 'client_link_qos', - 'server_link_qos', - 'lb_method', - 'minimum_active_members', - 'minimum_up_members', - 'minimum_up_members_action', - 'minimum_up_members_checking', - 'monitors', - 'queue_depth_limit', - 'queue_on_connection_limit', - 'queue_time_limit', - 'reselect_tries', - 'service_down_action', - 'slow_ramp_time', - 'priority_group_activation', - 'members', - 'metadata', - 'active_member_count', - 'available_member_count', - 'availability_status', - 'enabled_status', - 'status_reason', - 'all_max_queue_entry_age_ever', - 'all_avg_queue_entry_age', - 'all_queue_head_entry_age', - 'all_max_queue_entry_age_recently', - 'all_num_connections_queued_now', - 'all_num_connections_serviced', - 'pool_max_queue_entry_age_ever', - 'pool_avg_queue_entry_age', - 'pool_queue_head_entry_age', - 'pool_max_queue_entry_age_recently', - 'pool_num_connections_queued_now', - 'pool_num_connections_serviced', - 'current_sessions', - 'member_count', - 'total_requests', - 'server_side_bits_in', - 'server_side_bits_out', - 'server_side_current_connections', - 'server_side_max_connections', - 'server_side_pkts_in', - 'server_side_pkts_out', - 'server_side_total_connections', - ] - - @property - def active_member_count(self): - if 'availableMemberCnt' in self._values['stats']: - return int(self._values['stats']['activeMemberCnt']) - return None - - @property - def available_member_count(self): - if 'availableMemberCnt' in self._values['stats']: - return int(self._values['stats']['availableMemberCnt']) - return None - - @property - def all_max_queue_entry_age_ever(self): - return self._values['stats']['connqAll']['ageEdm'] - - @property - def all_avg_queue_entry_age(self): - return self._values['stats']['connqAll']['ageEma'] - - @property - def all_queue_head_entry_age(self): - return self._values['stats']['connqAll']['ageHead'] - - @property - def all_max_queue_entry_age_recently(self): - return self._values['stats']['connqAll']['ageMax'] - - @property - def all_num_connections_queued_now(self): - return self._values['stats']['connqAll']['depth'] - - @property - def all_num_connections_serviced(self): - return self._values['stats']['connqAll']['serviced'] - - @property - def availability_status(self): - return self._values['stats']['status']['availabilityState'] - - @property - def enabled_status(self): - return self._values['stats']['status']['enabledState'] - - @property - def status_reason(self): - return self._values['stats']['status']['statusReason'] - - @property - def pool_max_queue_entry_age_ever(self): - return self._values['stats']['connq']['ageEdm'] - - @property - def pool_avg_queue_entry_age(self): - return self._values['stats']['connq']['ageEma'] - - @property - def pool_queue_head_entry_age(self): - return self._values['stats']['connq']['ageHead'] - - @property - def pool_max_queue_entry_age_recently(self): - return self._values['stats']['connq']['ageMax'] - - @property - def pool_num_connections_queued_now(self): - return self._values['stats']['connq']['depth'] - - @property - def pool_num_connections_serviced(self): - return self._values['stats']['connq']['serviced'] - - @property - def current_sessions(self): - return self._values['stats']['curSessions'] - - @property - def member_count(self): - if 'memberCnt' in self._values['stats']: - return self._values['stats']['memberCnt'] - return None - - @property - def total_requests(self): - return self._values['stats']['totRequests'] - - @property - def server_side_bits_in(self): - return self._values['stats']['serverside']['bitsIn'] - - @property - def server_side_bits_out(self): - return self._values['stats']['serverside']['bitsOut'] - - @property - def server_side_current_connections(self): - return self._values['stats']['serverside']['curConns'] - - @property - def server_side_max_connections(self): - return self._values['stats']['serverside']['maxConns'] - - @property - def server_side_pkts_in(self): - return self._values['stats']['serverside']['pktsIn'] - - @property - def server_side_pkts_out(self): - return self._values['stats']['serverside']['pktsOut'] - - @property - def server_side_total_connections(self): - return self._values['stats']['serverside']['totConns'] - - @property - def ignore_persisted_weight(self): - return flatten_boolean(self._values['ignore_persisted_weight']) - - @property - def minimum_up_members_checking(self): - return flatten_boolean(self._values['minimum_up_members_checking']) - - @property - def queue_on_connection_limit(self): - return flatten_boolean(self._values['queue_on_connection_limit']) - - @property - def priority_group_activation(self): - """Returns the TMUI value for "Priority Group Activation" - - This value is identified as ``minActiveMembers`` in the REST API, so this - is just a convenience key for users of Ansible (where the ``bigip_virtual_server`` - parameter is called ``priority_group_activation``. - - Returns: - int: Priority number assigned to the pool members. - """ - return self._values['minimum_active_members'] - - @property - def metadata(self): - """Returns metadata associated with a pool - - An arbitrary amount of metadata may be associated with a pool. You typically - see this used in situations where the user wants to annotate a resource, maybe - in cases where an automation system is responsible for creating the resource. - - The metadata in the API is always stored as a list of dictionaries. We change - this to be a simple dictionary before it is returned to the user. - - Returns: - dict: A dictionary of key/value pairs where the key is the metadata name - and the value is the metadata value. - """ - if self._values['metadata'] is None: - return None - result = dict([(k['name'], k['value']) for k in self._values['metadata']]) - return result - - @property - def members(self): - if not self._values['members']: - return None - result = [] - for member in self._values['members']: - member['connection_limit'] = member.pop('connectionLimit', None) - member['dynamic_ratio'] = member.pop('dynamicRatio', None) - member['full_path'] = member.pop('fullPath', None) - member['inherit_profile'] = member.pop('inheritProfile', None) - member['priority_group'] = member.pop('priorityGroup', None) - member['rate_limit'] = member.pop('rateLimit', None) - - if 'fqdn' in member and 'autopopulate' in member['fqdn']: - if member['fqdn']['autopopulate'] == 'enabled': - member['fqdn_autopopulate'] = 'yes' - elif member['fqdn']['autopopulate'] == 'disabled': - member['fqdn_autopopulate'] = 'no' - del member['fqdn'] - - for key in ['ephemeral', 'inherit_profile', 'logging', 'rate_limit']: - tmp = flatten_boolean(member[key]) - member[key] = tmp - - if 'profiles' in member: - # Even though the ``profiles`` is a list, there is only ever 1 - member['encapsulation_profile'] = [x['name'] for x in member['profiles']][0] - del member['profiles'] - - if 'monitor' in member: - monitors = member.pop('monitor') - if monitors is not None: - try: - member['monitors'] = re.findall(r'/[\w-]+/[^\s}]+', monitors) - except Exception: - member['monitors'] = [monitors.strip()] - - session = member.pop('session') - state = member.pop('state') - - member['real_session'] = session - member['real_state'] = state - - if state in ['user-up', 'unchecked', 'fqdn-up-no-addr', 'fqdn-up'] and session in ['user-enabled']: - member['state'] = 'present' - elif state in ['user-down'] and session in ['user-disabled']: - member['state'] = 'forced_offline' - elif state in ['up', 'checking'] and session in ['monitor-enabled']: - member['state'] = 'present' - elif state in ['down'] and session in ['monitor-enabled']: - member['state'] = 'offline' - else: - member['state'] = 'disabled' - self._remove_internal_keywords(member) - member = dict([(k, v) for k, v in iteritems(member) if v is not None]) - result.append(member) - return result - - @property - def monitors(self): - if self._values['monitors'] is None: - return None - try: - result = re.findall(r'/[\w-]+/[^\s}]+', self._values['monitors']) - return result - except Exception: - return [self._values['monitors'].strip()] - - -class LtmPoolsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(LtmPoolsFactManager, self).__init__(**kwargs) - self.want = LtmPoolsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(ltm_pools=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource - members = self.read_member_from_device(attrs['fullPath']) - attrs['members'] = members - attrs['stats'] = self.read_stats_from_device(attrs['fullPath']) - params = LtmPoolsParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - """Read the LTM pools collection from the device - - Note that sub-collection expansion does not work with LTM pools. Therefore, - one needs to query the ``members`` endpoint separately and add that to the - list of ``attrs`` before the full set of attributes is sent to the ``Parameters`` - class. - - Returns: - list: List of ``Pool`` objects - """ - uri = "https://{0}:{1}/mgmt/tm/ltm/pool".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - def read_member_from_device(self, full_path): - uri = "https://{0}:{1}/mgmt/tm/ltm/pool/{2}/members".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(name=full_path) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - def read_stats_from_device(self, full_path): - uri = "https://{0}:{1}/mgmt/tm/ltm/pool/{2}/stats".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(name=full_path) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - try: - return result['stats'] - except KeyError: - return {} - - -class LtmPolicyParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'rulesReference': 'rules', - } - - returnables = [ - 'full_path', - 'name', - 'status', - 'description', - 'strategy', - 'rules', - 'requires', - 'controls', - ] - - def _handle_conditions(self, conditions): - result = [] - if conditions is None or 'items' not in conditions: - return result - for condition in conditions['items']: - tmp = dict() - tmp['case_insensitive'] = flatten_boolean(condition.pop('caseInsensitive', None)) - tmp['case_sensitive'] = flatten_boolean(condition.pop('caseSensitive', None)) - tmp['contains_string'] = flatten_boolean(condition.pop('contains', None)) - tmp['external'] = flatten_boolean(condition.pop('external', None)) - tmp['http_basic_auth'] = flatten_boolean(condition.pop('httpBasicAuth', None)) - tmp['http_host'] = flatten_boolean(condition.pop('httpHost', None)) - tmp['http_uri'] = flatten_boolean(condition.pop('httpUri', None)) - tmp['request'] = flatten_boolean(condition.pop('request', None)) - tmp['username'] = flatten_boolean(condition.pop('username', None)) - tmp['external'] = flatten_boolean(condition.pop('external', None)) - tmp['values'] = condition.pop('values', None) - tmp['all'] = flatten_boolean(condition.pop('all', None)) - result.append(self._filter_params(tmp)) - return result - - def _handle_actions(self, actions): - result = [] - if actions is None or 'items' not in actions: - return result - for action in actions['items']: - tmp = dict() - tmp['httpReply'] = flatten_boolean(action.pop('http_reply', None)) - tmp['redirect'] = flatten_boolean(action.pop('redirect', None)) - tmp['request'] = flatten_boolean(action.pop('request', None)) - tmp['location'] = action.pop('location', None) - result.append(self._filter_params(tmp)) - return result - - @property - def rules(self): - result = [] - if self._values['rules'] is None or 'items' not in self._values['rules']: - return result - for item in self._values['rules']['items']: - self._remove_internal_keywords(item) - item['conditions'] = self._handle_conditions(item.pop('conditionsReference', None)) - item['actions'] = self._handle_actions(item.pop('actionsReference', None)) - result.append(item) - return result - - -class LtmPolicyFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(LtmPolicyFactManager, self).__init__(**kwargs) - self.want = LtmPolicyParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(ltm_policies=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = LtmPolicyParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/policy/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - query = "?expandSubcollections=true" - resp = self.client.api.get(uri + query) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class NodesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'connectionLimit': 'connection_limit', - 'dynamicRatio': 'dynamic_ratio', - 'rateLimit': 'rate_limit', - 'monitor': 'monitors' - } - - returnables = [ - 'full_path', - 'name', - 'ratio', - 'description', - 'connection_limit', - 'address', - 'dynamic_ratio', - 'rate_limit', - 'monitor_status', - 'session_status', - 'availability_status', - 'enabled_status', - 'status_reason', - 'monitor_rule', - 'monitors', - 'monitor_type' - ] - - @property - def monitors(self): - if self._values['monitors'] is None: - return [] - try: - result = re.findall(r'/\w+/[^\s}]+', self._values['monitors']) - return result - except Exception: - return [self._values['monitors']] - - @property - def monitor_type(self): - if self._values['monitors'] is None: - return None - pattern = r'min\s+\d+\s+of' - matches = re.search(pattern, self._values['monitors']) - if matches: - return 'm_of_n' - else: - return 'and_list' - - @property - def rate_limit(self): - if self._values['rate_limit'] is None: - return None - elif self._values['rate_limit'] == 'disabled': - return 0 - else: - return int(self._values['rate_limit']) - - @property - def monitor_status(self): - return self._values['stats']['monitorStatus'] - - @property - def session_status(self): - return self._values['stats']['sessionStatus'] - - @property - def availability_status(self): - return self._values['stats']['status']['availabilityState'] - - @property - def enabled_status(self): - return self._values['stats']['status']['enabledState'] - - @property - def status_reason(self): - return self._values['stats']['status']['statusReason'] - - @property - def monitor_rule(self): - return self._values['stats']['monitorRule'] - - -class NodesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(NodesFactManager, self).__init__(**kwargs) - self.want = NodesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(nodes=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource - attrs['stats'] = self.read_stats_from_device(attrs['fullPath']) - params = NodesParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/node".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - def read_stats_from_device(self, full_path): - uri = "https://{0}:{1}/mgmt/tm/ltm/node/{2}/stats".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(name=full_path) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - try: - return result['stats'] - except KeyError: - return {} - - -class OneConnectProfilesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'clientTimeout': 'client_timeout', - 'defaultsFrom': 'parent', - 'idleTimeoutOverride': 'idle_timeout_override', - 'limitType': 'limit_type', - 'maxAge': 'max_age', - 'maxReuse': 'max_reuse', - 'maxSize': 'max_size', - 'sharePools': 'share_pools', - 'sourceMask': 'source_mask', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'idle_timeout_override', - 'limit_type', - 'max_age', - 'max_reuse', - 'max_size', - 'share_pools', - 'source_mask', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def idle_timeout_override(self): - if self._values['idle_timeout_override'] is None: - return None - elif self._values['idle_timeout_override'] == 'disabled': - return 0 - elif self._values['idle_timeout_override'] == 'indefinite': - return 4294967295 - return int(self._values['idle_timeout_override']) - - @property - def share_pools(self): - return flatten_boolean(self._values['share_pools']) - - -class OneConnectProfilesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(OneConnectProfilesFactManager, self).__init__(**kwargs) - self.want = OneConnectProfilesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(oneconnect_profiles=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = OneConnectProfilesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/one-connect".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class PartitionParameters(BaseParameters): - api_map = { - 'defaultRouteDomain': 'default_route_domain', - 'fullPath': 'full_path', - } - - returnables = [ - 'name', - 'full_path', - 'description', - 'default_route_domain' - ] - - -class PartitionFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(PartitionFactManager, self).__init__(**kwargs) - self.want = PartitionParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(partitions=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = PartitionParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/auth/partition".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class ProvisionInfoParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'cpuRatio': 'cpu_ratio', - 'diskRatio': 'disk_ratio', - 'memoryRatio': 'memory_ratio', - } - - returnables = [ - 'full_path', - 'name', - 'cpu_ratio', - 'disk_ratio', - 'memory_ratio', - 'level' - ] - - -class ProvisionInfoFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(ProvisionInfoFactManager, self).__init__(**kwargs) - self.want = ProvisionInfoParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(provision_info=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = ProvisionInfoParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/provision".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class RouteDomainParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'bwcPolicy': 'bandwidth_controller_policy', - 'connectionLimit': 'connection_limit', - 'flowEvictionPolicy': 'flow_eviction_policy', - 'servicePolicy': 'service_policy', - 'routingProtocol': 'routing_protocol' - } - - returnables = [ - 'name', - 'id', - 'full_path', - 'parent', - 'bandwidth_controller_policy', - 'connection_limit', - 'description', - 'flow_eviction_policy', - 'service_policy', - 'strict', - 'routing_protocol', - 'vlans' - ] - - @property - def strict(self): - return flatten_boolean(self._values['strict']) - - @property - def connection_limit(self): - if self._values['connection_limit'] is None: - return None - return int(self._values['connection_limit']) - - -class RouteDomainFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(RouteDomainFactManager, self).__init__(**kwargs) - self.want = RouteDomainParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(route_domains=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = RouteDomainParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/net/route-domain".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class SelfIpsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'trafficGroup': 'traffic_group', - 'servicePolicy': 'service_policy', - 'allowService': 'allow_access_list', - 'inheritedTrafficGroup': 'traffic_group_inherited' - } - - returnables = [ - 'full_path', - 'name', - 'address', - 'description', - 'netmask', - 'netmask_cidr', - 'floating', - 'traffic_group', - 'service_policy', - 'vlan', - 'allow_access_list', - 'traffic_group_inherited' - ] - - @property - def address(self): - parts = self._values['address'].split('/') - return parts[0] - - @property - def netmask(self): - parts = self._values['address'].split('/') - return to_netmask(parts[1]) - - @property - def netmask_cidr(self): - parts = self._values['address'].split('/') - return int(parts[1]) - - @property - def traffic_group_inherited(self): - if self._values['traffic_group_inherited'] is None: - return None - elif self._values['traffic_group_inherited'] in [False, 'false']: - # BIG-IP appears to store this as a string. This is a bug, so we handle both - # cases here. - return 'no' - else: - return 'yes' - - @property - def floating(self): - if self._values['floating'] is None: - return None - elif self._values['floating'] == 'disabled': - return 'no' - else: - return 'yes' - - -class SelfIpsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SelfIpsFactManager, self).__init__(**kwargs) - self.want = SelfIpsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(self_ips=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = SelfIpsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/net/self".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class ServerSslProfilesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'alertTimeout': 'alert_timeout', - 'allowExpiredCrl': 'allow_expired_crl', - 'authenticate': 'authentication_frequency', - 'authenticateDepth': 'authenticate_depth', - 'authenticateName': 'authenticate_name', - 'bypassOnClientCertFail': 'bypass_on_client_cert_fail', - 'bypassOnHandshakeAlert': 'bypass_on_handshake_alert', - 'c3dCaCert': 'c3d_ca_cert', - 'c3dCaKey': 'c3d_ca_key', - 'c3dCertExtensionIncludes': 'c3d_cert_extension_includes', - 'c3dCertLifespan': 'c3d_cert_lifespan', - 'caFile': 'ca_file', - 'cacheSize': 'cache_size', - 'cacheTimeout': 'cache_timeout', - 'cipherGroup': 'cipher_group', - 'crlFile': 'crl_file', - 'expireCertResponseControl': 'expire_cert_response_control', - 'genericAlert': 'generic_alert', - 'handshakeTimeout': 'handshake_timeout', - 'maxActiveHandshakes': 'max_active_handshakes', - 'modSslMethods': 'mod_ssl_methods', - 'tmOptions': 'options', - 'peerCertMode': 'peer_cert_mode', - 'proxySsl': 'proxy_ssl', - 'proxySslPassthrough': 'proxy_ssl_passthrough', - 'renegotiatePeriod': 'renegotiate_period', - 'renegotiateSize': 'renegotiate_size', - 'retainCertificate': 'retain_certificate', - 'secureRenegotiation': 'secure_renegotiation', - 'serverName': 'server_name', - 'sessionMirroring': 'session_mirroring', - 'sessionTicket': 'session_ticket', - 'sniDefault': 'sni_default', - 'sniRequire': 'sni_require', - 'sslC3d': 'ssl_c3d', - 'sslForwardProxy': 'ssl_forward_proxy_enabled', - 'sslForwardProxyBypass': 'ssl_forward_proxy_bypass', - 'sslSignHash': 'ssl_sign_hash', - 'strictResume': 'strict_resume', - 'uncleanShutdown': 'unclean_shutdown', - 'untrustedCertResponseControl': 'untrusted_cert_response_control' - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'unclean_shutdown', - 'strict_resume', - 'ssl_forward_proxy_enabled', - 'ssl_forward_proxy_bypass', - 'sni_default', - 'sni_require', - 'ssl_c3d', - 'session_mirroring', - 'session_ticket', - 'mod_ssl_methods', - 'allow_expired_crl', - 'retain_certificate', - 'mode', - 'bypass_on_client_cert_fail', - 'bypass_on_handshake_alert', - 'generic_alert', - 'renegotiation', - 'proxy_ssl', - 'proxy_ssl_passthrough', - 'peer_cert_mode', - 'untrusted_cert_response_control', - 'ssl_sign_hash', - 'server_name', - 'secure_renegotiation', - 'renegotiate_size', - 'renegotiate_period', - 'options', - 'ocsp', - 'max_active_handshakes', - 'key', - 'handshake_timeout', - 'expire_cert_response_control', - 'cert', - 'chain', - 'authentication_frequency', - 'ciphers', - 'cipher_group', - 'crl_file', - 'cache_timeout', - 'cache_size', - 'ca_file', - 'c3d_cert_lifespan', - 'alert_timeout', - 'c3d_ca_key', - 'authenticate_depth', - 'authenticate_name', - 'c3d_ca_cert', - 'c3d_cert_extension_includes', - ] - - @property - def c3d_cert_extension_includes(self): - if self._values['c3d_cert_extension_includes'] is None: - return None - if len(self._values['c3d_cert_extension_includes']) == 0: - return None - self._values['c3d_cert_extension_includes'].sort() - return self._values['c3d_cert_extension_includes'] - - @property - def options(self): - if self._values['options'] is None: - return None - if len(self._values['options']) == 0: - return None - self._values['options'].sort() - return self._values['options'] - - @property - def c3d_ca_cert(self): - if self._values['c3d_ca_cert'] in [None, 'none']: - return None - return self._values['c3d_ca_cert'] - - @property - def ocsp(self): - if self._values['ocsp'] in [None, 'none']: - return None - return self._values['ocsp'] - - @property - def server_name(self): - if self._values['server_name'] in [None, 'none']: - return None - return self._values['server_name'] - - @property - def cipher_group(self): - if self._values['cipher_group'] in [None, 'none']: - return None - return self._values['cipher_group'] - - @property - def authenticate_name(self): - if self._values['authenticate_name'] in [None, 'none']: - return None - return self._values['authenticate_name'] - - @property - def c3d_ca_key(self): - if self._values['c3d_ca_key'] in [None, 'none']: - return None - return self._values['c3d_ca_key'] - - @property - def ca_file(self): - if self._values['ca_file'] in [None, 'none']: - return None - return self._values['ca_file'] - - @property - def crl_file(self): - if self._values['crl_file'] in [None, 'none']: - return None - return self._values['crl_file'] - - @property - def authentication_frequency(self): - if self._values['authentication_frequency'] in [None, 'none']: - return None - return self._values['authentication_frequency'] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def proxy_ssl_passthrough(self): - return flatten_boolean(self._values['proxy_ssl_passthrough']) - - @property - def proxy_ssl(self): - return flatten_boolean(self._values['proxy_ssl']) - - @property - def generic_alert(self): - return flatten_boolean(self._values['generic_alert']) - - @property - def renegotiation(self): - return flatten_boolean(self._values['renegotiation']) - - @property - def bypass_on_handshake_alert(self): - return flatten_boolean(self._values['bypass_on_handshake_alert']) - - @property - def bypass_on_client_cert_fail(self): - return flatten_boolean(self._values['bypass_on_client_cert_fail']) - - @property - def mode(self): - return flatten_boolean(self._values['mode']) - - @property - def retain_certificate(self): - return flatten_boolean(self._values['retain_certificate']) - - @property - def allow_expired_crl(self): - return flatten_boolean(self._values['allow_expired_crl']) - - @property - def mod_ssl_methods(self): - return flatten_boolean(self._values['mod_ssl_methods']) - - @property - def session_ticket(self): - return flatten_boolean(self._values['session_ticket']) - - @property - def session_mirroring(self): - return flatten_boolean(self._values['session_mirroring']) - - @property - def unclean_shutdown(self): - return flatten_boolean(self._values['unclean_shutdown']) - - @property - def strict_resume(self): - return flatten_boolean(self._values['strict_resume']) - - @property - def ssl_forward_proxy_enabled(self): - return flatten_boolean(self._values['ssl_forward_proxy_enabled']) - - @property - def ssl_forward_proxy_bypass(self): - return flatten_boolean(self._values['ssl_forward_proxy_bypass']) - - @property - def sni_default(self): - return flatten_boolean(self._values['sni_default']) - - @property - def sni_require(self): - return flatten_boolean(self._values['sni_require']) - - @property - def ssl_c3d(self): - return flatten_boolean(self._values['ssl_c3d']) - - -class ServerSslProfilesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(ServerSslProfilesFactManager, self).__init__(**kwargs) - self.want = ServerSslProfilesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(server_ssl_profiles=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = ServerSslProfilesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/server-ssl".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class SoftwareVolumesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'basebuild': 'base_build', - } - - returnables = [ - 'full_path', - 'name', - 'active', - 'base_build', - 'build', - 'product', - 'status', - 'version', - 'install_volume', - 'default_boot_location' - ] - - @property - def install_volume(self): - if self._values['media'] is None: - return None - return self._values['media'].get('name', None) - - @property - def default_boot_location(self): - if self._values['media'] is None: - return None - return flatten_boolean(self._values['media'].get('defaultBootLocation', None)) - - @property - def active(self): - if self._values['active'] is True: - return 'yes' - return 'no' - - -class SoftwareVolumesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SoftwareVolumesFactManager, self).__init__(**kwargs) - self.want = SoftwareVolumesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(software_volumes=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = SoftwareVolumesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/software/volume".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class SoftwareHotfixesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - } - - returnables = [ - 'name', - 'full_path', - 'build', - 'checksum', - 'id', - 'product', - 'title', - 'verified', - 'version', - ] - - -class SoftwareHotfixesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SoftwareHotfixesFactManager, self).__init__(**kwargs) - self.want = SoftwareHotfixesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(software_hotfixes=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = SoftwareHotfixesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/software/hotfix".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class SoftwareImagesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'buildDate': 'build_date', - 'fileSize': 'file_size', - 'lastModified': 'last_modified', - } - - returnables = [ - 'name', - 'full_path', - 'build', - 'build_date', - 'checksum', - 'file_size', - 'last_modified', - 'product', - 'verified', - 'version', - ] - - @property - def file_size(self): - if self._values['file_size'] is None: - return None - matches = re.match(r'\d+', self._values['file_size']) - if matches: - return int(matches.group(0)) - - @property - def build_date(self): - """Normalizes the build_date string - - The ISOs usually ship with a broken format - - ex: Tue May 15 15 26 30 PDT 2018 - - This will re-format that time so that it looks like ISO 8601 without - microseconds - - ex: 2018-05-15T15:26:30 - - :return: - """ - if self._values['build_date'] is None: - return None - - d = self._values['build_date'].split(' ') - - # This removes the timezone portion from the string. This is done - # because Python has awfule tz parsing and strptime doesnt work with - # all timezones in %Z; it only uses the timezones found in time.tzname - d.pop(6) - - result = datetime.datetime.strptime(' '.join(d), '%a %b %d %H %M %S %Y').isoformat() - return result - - @property - def last_modified(self): - """Normalizes the last_modified string - - The strings that the system reports look like the following - - ex: Tue May 15 15:26:30 2018 - - This property normalizes this value to be isoformat - - ex: 2018-05-15T15:26:30 - - :return: - """ - if self._values['last_modified'] is None: - return None - result = datetime.datetime.strptime(self._values['last_modified'], '%a %b %d %H:%M:%S %Y').isoformat() - return result - - -class SoftwareImagesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SoftwareImagesFactManager, self).__init__(**kwargs) - self.want = SoftwareImagesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(software_images=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = SoftwareImagesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/software/image".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class SslCertificatesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'keyType': 'key_type', - 'certificateKeySize': 'key_size', - 'systemPath': 'system_path', - 'checksum': 'sha1_checksum', - 'lastUpdateTime': 'last_update_time', - 'isBundle': 'is_bundle', - 'expirationString': 'expiration_date', - 'expirationDate': 'expiration_timestamp', - 'createTime': 'create_time' - } - - returnables = [ - 'full_path', - 'name', - 'key_type', - 'key_size', - 'system_path', - 'sha1_checksum', - 'subject', - 'last_update_time', - 'issuer', - 'is_bundle', - 'fingerprint', - 'expiration_date', - 'expiration_timestamp', - 'create_time', - ] - - @property - def sha1_checksum(self): - if self._values['sha1_checksum'] is None: - return None - parts = self._values['sha1_checksum'].split(':') - return parts[2] - - @property - def is_bundle(self): - if self._values['sha1_checksum'] is None: - return None - if self._values['is_bundle'] in BOOLEANS_TRUE: - return 'yes' - return 'no' - - -class SslCertificatesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SslCertificatesFactManager, self).__init__(**kwargs) - self.want = SslCertificatesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(ssl_certs=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = SslCertificatesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/file/ssl-cert".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class SslKeysParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'keyType': 'key_type', - 'keySize': 'key_size', - 'securityType': 'security_type', - 'systemPath': 'system_path', - 'checksum': 'sha1_checksum' - } - - returnables = [ - 'full_path', - 'name', - 'key_type', - 'key_size', - 'security_type', - 'system_path', - 'sha1_checksum' - ] - - @property - def sha1_checksum(self): - if self._values['sha1_checksum'] is None: - return None - parts = self._values['sha1_checksum'].split(':') - return parts[2] - - -class SslKeysFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SslKeysFactManager, self).__init__(**kwargs) - self.want = SslKeysParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(ssl_keys=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = SslKeysParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/file/ssl-key".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class SystemDbParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultValue': 'default', - 'scfConfig': 'scf_config', - 'valueRange': 'value_range' - } - - returnables = [ - 'name', - 'full_path', - 'default', - 'scf_config', - 'value', - 'value_range' - ] - - -class SystemDbFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SystemDbFactManager, self).__init__(**kwargs) - self.want = SystemInfoParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(system_db=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = SystemDbParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/db".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class SystemInfoParameters(BaseParameters): - api_map = { - - } - - returnables = [ - 'base_mac_address', - 'marketing_name', - 'time', - 'hardware_information', - 'product_information', - 'package_edition', - 'package_version', - 'product_code', - 'product_build', - 'product_built', - 'product_build_date', - 'product_changelist', - 'product_jobid', - 'product_version', - 'uptime', - 'chassis_serial', - 'host_board_part_revision', - 'host_board_serial', - 'platform', - 'switch_board_part_revision', - 'switch_board_serial' - ] - - @property - def chassis_serial(self): - if self._values['system-info'] is None: - return None - if 'bigipChassisSerialNum' not in self._values['system-info'][0]: - return None - return self._values['system-info'][0]['bigipChassisSerialNum'] - - @property - def switch_board_serial(self): - if self._values['system-info'] is None: - return None - if 'switchBoardSerialNum' not in self._values['system-info'][0]: - return None - if self._values['system-info'][0]['switchBoardSerialNum'].strip() == '': - return None - return self._values['system-info'][0]['switchBoardSerialNum'] - - @property - def switch_board_part_revision(self): - if self._values['system-info'] is None: - return None - if 'switchBoardPartRevNum' not in self._values['system-info'][0]: - return None - if self._values['system-info'][0]['switchBoardPartRevNum'].strip() == '': - return None - return self._values['system-info'][0]['switchBoardPartRevNum'] - - @property - def platform(self): - if self._values['system-info'] is None: - return None - return self._values['system-info'][0]['platform'] - - @property - def host_board_serial(self): - if self._values['system-info'] is None: - return None - if 'hostBoardSerialNum' not in self._values['system-info'][0]: - return None - if self._values['system-info'][0]['hostBoardSerialNum'].strip() == '': - return None - return self._values['system-info'][0]['hostBoardSerialNum'] - - @property - def host_board_part_revision(self): - if self._values['system-info'] is None: - return None - if 'hostBoardPartRevNum' not in self._values['system-info'][0]: - return None - if self._values['system-info'][0]['hostBoardPartRevNum'].strip() == '': - return None - return self._values['system-info'][0]['hostBoardPartRevNum'] - - @property - def package_edition(self): - return self._values['Edition'] - - @property - def package_version(self): - return 'Build {0} - {1}'.format(self._values['Build'], self._values['Date']) - - @property - def product_build(self): - return self._values['Build'] - - @property - def product_build_date(self): - return self._values['Date'] - - @property - def product_built(self): - if 'Built' in self._values['version_info']: - return int(self._values['version_info']['Built']) - - @property - def product_changelist(self): - if 'Changelist' in self._values['version_info']: - return int(self._values['version_info']['Changelist']) - - @property - def product_jobid(self): - if 'JobID' in self._values['version_info']: - return int(self._values['version_info']['JobID']) - - @property - def product_code(self): - return self._values['Product'] - - @property - def product_version(self): - return self._values['Version'] - - @property - def hardware_information(self): - if self._values['hardware-version'] is None: - return None - self._transform_name_attribute(self._values['hardware-version']) - result = [v for k, v in iteritems(self._values['hardware-version'])] - return result - - def _transform_name_attribute(self, entry): - if isinstance(entry, dict): - for k, v in iteritems(entry): - if k == 'tmName': - entry['name'] = entry.pop('tmName') - self._transform_name_attribute(v) - elif isinstance(entry, list): - for k in entry: - if k == 'tmName': - entry['name'] = entry.pop('tmName') - self._transform_name_attribute(k) - else: - return - - @property - def time(self): - if self._values['fullDate'] is None: - return None - date = datetime.datetime.strptime(self._values['fullDate'], "%Y-%m-%dT%H:%M:%SZ") - result = dict( - day=date.day, - hour=date.hour, - minute=date.minute, - month=date.month, - second=date.second, - year=date.year - ) - return result - - @property - def marketing_name(self): - if self._values['platform'] is None: - return None - return self._values['platform'][0]['marketingName'] - - @property - def base_mac_address(self): - if self._values['platform'] is None: - return None - return self._values['platform'][0]['baseMac'] - - -class SystemInfoFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SystemInfoFactManager, self).__init__(**kwargs) - self.want = SystemInfoParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(system_info=facts) - return result - - def _exec_module(self): - facts = self.read_facts() - results = facts.to_return() - return results - - def read_facts(self): - collection = self.read_collection_from_device() - params = SystemInfoParameters(params=collection) - return params - - def read_collection_from_device(self): - result = dict() - tmp = self.read_hardware_info_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_clock_info_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_version_info_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_uptime_info_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_version_file_info_from_device() - if tmp: - result.update(tmp) - - return result - - def read_version_file_info_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/util/bash".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - args = dict( - command='run', - utilCmdArgs='-c "cat /VERSION"' - ) - resp = self.client.api.post(uri, json=args) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - try: - pattern = r'^(?P(Product|Build|Sequence|BaseBuild|Edition|Date|Built|Changelist|JobID))\:(?P.*)' - result = response['commandResult'].strip() - except KeyError: - return None - - if 'No such file or directory' in result: - return None - - lines = response['commandResult'].split("\n") - result = dict() - for line in lines: - if not line: - continue - matches = re.match(pattern, line) - if matches: - result[matches.group('key')] = matches.group('value').strip() - - if result: - return dict( - version_info=result - ) - - def read_uptime_info_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/util/bash".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - args = dict( - command='run', - utilCmdArgs='-c "cat /proc/uptime"' - ) - resp = self.client.api.post(uri, json=args) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - try: - parts = response['commandResult'].strip().split(' ') - return dict( - uptime=math.floor(float(parts[0])) - ) - except KeyError: - pass - - def read_hardware_info_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/hardware".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - return result - - def read_clock_info_from_device(self): - """Parses clock info from the REST API - - The clock stat returned from the REST API (at the time of 13.1.0.7) - is similar to the following. - - { - "kind": "tm:sys:clock:clockstats", - "selfLink": "https://localhost/mgmt/tm/sys/clock?ver=13.1.0.4", - "entries": { - "https://localhost/mgmt/tm/sys/clock/0": { - "nestedStats": { - "entries": { - "fullDate": { - "description": "2018-06-05T13:38:33Z" - } - } - } - } - } - } - - Parsing this data using the ``parseStats`` method, yields a list of - the clock stats in a format resembling that below. - - [{'fullDate': '2018-06-05T13:41:05Z'}] - - Therefore, this method cherry-picks the first entry from this list - and returns it. There can be no other items in this list. - - Returns: - A dict mapping keys to the corresponding clock stats. For - example: - - {'fullDate': '2018-06-05T13:41:05Z'} - - There should never not be a clock stat, unless by chance it - is removed from the API in the future, or changed to a different - API endpoint. - - Raises: - F5ModuleError: A non-successful HTTP code was returned or a JSON - response was not found. - """ - uri = "https://{0}:{1}/mgmt/tm/sys/clock".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - return result[0] - - def read_version_info_from_device(self): - """Parses version info from the REST API - - The version stat returned from the REST API (at the time of 13.1.0.7) - is similar to the following. - - { - "kind": "tm:sys:version:versionstats", - "selfLink": "https://localhost/mgmt/tm/sys/version?ver=13.1.0.4", - "entries": { - "https://localhost/mgmt/tm/sys/version/0": { - "nestedStats": { - "entries": { - "Build": { - "description": "0.0.6" - }, - "Date": { - "description": "Tue Mar 13 20:10:42 PDT 2018" - }, - "Edition": { - "description": "Point Release 4" - }, - "Product": { - "description": "BIG-IP" - }, - "Title": { - "description": "Main Package" - }, - "Version": { - "description": "13.1.0.4" - } - } - } - } - } - } - - Parsing this data using the ``parseStats`` method, yields a list of - the clock stats in a format resembling that below. - - [{'Build': '0.0.6', 'Date': 'Tue Mar 13 20:10:42 PDT 2018', - 'Edition': 'Point Release 4', 'Product': 'BIG-IP', 'Title': 'Main Package', - 'Version': '13.1.0.4'}] - - Therefore, this method cherry-picks the first entry from this list - and returns it. There can be no other items in this list. - - Returns: - A dict mapping keys to the corresponding clock stats. For - example: - - {'Build': '0.0.6', 'Date': 'Tue Mar 13 20:10:42 PDT 2018', - 'Edition': 'Point Release 4', 'Product': 'BIG-IP', 'Title': 'Main Package', - 'Version': '13.1.0.4'} - - There should never not be a version stat, unless by chance it - is removed from the API in the future, or changed to a different - API endpoint. - - Raises: - F5ModuleError: A non-successful HTTP code was returned or a JSON - response was not found. - """ - uri = "https://{0}:{1}/mgmt/tm/sys/version".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - return result[0] - - -class TcpMonitorsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'adaptiveDivergenceType': 'adaptive_divergence_type', - 'adaptiveDivergenceValue': 'adaptive_divergence_value', - 'adaptiveLimit': 'adaptive_limit', - 'adaptiveSamplingTimespan': 'adaptive_sampling_timespan', - 'ipDscp': 'ip_dscp', - 'manualResume': 'manual_resume', - 'timeUntilUp': 'time_until_up', - 'upInterval': 'up_interval', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'adaptive', - 'adaptive_divergence_type', - 'adaptive_divergence_value', - 'adaptive_limit', - 'adaptive_sampling_timespan', - 'destination', - 'interval', - 'ip_dscp', - 'manual_resume', - 'reverse', - 'time_until_up', - 'timeout', - 'transparent', - 'up_interval', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def transparent(self): - return flatten_boolean(self._values['transparent']) - - @property - def manual_resume(self): - return flatten_boolean(self._values['manual_resume']) - - @property - def adaptive(self): - return flatten_boolean(self._values['adaptive']) - - @property - def reverse(self): - return flatten_boolean(self._values['reverse']) - - -class TcpMonitorsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(TcpMonitorsFactManager, self).__init__(**kwargs) - self.want = TcpMonitorsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(tcp_monitors=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = TcpMonitorsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class TcpHalfOpenMonitorsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'manualResume': 'manual_resume', - 'timeUntilUp': 'time_until_up', - 'upInterval': 'up_interval', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'destination', - 'interval', - 'manual_resume', - 'time_until_up', - 'timeout', - 'transparent', - 'up_interval', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def transparent(self): - return flatten_boolean(self._values['transparent']) - - @property - def manual_resume(self): - return flatten_boolean(self._values['manual_resume']) - - -class TcpHalfOpenMonitorsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(TcpHalfOpenMonitorsFactManager, self).__init__(**kwargs) - self.want = TcpHalfOpenMonitorsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(tcp_half_open_monitors=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = TcpHalfOpenMonitorsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-half-open".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class TcpProfilesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'defaultsFrom': 'parent', - 'ackOnPush': 'ack_on_push', - 'autoProxyBufferSize': 'auto_proxy_buffer', - 'autoReceiveWindowSize': 'auto_receive_window', - 'autoSendBufferSize': 'auto_send_buffer', - 'closeWaitTimeout': 'close_wait', - 'cmetricsCache': 'congestion_metrics_cache', - 'cmetricsCacheTimeout': 'congestion_metrics_cache_timeout', - 'congestionControl': 'congestion_control', - 'deferredAccept': 'deferred_accept', - 'delayWindowControl': 'delay_window_control', - 'delayedAcks': 'delayed_acks', - 'earlyRetransmit': 'early_retransmit', - 'ecn': 'explicit_congestion_notification', - 'enhancedLossRecovery': 'enhanced_loss_recovery', - 'fastOpen': 'fast_open', - 'fastOpenCookieExpiration': 'fast_open_cookie_expiration', - 'finWaitTimeout': 'fin_wait_1', - 'finWait_2Timeout': 'fin_wait_2', - 'idleTimeout': 'idle_timeout', - 'initCwnd': 'initial_congestion_window_size', - 'initRwnd': 'initial_receive_window_size', - 'ipDfMode': 'dont_fragment_flag', - 'ipTosToClient': 'ip_tos', - 'ipTtlMode': 'time_to_live', - 'ipTtlV4': 'time_to_live_v4', - 'ipTtlV6': 'time_to_live_v6', - 'keepAliveInterval': 'keep_alive_interval', - 'limitedTransmit': 'limited_transmit_recovery', - 'linkQosToClient': 'link_qos', - 'maxRetrans': 'max_segment_retrans', - 'synMaxRetrans': 'max_syn_retrans', - 'rexmtThresh': 'retransmit_threshold', - 'maxSegmentSize': 'max_segment_size', - 'md5Signature': 'md5_signature', - 'minimumRto': 'minimum_rto', - 'mptcp': 'multipath_tcp', - 'mptcpCsum': 'mptcp_checksum', - 'mptcpCsumVerify': 'mptcp_checksum_verify', - 'mptcpFallback': 'mptcp_fallback', - 'mptcpFastjoin': 'mptcp_fast_join', - 'mptcpIdleTimeout': 'mptcp_idle_timeout', - 'mptcpJoinMax': 'mptcp_join_max', - 'mptcpMakeafterbreak': 'mptcp_make_after_break', - 'mptcpNojoindssack': 'mptcp_no_join_dss_ack', - 'mptcpRtomax': 'mptcp_rto_max', - 'mptcpRxmitmin': 'mptcp_retransmit_min', - 'mptcpSubflowmax': 'mptcp_subflow_max', - 'mptcpTimeout': 'mptcp_timeout', - 'nagle': 'nagle_algorithm', - 'pktLossIgnoreBurst': 'pkt_loss_ignore_burst', - 'pktLossIgnoreRate': 'pkt_loss_ignore_rate', - 'proxyBufferHigh': 'proxy_buffer_high', - 'proxyBufferLow': 'proxy_buffer_low', - 'proxyMss': 'proxy_max_segment', - 'proxyOptions': 'proxy_options', - 'pushFlag': 'push_flag', - 'ratePace': 'rate_pace', - 'ratePaceMaxRate': 'rate_pace_max_rate', - 'receiveWindowSize': 'receive_window', - 'resetOnTimeout': 'reset_on_timeout', - 'selectiveAcks': 'selective_acks', - 'selectiveNack': 'selective_nack', - 'sendBufferSize': 'send_buffer', - 'slowStart': 'slow_start', - 'synCookieEnable': 'syn_cookie_enable', - 'synCookieWhitelist': 'syn_cookie_white_list', - 'synRtoBase': 'syn_retrans_to_base', - 'tailLossProbe': 'tail_loss_probe', - 'timeWaitRecycle': 'time_wait_recycle', - 'timeWaitTimeout': 'time_wait', - 'verifiedAccept': 'verified_accept', - 'zeroWindowTimeout': 'zero_window_timeout', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'abc', - 'ack_on_push', - 'auto_proxy_buffer', - 'auto_receive_window', - 'auto_send_buffer', - 'close_wait', - 'congestion_metrics_cache', - 'congestion_metrics_cache_timeout', - 'congestion_control', - 'deferred_accept', - 'delay_window_control', - 'delayed_acks', - 'dsack', - 'early_retransmit', - 'explicit_congestion_notification', - 'enhanced_loss_recovery', - 'fast_open', - 'fast_open_cookie_expiration', - 'fin_wait_1', - 'fin_wait_2', - 'idle_timeout', - 'initial_congestion_window_size', - 'initial_receive_window_size', - 'dont_fragment_flag', - 'ip_tos', - 'time_to_live', - 'time_to_live_v4', - 'time_to_live_v6', - 'keep_alive_interval', - 'limited_transmit_recovery', - 'link_qos', - 'max_segment_retrans', - 'max_syn_retrans', - 'max_segment_size', - 'md5_signature', - 'minimum_rto', - 'multipath_tcp', - 'mptcp_checksum', - 'mptcp_checksum_verify', - 'mptcp_fallback', - 'mptcp_fast_join', - 'mptcp_idle_timeout', - 'mptcp_join_max', - 'mptcp_make_after_break', - 'mptcp_no_join_dss_ack', - 'mptcp_rto_max', - 'mptcp_retransmit_min', - 'mptcp_subflow_max', - 'mptcp_timeout', - 'nagle_algorithm', - 'pkt_loss_ignore_burst', - 'pkt_loss_ignore_rate', - 'proxy_buffer_high', - 'proxy_buffer_low', - 'proxy_max_segment', - 'proxy_options', - 'push_flag', - 'rate_pace', - 'rate_pace_max_rate', - 'receive_window', - 'reset_on_timeout', - 'retransmit_threshold', - 'selective_acks', - 'selective_nack', - 'send_buffer', - 'slow_start', - 'syn_cookie_enable', - 'syn_cookie_white_list', - 'syn_retrans_to_base', - 'tail_loss_probe', - 'time_wait_recycle', - 'time_wait', - 'timestamps', - 'verified_accept', - 'zero_window_timeout', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def time_wait(self): - if self._values['time_wait'] is None: - return None - if self._values['time_wait'] == 0: - return "immediate" - if self._values['time_wait'] == 4294967295: - return 'indefinite' - return self._values['time_wait'] - - @property - def close_wait(self): - if self._values['close_wait'] is None: - return None - if self._values['close_wait'] == 0: - return "immediate" - if self._values['close_wait'] == 4294967295: - return 'indefinite' - return self._values['close_wait'] - - @property - def fin_wait_1(self): - if self._values['fin_wait_1'] is None: - return None - if self._values['fin_wait_1'] == 0: - return "immediate" - if self._values['fin_wait_1'] == 4294967295: - return 'indefinite' - return self._values['fin_wait_1'] - - @property - def fin_wait_2(self): - if self._values['fin_wait_2'] is None: - return None - if self._values['fin_wait_2'] == 0: - return "immediate" - if self._values['fin_wait_2'] == 4294967295: - return 'indefinite' - return self._values['fin_wait_2'] - - @property - def zero_window_timeout(self): - if self._values['zero_window_timeout'] is None: - return None - if self._values['zero_window_timeout'] == 4294967295: - return 'indefinite' - return self._values['zero_window_timeout'] - - @property - def idle_timeout(self): - if self._values['idle_timeout'] is None: - return None - if self._values['idle_timeout'] == 4294967295: - return 'indefinite' - return self._values['idle_timeout'] - - @property - def keep_alive_interval(self): - if self._values['keep_alive_interval'] is None: - return None - if self._values['keep_alive_interval'] == 4294967295: - return 'indefinite' - return self._values['keep_alive_interval'] - - @property - def verified_accept(self): - return flatten_boolean(self._values['verified_accept']) - - @property - def timestamps(self): - return flatten_boolean(self._values['timestamps']) - - @property - def time_wait_recycle(self): - return flatten_boolean(self._values['time_wait_recycle']) - - @property - def tail_loss_probe(self): - return flatten_boolean(self._values['tail_loss_probe']) - - @property - def syn_cookie_white_list(self): - return flatten_boolean(self._values['syn_cookie_white_list']) - - @property - def syn_cookie_enable(self): - return flatten_boolean(self._values['syn_cookie_enable']) - - @property - def slow_start(self): - return flatten_boolean(self._values['slow_start']) - - @property - def selective_nack(self): - return flatten_boolean(self._values['selective_nack']) - - @property - def selective_acks(self): - return flatten_boolean(self._values['selective_acks']) - - @property - def reset_on_timeout(self): - return flatten_boolean(self._values['reset_on_timeout']) - - @property - def rate_pace(self): - return flatten_boolean(self._values['rate_pace']) - - @property - def proxy_options(self): - return flatten_boolean(self._values['proxy_options']) - - @property - def proxy_max_segment(self): - return flatten_boolean(self._values['proxy_max_segment']) - - @property - def nagle_algorithm(self): - return flatten_boolean(self._values['nagle_algorithm']) - - @property - def mptcp_no_join_dss_ack(self): - return flatten_boolean(self._values['mptcp_no_join_dss_ack']) - - @property - def mptcp_make_after_break(self): - return flatten_boolean(self._values['mptcp_make_after_break']) - - @property - def mptcp_fast_join(self): - return flatten_boolean(self._values['mptcp_fast_join']) - - @property - def mptcp_checksum_verify(self): - return flatten_boolean(self._values['mptcp_checksum_verify']) - - @property - def mptcp_checksum(self): - return flatten_boolean(self._values['mptcp_checksum']) - - @property - def multipath_tcp(self): - return flatten_boolean(self._values['multipath_tcp']) - - @property - def md5_signature(self): - return flatten_boolean(self._values['md5_signature']) - - @property - def limited_transmit_recovery(self): - return flatten_boolean(self._values['limited_transmit_recovery']) - - @property - def fast_open(self): - return flatten_boolean(self._values['fast_open']) - - @property - def enhanced_loss_recovery(self): - return flatten_boolean(self._values['enhanced_loss_recovery']) - - @property - def explicit_congestion_notification(self): - return flatten_boolean(self._values['explicit_congestion_notification']) - - @property - def early_retransmit(self): - return flatten_boolean(self._values['early_retransmit']) - - @property - def dsack(self): - return flatten_boolean(self._values['dsack']) - - @property - def delayed_acks(self): - return flatten_boolean(self._values['delayed_acks']) - - @property - def delay_window_control(self): - return flatten_boolean(self._values['delay_window_control']) - - @property - def deferred_accept(self): - return flatten_boolean(self._values['deferred_accept']) - - @property - def congestion_metrics_cache(self): - return flatten_boolean(self._values['congestion_metrics_cache']) - - @property - def auto_send_buffer(self): - return flatten_boolean(self._values['auto_send_buffer']) - - @property - def auto_receive_window(self): - return flatten_boolean(self._values['auto_receive_window']) - - @property - def auto_proxy_buffer(self): - return flatten_boolean(self._values['auto_proxy_buffer']) - - @property - def abc(self): - return flatten_boolean(self._values['abc']) - - @property - def ack_on_push(self): - return flatten_boolean(self._values['ack_on_push']) - - -class TcpProfilesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(TcpProfilesFactManager, self).__init__(**kwargs) - self.want = TcpProfilesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(tcp_profiles=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = TcpProfilesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/tcp".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class TrafficGroupsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'autoFailbackEnabled': 'auto_failback_enabled', - 'autoFailbackTime': 'auto_failback_time', - 'haLoadFactor': 'ha_load_factor', - 'haOrder': 'ha_order', - 'isFloating': 'is_floating', - 'mac': 'mac_masquerade_address' - } - - returnables = [ - 'full_path', - 'name', - 'description', - 'auto_failback_enabled', - 'auto_failback_time', - 'ha_load_factor', - 'ha_order', - 'is_floating', - 'mac_masquerade_address' - ] - - @property - def auto_failback_time(self): - if self._values['auto_failback_time'] is None: - return None - return int(self._values['auto_failback_time']) - - @property - def auto_failback_enabled(self): - if self._values['auto_failback_enabled'] is None: - return None - elif self._values['auto_failback_enabled'] == 'false': - # Yes, the REST API stores this as a string - return 'no' - return 'yes' - - @property - def is_floating(self): - if self._values['is_floating'] is None: - return None - elif self._values['is_floating'] == 'true': - # Yes, the REST API stores this as a string - return 'yes' - return 'no' - - @property - def mac_masquerade_address(self): - if self._values['mac_masquerade_address'] in [None, 'none']: - return None - return self._values['mac_masquerade_address'] - - -class TrafficGroupsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(TrafficGroupsFactManager, self).__init__(**kwargs) - self.want = TrafficGroupsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(traffic_groups=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource - attrs['stats'] = self.read_stats_from_device(attrs['fullPath']) - params = TrafficGroupsParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/cm/traffic-group".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - def read_stats_from_device(self, full_path): - uri = "https://{0}:{1}/mgmt/tm/cm/traffic-group/{2}/stats".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(name=full_path) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - try: - return result['stats'] - except KeyError: - return {} - - -class TrunksParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'media': 'media_speed', - 'lacpMode': 'lacp_mode', - 'lacp': 'lacp_state', - 'lacpTimeout': 'lacp_timeout', - 'stp': 'stp_enabled', - 'workingMbrCount': 'operational_member_count', - 'linkSelectPolicy': 'link_selection_policy', - 'distributionHash': 'distribution_hash', - 'cfgMbrCount': 'configured_member_count' - } - - returnables = [ - 'full_path', - 'name', - 'description', - 'media_speed', - 'lacp_mode', # 'active' or 'passive' - 'lacp_enabled', - 'stp_enabled', - 'operational_member_count', - 'media_status', - 'link_selection_policy', - 'lacp_timeout', - 'interfaces', - 'distribution_hash', - 'configured_member_count' - ] - - @property - def lacp_enabled(self): - if self._values['lacp_enabled'] is None: - return None - elif self._values['lacp_enabled'] == 'disabled': - return 'no' - return 'yes' - - @property - def stp_enabled(self): - if self._values['stp_enabled'] is None: - return None - elif self._values['stp_enabled'] == 'disabled': - return 'no' - return 'yes' - - @property - def media_status(self): - return self._values['stats']['status'] - - -class TrunksFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(TrunksFactManager, self).__init__(**kwargs) - self.want = TrunksParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(trunks=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource - attrs['stats'] = self.read_stats_from_device(attrs['fullPath']) - params = TrunksParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/net/trunk".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - def read_stats_from_device(self, full_path): - uri = "https://{0}:{1}/mgmt/tm/net/trunk/{2}/stats".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(name=full_path) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - try: - return result['stats'] - except KeyError: - return {} - - -class UsersParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'partitionAccess': 'partition_access', - } - - returnables = [ - 'full_path', - 'name', - 'description', - 'partition_access', - 'shell', - ] - - @property - def partition_access(self): - result = [] - if self._values['partition_access'] is None: - return [] - for partition in self._values['partition_access']: - del partition['nameReference'] - result.append(partition) - return result - - @property - def shell(self): - if self._values['shell'] in [None, 'none']: - return None - return self._values['shell'] - - -class UsersFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(UsersFactManager, self).__init__(**kwargs) - self.want = UsersParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(users=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource - params = UsersParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/auth/user".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class UdpProfilesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'allowNoPayload': 'allow_no_payload', - 'bufferMaxBytes': 'buffer_max_bytes', - 'bufferMaxPackets': 'buffer_max_packets', - 'datagramLoadBalancing': 'datagram_load_balancing', - 'defaultsFrom': 'parent', - 'idleTimeout': 'idle_timeout', - 'ipDfMode': 'ip_df_mode', - 'ipTosToClient': 'ip_tos_to_client', - 'ipTtlMode': 'ip_ttl_mode', - 'ipTtlV4': 'ip_ttl_v4', - 'ipTtlV6': 'ip_ttl_v6', - 'linkQosToClient': 'link_qos_to_client', - 'noChecksum': 'no_checksum', - 'proxyMss': 'proxy_mss', - } - - returnables = [ - 'full_path', - 'name', - 'parent', - 'description', - 'allow_no_payload', - 'buffer_max_bytes', - 'buffer_max_packets', - 'datagram_load_balancing', - 'idle_timeout', - 'ip_df_mode', - 'ip_tos_to_client', - 'ip_ttl_mode', - 'ip_ttl_v4', - 'ip_ttl_v6', - 'link_qos_to_client', - 'no_checksum', - 'proxy_mss', - ] - - @property - def description(self): - if self._values['description'] in [None, 'none']: - return None - return self._values['description'] - - @property - def allow_no_payload(self): - return flatten_boolean(self._values['allow_no_payload']) - - @property - def datagram_load_balancing(self): - return flatten_boolean(self._values['datagram_load_balancing']) - - @property - def proxy_mss(self): - return flatten_boolean(self._values['proxy_mss']) - - @property - def no_checksum(self): - return flatten_boolean(self._values['no_checksum']) - - -class UdpProfilesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(UdpProfilesFactManager, self).__init__(**kwargs) - self.want = UdpProfilesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(udp_profiles=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = UdpProfilesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/udp".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class VcmpGuestsParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'allowedSlots': 'allowed_slots', - 'assignedSlots': 'assigned_slots', - 'bootPriority': 'boot_priority', - 'coresPerSlot': 'cores_per_slot', - 'initialImage': 'initial_image', - 'initialHotfix': 'hotfix_image', - 'managementGw': 'mgmt_route', - 'managementIp': 'mgmt_address', - 'managementNetwork': 'mgmt_network', - 'minSlots': 'min_number_of_slots', - 'slots': 'number_of_slots', - 'sslMode': 'ssl_mode', - 'virtualDisk': 'virtual_disk' - } - - returnables = [ - 'name', - 'full_path', - 'allowed_slots', - 'assigned_slots', - 'boot_priority', - 'cores_per_slot', - 'hostname', - 'hotfix_image', - 'initial_image', - 'mgmt_route', - 'mgmt_address', - 'mgmt_network', - 'vlans', - 'min_number_of_slots', - 'number_of_slots', - 'ssl_mode', - 'state', - 'virtual_disk', - ] - - -class VcmpGuestsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(VcmpGuestsFactManager, self).__init__(**kwargs) - self.want = VcmpGuestsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(vcmp_guests=facts) - return result - - def _exec_module(self): - if 'vcmp' not in self.provisioned_modules: - return [] - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = VcmpGuestsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/vcmp/guest".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class VirtualAddressesParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'arp': 'arp_enabled', - 'autoDelete': 'auto_delete_enabled', - 'connectionLimit': 'connection_limit', - 'icmpEcho': 'icmp_echo', - 'mask': 'netmask', - 'routeAdvertisement': 'route_advertisement', - 'trafficGroup': 'traffic_group', - 'inheritedTrafficGroup': 'inherited_traffic_group' - } - - returnables = [ - 'full_path', - 'name', - 'address', - 'arp_enabled', - 'auto_delete_enabled', - 'connection_limit', - 'description', - 'enabled', - 'icmp_echo', - 'floating', - 'netmask', - 'route_advertisement', - 'traffic_group', - 'spanning', - 'inherited_traffic_group' - ] - - @property - def spanning(self): - return flatten_boolean(self._values['spanning']) - - @property - def arp_enabled(self): - return flatten_boolean(self._values['arp_enabled']) - - @property - def route_advertisement(self): - return flatten_boolean(self._values['route_advertisement']) - - @property - def auto_delete_enabled(self): - return flatten_boolean(self._values['auto_delete_enabled']) - - @property - def inherited_traffic_group(self): - return flatten_boolean(self._values['inherited_traffic_group']) - - @property - def icmp_echo(self): - return flatten_boolean(self._values['icmp_echo']) - - @property - def floating(self): - return flatten_boolean(self._values['floating']) - - @property - def enabled(self): - return flatten_boolean(self._values['enabled']) - - -class VirtualAddressesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(VirtualAddressesFactManager, self).__init__(**kwargs) - self.want = VirtualAddressesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(virtual_addresses=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = VirtualAddressesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/virtual-address".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class VirtualServersParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'autoLasthop': 'auto_lasthop', - 'bwcPolicy': 'bw_controller_policy', - 'cmpEnabled': 'cmp_enabled', - 'connectionLimit': 'connection_limit', - 'fallbackPersistence': 'fallback_persistence_profile', - 'persist': 'persistence_profile', - 'translatePort': 'translate_port', - 'translateAddress': 'translate_address', - 'lastHopPool': 'last_hop_pool', - 'nat64': 'nat64_enabled', - 'sourcePort': 'source_port_behavior', - 'ipIntelligencePolicy': 'ip_intelligence_policy', - 'ipProtocol': 'protocol', - 'pool': 'default_pool', - 'rateLimitMode': 'rate_limit_mode', - 'rateLimitSrcMask': 'rate_limit_source_mask', - 'rateLimitDstMask': 'rate_limit_destination_mask', - 'rateLimit': 'rate_limit', - 'sourceAddressTranslation': 'snat_type', - 'gtmScore': 'gtm_score', - 'rateClass': 'rate_class', - 'source': 'source_address', - 'auth': 'authentication_profile', - 'mirror': 'connection_mirror_enabled', - 'rules': 'irules', - 'securityLogProfiles': 'security_log_profiles', - 'profilesReference': 'profiles' - } - - returnables = [ - 'full_path', - 'name', - 'auto_lasthop', - 'bw_controller_policy', - 'cmp_enabled', - 'connection_limit', - 'description', - 'enabled', - 'fallback_persistence_profile', - 'persistence_profile', - 'translate_port', - 'translate_address', - 'vlans', - 'destination', - 'last_hop_pool', - 'nat64_enabled', - 'source_port_behavior', - 'ip_intelligence_policy', - 'protocol', - 'default_pool', - 'rate_limit_mode', - 'rate_limit_source_mask', - 'rate_limit', - 'snat_type', - 'snat_pool', - 'gtm_score', - 'rate_class', - 'rate_limit_destination_mask', - 'source_address', - 'authentication_profile', - 'connection_mirror_enabled', - 'irules', - 'security_log_profiles', - 'type', - 'profiles', - 'destination_address', - 'destination_port', - 'availability_status', - 'status_reason', - 'total_requests', - 'client_side_bits_in', - 'client_side_bits_out', - 'client_side_current_connections', - 'client_side_evicted_connections', - 'client_side_max_connections', - 'client_side_pkts_in', - 'client_side_pkts_out', - 'client_side_slow_killed', - 'client_side_total_connections', - 'cmp_mode', - 'ephemeral_bits_in', - 'ephemeral_bits_out', - 'ephemeral_current_connections', - 'ephemeral_evicted_connections', - 'ephemeral_max_connections', - 'ephemeral_pkts_in', - 'ephemeral_pkts_out', - 'ephemeral_slow_killed', - 'ephemeral_total_connections', - 'total_software_accepted_syn_cookies', - 'total_hardware_accepted_syn_cookies', - 'total_hardware_syn_cookies', - 'hardware_syn_cookie_instances', - 'total_software_rejected_syn_cookies', - 'software_syn_cookie_instances', - 'current_syn_cache', - 'syn_cache_overflow', - 'total_software_syn_cookies', - 'syn_cookies_status', - 'max_conn_duration', - 'mean_conn_duration', - 'min_conn_duration', - 'cpu_usage_ratio_last_5_min', - 'cpu_usage_ratio_last_5_sec', - 'cpu_usage_ratio_last_1_min', - ] - - @property - def max_conn_duration(self): - return self._values['stats']['csMaxConnDur'] - - @property - def mean_conn_duration(self): - return self._values['stats']['csMeanConnDur'] - - @property - def min_conn_duration(self): - return self._values['stats']['csMinConnDur'] - - @property - def cpu_usage_ratio_last_5_min(self): - return self._values['stats']['fiveMinAvgUsageRatio'] - - @property - def cpu_usage_ratio_last_5_sec(self): - return self._values['stats']['fiveSecAvgUsageRatio'] - - @property - def cpu_usage_ratio_last_1_min(self): - return self._values['stats']['oneMinAvgUsageRatio'] - - @property - def cmp_mode(self): - return self._values['stats']['cmpEnableMode'] - - @property - def availability_status(self): - return self._values['stats']['status']['availabilityState'] - - @property - def status_reason(self): - return self._values['stats']['status']['statusReason'] - - @property - def total_requests(self): - return self._values['stats']['totRequests'] - - @property - def ephemeral_bits_in(self): - return self._values['stats']['ephemeral']['bitsIn'] - - @property - def ephemeral_bits_out(self): - return self._values['stats']['ephemeral']['bitsOut'] - - @property - def ephemeral_current_connections(self): - return self._values['stats']['ephemeral']['curConns'] - - @property - def ephemeral_evicted_connections(self): - return self._values['stats']['ephemeral']['evictedConns'] - - @property - def ephemeral_max_connections(self): - return self._values['stats']['ephemeral']['maxConns'] - - @property - def ephemeral_pkts_in(self): - return self._values['stats']['ephemeral']['pktsIn'] - - @property - def ephemeral_pkts_out(self): - return self._values['stats']['ephemeral']['pktsOut'] - - @property - def ephemeral_slow_killed(self): - return self._values['stats']['ephemeral']['slowKilled'] - - @property - def ephemeral_total_connections(self): - return self._values['stats']['ephemeral']['totConns'] - - @property - def client_side_bits_in(self): - return self._values['stats']['clientside']['bitsIn'] - - @property - def client_side_bits_out(self): - return self._values['stats']['clientside']['bitsOut'] - - @property - def client_side_current_connections(self): - return self._values['stats']['clientside']['curConns'] - - @property - def client_side_evicted_connections(self): - return self._values['stats']['clientside']['evictedConns'] - - @property - def client_side_max_connections(self): - return self._values['stats']['clientside']['maxConns'] - - @property - def client_side_pkts_in(self): - return self._values['stats']['clientside']['pktsIn'] - - @property - def client_side_pkts_out(self): - return self._values['stats']['clientside']['pktsOut'] - - @property - def client_side_slow_killed(self): - return self._values['stats']['clientside']['slowKilled'] - - @property - def client_side_total_connections(self): - return self._values['stats']['clientside']['totConns'] - - @property - def total_software_accepted_syn_cookies(self): - return self._values['stats']['syncookie']['accepts'] - - @property - def total_hardware_accepted_syn_cookies(self): - return self._values['stats']['syncookie']['hwAccepts'] - - @property - def total_hardware_syn_cookies(self): - return self._values['stats']['syncookie']['hwSyncookies'] - - @property - def hardware_syn_cookie_instances(self): - return self._values['stats']['syncookie']['hwsyncookieInstance'] - - @property - def total_software_rejected_syn_cookies(self): - return self._values['stats']['syncookie']['rejects'] - - @property - def software_syn_cookie_instances(self): - return self._values['stats']['syncookie']['swsyncookieInstance'] - - @property - def current_syn_cache(self): - return self._values['stats']['syncookie']['syncacheCurr'] - - @property - def syn_cache_overflow(self): - return self._values['stats']['syncookie']['syncacheOver'] - - @property - def total_software_syn_cookies(self): - return self._values['stats']['syncookie']['syncookies'] - - @property - def syn_cookies_status(self): - return self._values['stats']['syncookieStatus'] - - @property - def destination_address(self): - if self._values['destination'] is None: - return None - tup = self.destination_tuple - return tup.ip - - @property - def destination_port(self): - if self._values['destination'] is None: - return None - tup = self.destination_tuple - return tup.port - - @property - def type(self): - """Attempt to determine the current server type - - This check is very unscientific. It turns out that this information is not - exactly available anywhere on a BIG-IP. Instead, we rely on a semi-reliable - means for determining what the type of the virtual server is. Hopefully it - always works. - - There are a handful of attributes that can be used to determine a specific - type. There are some types though that can only be determined by looking at - the profiles that are assigned to them. We follow that method for those - complicated types; message-routing, fasthttp, and fastl4. - - Because type determination is an expensive operation, we cache the result - from the operation. - - Returns: - string: The server type. - """ - if self._values['l2Forward'] is True: - result = 'forwarding-l2' - elif self._values['ipForward'] is True: - result = 'forwarding-ip' - elif self._values['stateless'] is True: - result = 'stateless' - elif self._values['reject'] is True: - result = 'reject' - elif self._values['dhcpRelay'] is True: - result = 'dhcp' - elif self._values['internal'] is True: - result = 'internal' - elif self.has_fasthttp_profiles: - result = 'performance-http' - elif self.has_fastl4_profiles: - result = 'performance-l4' - elif self.has_message_routing_profiles: - result = 'message-routing' - else: - result = 'standard' - return result - - @property - def profiles(self): - """Returns a list of profiles from the API - - The profiles are formatted so that they are usable in this module and - are able to be compared by the Difference engine. - - Returns: - list (:obj:`list` of :obj:`dict`): List of profiles. - - Each dictionary in the list contains the following three (3) keys. - - * name - * context - * fullPath - - Raises: - F5ModuleError: If the specified context is a value other that - ``all``, ``server-side``, or ``client-side``. - """ - if 'items' not in self._values['profiles']: - return None - result = [] - for item in self._values['profiles']['items']: - context = item['context'] - if context == 'serverside': - context = 'server-side' - elif context == 'clientside': - context = 'client-side' - name = item['name'] - if context in ['all', 'server-side', 'client-side']: - result.append(dict(name=name, context=context, full_path=item['fullPath'])) - else: - raise F5ModuleError( - "Unknown profile context found: '{0}'".format(context) - ) - return result - - @property - def has_message_routing_profiles(self): - if self.profiles is None: - return None - current = self._read_current_message_routing_profiles_from_device() - result = [x['name'] for x in self.profiles if x['name'] in current] - if len(result) > 0: - return True - return False - - @property - def has_fastl4_profiles(self): - if self.profiles is None: - return None - current = self._read_current_fastl4_profiles_from_device() - result = [x['name'] for x in self.profiles if x['name'] in current] - if len(result) > 0: - return True - return False - - @property - def has_fasthttp_profiles(self): - """Check if ``fasthttp`` profile is in API profiles - - This method is used to determine the server type when doing comparisons - in the Difference class. - - Returns: - bool: True if server has ``fasthttp`` profiles. False otherwise. - """ - if self.profiles is None: - return None - current = self._read_current_fasthttp_profiles_from_device() - result = [x['name'] for x in self.profiles if x['name'] in current] - if len(result) > 0: - return True - return False - - def _read_current_message_routing_profiles_from_device(self): - result = [] - result += self._read_diameter_profiles_from_device() - result += self._read_sip_profiles_from_device() - return result - - def _read_diameter_profiles_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/diameter/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = [x['name'] for x in response['items']] - return result - - def _read_sip_profiles_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/sip/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = [x['name'] for x in response['items']] - return result - - def _read_current_fastl4_profiles_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/fastl4/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = [x['name'] for x in response['items']] - return result - - def _read_current_fasthttp_profiles_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/profile/fasthttp/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = [x['name'] for x in response['items']] - return result - - @property - def security_log_profiles(self): - if self._values['security_log_profiles'] is None: - return None - result = list(set([x.strip('"') for x in self._values['security_log_profiles']])) - result.sort() - return result - - @property - def snat_type(self): - if self._values['snat_type'] is None: - return None - if 'type' in self._values['snat_type']: - if self._values['snat_type']['type'] == 'automap': - return 'automap' - elif self._values['snat_type']['type'] == 'none': - return 'none' - elif self._values['snat_type']['type'] == 'pool': - return 'snat' - - @property - def connection_mirror_enabled(self): - if self._values['connection_mirror_enabled'] is None: - return None - elif self._values['connection_mirror_enabled'] == 'enabled': - return 'yes' - return 'no' - - @property - def rate_limit(self): - if self._values['rate_limit'] is None: - return None - elif self._values['rate_limit'] == 'disabled': - return -1 - return int(self._values['rate_limit']) - - @property - def nat64_enabled(self): - if self._values['nat64_enabled'] is None: - return None - elif self._values['nat64_enabled'] == 'enabled': - return 'yes' - return 'no' - - @property - def enabled(self): - if self._values['enabled'] is None: - return 'no' - elif self._values['enabled'] is True: - return 'yes' - return 'no' - - @property - def translate_port(self): - if self._values['translate_port'] is None: - return None - elif self._values['translate_port'] == 'enabled': - return 'yes' - return 'no' - - @property - def translate_address(self): - if self._values['translate_address'] is None: - return None - elif self._values['translate_address'] == 'enabled': - return 'yes' - return 'no' - - @property - def persistence_profile(self): - """Return persistence profile in a consumable form - - I don't know why the persistence profile is stored this way, but below is the - general format of it. - - "persist": [ - { - "name": "msrdp", - "partition": "Common", - "tmDefault": "yes", - "nameReference": { - "link": "https://localhost/mgmt/tm/ltm/persistence/msrdp/~Common~msrdp?ver=13.1.0.4" - } - } - ], - - As you can see, this is quite different from something like the fallback - persistence profile which is just simply - - /Common/fallback1 - - This method makes the persistence profile look like the fallback profile. - - Returns: - string: The persistence profile configured on the virtual. - """ - if self._values['persistence_profile'] is None: - return None - profile = self._values['persistence_profile'][0] - result = fq_name(profile['partition'], profile['name']) - return result - - @property - def destination_tuple(self): - Destination = namedtuple('Destination', ['ip', 'port', 'route_domain']) - - # Remove the partition - if self._values['destination'] is None: - result = Destination(ip=None, port=None, route_domain=None) - return result - destination = re.sub(r'^/[a-zA-Z0-9_.-]+/', '', self._values['destination']) - - if is_valid_ip(destination): - result = Destination( - ip=destination, - port=None, - route_domain=None - ) - return result - - # Covers the following examples - # - # /Common/2700:bc00:1f10:101::6%2.80 - # 2700:bc00:1f10:101::6%2.80 - # 1.1.1.1%2:80 - # /Common/1.1.1.1%2:80 - # /Common/2700:bc00:1f10:101::6%2.any - # - pattern = r'(?P[^%]+)%(?P[0-9]+)[:.](?P[0-9]+|any)' - matches = re.search(pattern, destination) - if matches: - try: - port = int(matches.group('port')) - except ValueError: - # Can be a port of "any". This only happens with IPv6 - port = matches.group('port') - if port == 'any': - port = 0 - ip = matches.group('ip') - if not is_valid_ip(ip): - raise F5ModuleError( - "The provided destination is not a valid IP address" - ) - result = Destination( - ip=matches.group('ip'), - port=port, - route_domain=int(matches.group('route_domain')) - ) - return result - - pattern = r'(?P[^%]+)%(?P[0-9]+)' - matches = re.search(pattern, destination) - if matches: - ip = matches.group('ip') - if not is_valid_ip(ip): - raise F5ModuleError( - "The provided destination is not a valid IP address" - ) - result = Destination( - ip=matches.group('ip'), - port=None, - route_domain=int(matches.group('route_domain')) - ) - return result - - parts = destination.split('.') - if len(parts) == 4: - # IPv4 - ip, port = destination.split(':') - if not is_valid_ip(ip): - raise F5ModuleError( - "The provided destination is not a valid IP address" - ) - result = Destination( - ip=ip, - port=int(port), - route_domain=None - ) - return result - elif len(parts) == 2: - # IPv6 - ip, port = destination.split('.') - try: - port = int(port) - except ValueError: - # Can be a port of "any". This only happens with IPv6 - if port == 'any': - port = 0 - if not is_valid_ip(ip): - raise F5ModuleError( - "The provided destination is not a valid IP address" - ) - result = Destination( - ip=ip, - port=port, - route_domain=None - ) - return result - else: - result = Destination(ip=None, port=None, route_domain=None) - return result - - -class VirtualServersFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(VirtualServersFactManager, self).__init__(**kwargs) - self.want = VirtualServersParameters(client=self.client, params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(virtual_servers=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource - attrs['stats'] = self.read_stats_from_device(attrs['fullPath']) - params = VirtualServersParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/ltm/virtual?expandSubcollections=true".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - def read_stats_from_device(self, full_path): - uri = "https://{0}:{1}/mgmt/tm/ltm/virtual/{2}/stats".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(name=full_path) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - try: - return result['stats'] - except KeyError: - return {} - - -class VlansParameters(BaseParameters): - api_map = { - 'autoLasthop': 'auto_lasthop', - 'cmpHash': 'cmp_hash_algorithm', - 'failsafeAction': 'failsafe_action', - 'failsafe': 'failsafe_enabled', - 'failsafeTimeout': 'failsafe_timeout', - 'ifIndex': 'if_index', - 'learning': 'learning_mode', - 'interfacesReference': 'interfaces', - 'sourceChecking': 'source_check_enabled', - 'fullPath': 'full_path' - } - - returnables = [ - 'full_path', - 'name', - 'auto_lasthop', - 'cmp_hash_algorithm', - 'description', - 'failsafe_action', - 'failsafe_enabled', - 'failsafe_timeout', - 'if_index', - 'learning_mode', - 'interfaces', - 'mtu', - 'sflow_poll_interval', - 'sflow_poll_interval_global', - 'sflow_sampling_rate', - 'sflow_sampling_rate_global', - 'source_check_enabled', - 'true_mac_address', - 'tag', - ] - - @property - def interfaces(self): - if self._values['interfaces'] is None: - return None - if 'items' not in self._values['interfaces']: - return None - result = [] - for item in self._values['interfaces']['items']: - tmp = dict( - name=item['name'], - full_path=item['fullPath'] - ) - if 'tagged' in item: - tmp['tagged'] = 'yes' - else: - tmp['tagged'] = 'no' - result.append(tmp) - return result - - @property - def sflow_poll_interval(self): - return int(self._values['sflow']['pollInterval']) - - @property - def sflow_poll_interval_global(self): - return flatten_boolean(self._values['sflow']['pollIntervalGlobal']) - - @property - def sflow_sampling_rate(self): - return int(self._values['sflow']['samplingRate']) - - @property - def sflow_sampling_rate_global(self): - return flatten_boolean(self._values['sflow']['samplingRateGlobal']) - - @property - def source_check_state(self): - return flatten_boolean(self._values['source_check_state']) - - @property - def true_mac_address(self): - # Who made this field a "description"!? - return self._values['stats']['macTrue'] - - @property - def tag(self): - # We can't agree on field names...SMH - return self._values['stats']['id'] - - @property - def failsafe_enabled(self): - return flatten_boolean(self._values['failsafe_enabled']) - - -class VlansFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(VlansFactManager, self).__init__(**kwargs) - self.want = VlansParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(vlans=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource - attrs['stats'] = self.read_stats_from_device(attrs['fullPath']) - params = VlansParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/net/vlan?expandSubcollections=true".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - def read_stats_from_device(self, full_path): - uri = "https://{0}:{1}/mgmt/tm/net/vlan/{2}/stats".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(name=full_path) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - try: - return result['stats'] - except KeyError: - return {} - - -class ModuleManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = kwargs.get('client', None) - self.kwargs = kwargs - self.want = Parameters(params=self.module.params) - self.managers = { - 'asm-policy-stats': AsmPolicyStatsFactManager, - 'asm-policies': AsmPolicyFactManager, - 'asm-server-technologies': AsmServerTechnologyFactManager, - 'asm-signature-sets': AsmSignatureSetsFactManager, - 'client-ssl-profiles': ClientSslProfilesFactManager, - 'devices': DevicesFactManager, - 'device-groups': DeviceGroupsFactManager, - 'external-monitors': ExternalMonitorsFactManager, - 'fasthttp-profiles': FastHttpProfilesFactManager, - 'fastl4-profiles': FastL4ProfilesFactManager, - 'gateway-icmp-monitors': GatewayIcmpMonitorsFactManager, - 'gtm-a-pools': GtmAPoolsFactManager, - 'gtm-servers': GtmServersFactManager, - 'gtm-a-wide-ips': GtmAWideIpsFactManager, - 'gtm-aaaa-pools': GtmAaaaPoolsFactManager, - 'gtm-aaaa-wide-ips': GtmAaaaWideIpsFactManager, - 'gtm-cname-pools': GtmCnamePoolsFactManager, - 'gtm-cname-wide-ips': GtmCnameWideIpsFactManager, - 'gtm-mx-pools': GtmMxPoolsFactManager, - 'gtm-mx-wide-ips': GtmMxWideIpsFactManager, - 'gtm-naptr-pools': GtmNaptrPoolsFactManager, - 'gtm-naptr-wide-ips': GtmNaptrWideIpsFactManager, - 'gtm-srv-pools': GtmSrvPoolsFactManager, - 'gtm-srv-wide-ips': GtmSrvWideIpsFactManager, - 'http-monitors': HttpMonitorsFactManager, - 'https-monitors': HttpsMonitorsFactManager, - 'http-profiles': HttpProfilesFactManager, - 'iapp-services': IappServicesFactManager, - 'iapplx-packages': IapplxPackagesFactManager, - 'icmp-monitors': IcmpMonitorsFactManager, - 'interfaces': InterfacesFactManager, - 'internal-data-groups': InternalDataGroupsFactManager, - 'irules': IrulesFactManager, - 'ltm-pools': LtmPoolsFactManager, - 'ltm-policies': LtmPolicyFactManager, - 'nodes': NodesFactManager, - 'oneconnect-profiles': OneConnectProfilesFactManager, - 'partitions': PartitionFactManager, - 'provision-info': ProvisionInfoFactManager, - 'route-domains': RouteDomainFactManager, - 'self-ips': SelfIpsFactManager, - 'server-ssl-profiles': ServerSslProfilesFactManager, - 'software-volumes': SoftwareVolumesFactManager, - 'software-images': SoftwareImagesFactManager, - 'software-hotfixes': SoftwareHotfixesFactManager, - 'ssl-certs': SslCertificatesFactManager, - 'ssl-keys': SslKeysFactManager, - 'system-db': SystemDbFactManager, - 'system-info': SystemInfoFactManager, - 'tcp-monitors': TcpMonitorsFactManager, - 'tcp-half-open-monitors': TcpHalfOpenMonitorsFactManager, - 'tcp-profiles': TcpProfilesFactManager, - 'traffic-groups': TrafficGroupsFactManager, - 'trunks': TrunksFactManager, - 'udp-profiles': UdpProfilesFactManager, - 'users': UsersFactManager, - 'vcmp-guests': VcmpGuestsFactManager, - 'virtual-addresses': VirtualAddressesFactManager, - 'virtual-servers': VirtualServersFactManager, - 'vlans': VlansFactManager, - } - - def exec_module(self): - self.handle_all_keyword() - self.handle_profiles_keyword() - self.handle_monitors_keyword() - self.handle_gtm_pools_keyword() - self.handle_gtm_wide_ips_keyword() - res = self.check_valid_gather_subset(self.want.gather_subset) - if res: - invalid = ','.join(res) - raise F5ModuleError( - "The specified 'gather_subset' options are invalid: {0}".format(invalid) - ) - result = self.filter_excluded_facts() - - managers = [] - for name in result: - manager = self.get_manager(name) - if manager: - managers.append(manager) - - if not managers: - result = dict( - queried=False - ) - return result - - result = self.execute_managers(managers) - if result: - result['queried'] = True - else: - result['queried'] = False - return result - - def filter_excluded_facts(self): - # Remove the excluded entries from the list of possible facts - exclude = [x[1:] for x in self.want.gather_subset if x[0] == '!'] - include = [x for x in self.want.gather_subset if x[0] != '!'] - result = [x for x in include if x not in exclude] - return result - - def handle_all_keyword(self): - if 'all' not in self.want.gather_subset: - return - managers = list(self.managers.keys()) + self.want.gather_subset - managers.remove('all') - self.want.update({'gather_subset': managers}) - - def handle_profiles_keyword(self): - if 'profiles' not in self.want.gather_subset: - return - managers = [x for x in self.managers.keys() if '-profiles' in x] + self.want.gather_subset - managers.remove('profiles') - self.want.update({'gather_subset': managers}) - - def handle_monitors_keyword(self): - if 'monitors' not in self.want.gather_subset: - return - managers = [x for x in self.managers.keys() if '-monitors' in x] + self.want.gather_subset - managers.remove('monitors') - self.want.update({'gather_subset': managers}) - - def handle_gtm_pools_keyword(self): - if 'gtm-pools' not in self.want.gather_subset: - return - keys = self.managers.keys() - managers = [x for x in keys if x.startswith('gtm-') and x.endswith('-pools')] - managers += self.want.gather_subset - managers.remove('gtm-pools') - self.want.update({'gather_subset': managers}) - - def handle_gtm_wide_ips_keyword(self): - if 'gtm-wide-ips' not in self.want.gather_subset: - return - keys = self.managers.keys() - managers = [x for x in keys if x.startswith('gtm-') and x.endswith('-wide-ips')] - managers += self.want.gather_subset - managers.remove('gtm-wide-ips') - self.want.update({'gather_subset': managers}) - - def check_valid_gather_subset(self, includes): - """Check that the specified subset is valid - - The ``gather_subset`` parameter is specified as a "raw" field which means that - any Python type could technically be provided - - :param includes: - :return: - """ - keys = self.managers.keys() - result = [] - for x in includes: - if x not in keys: - if x[0] == '!': - if x[1:] not in keys: - result.append(x) - else: - result.append(x) - return result - - def execute_managers(self, managers): - results = dict() - client = F5RestClient(**self.module.params) - prov = modules_provisioned(client) - for manager in managers: - manager.provisioned_modules = prov - result = manager.exec_module() - results.update(result) - return results - - def get_manager(self, which): - result = {} - manager = self.managers.get(which, None) - if not manager: - return result - kwargs = dict() - kwargs.update(self.kwargs) - - kwargs['client'] = F5RestClient(**self.module.params) - result = manager(**kwargs) - return result - - -class ArgumentSpec(object): - def __init__(self): - self.supports_check_mode = False - argument_spec = dict( - gather_subset=dict( - type='list', - required=True, - aliases=['include'], - choices=[ - # Meta choices - 'all', - 'monitors', - 'profiles', - 'gtm-pools', - 'gtm-wide-ips', - - # Non-meta choices - 'asm-policies', - 'asm-policy-stats', - 'asm-server-technologies', - 'asm-signature-sets', - 'client-ssl-profiles', - 'devices', - 'device-groups', - 'external-monitors', - 'fasthttp-profiles', - 'fastl4-profiles', - 'gateway-icmp-monitors', - 'gtm-a-pools', - 'gtm-servers', - 'gtm-a-wide-ips', - 'gtm-aaaa-pools', - 'gtm-aaaa-wide-ips', - 'gtm-cname-pools', - 'gtm-cname-wide-ips', - 'gtm-mx-pools', - 'gtm-mx-wide-ips', - 'gtm-naptr-pools', - 'gtm-naptr-wide-ips', - 'gtm-srv-pools', - 'gtm-srv-wide-ips', - 'http-profiles', - 'http-monitors', - 'https-monitors', - 'iapp-services', - 'iapplx-packages', - 'icmp-monitors', - 'interfaces', - 'internal-data-groups', - 'irules', - 'ltm-pools', - 'ltm-policies', - 'nodes', - 'oneconnect-profiles', - 'partitions', - 'provision-info', - 'self-ips', - 'server-ssl-profiles', - 'software-volumes', - 'software-images', - 'software-hotfixes', - 'ssl-certs', - 'ssl-keys', - 'system-db', - 'system-info', - 'tcp-monitors', - 'tcp-half-open-monitors', - 'tcp-profiles', - 'traffic-groups', - 'trunks', - 'udp-profiles', - 'users', - 'vcmp-guests', - 'virtual-addresses', - 'virtual-servers', - 'vlans', - - # Negations of meta choices - '!all', - "!monitors", - '!profiles', - '!gtm-pools', - '!gtm-wide-ips', - - # Negations of non-meta-choices - '!asm-policy-stats', - '!asm-policies', - '!asm-server-technologies', - '!asm-signature-sets', - '!client-ssl-profiles', - '!devices', - '!device-groups', - '!external-monitors', - '!fasthttp-profiles', - '!fastl4-profiles', - '!gateway-icmp-monitors', - '!gtm-a-pools', - '!gtm-servers', - '!gtm-a-wide-ips', - '!gtm-aaaa-pools', - '!gtm-aaaa-wide-ips', - '!gtm-cname-pools', - '!gtm-cname-wide-ips', - '!gtm-mx-pools', - '!gtm-mx-wide-ips', - '!gtm-naptr-pools', - '!gtm-naptr-wide-ips', - '!gtm-srv-pools', - '!gtm-srv-wide-ips', - '!http-profiles', - '!http-monitors', - '!https-monitors', - '!iapp-services', - '!iapplx-packages', - '!icmp-monitors', - '!interfaces', - '!internal-data-groups', - '!irules', - '!ltm-pools', - '!ltm-policies', - '!nodes', - '!oneconnect-profiles', - '!partitions', - '!provision-info', - '!self-ips', - '!server-ssl-profiles', - '!software-volumes', - '!software-images', - '!software-hotfixes', - '!ssl-certs', - '!ssl-keys', - '!system-db', - '!system-info', - '!tcp-monitors', - '!tcp-half-open-monitors', - '!tcp-profiles', - '!traffic-groups', - '!trunks', - '!udp-profiles', - '!users', - '!vcmp-guests', - '!virtual-addresses', - '!virtual-servers', - '!vlans', - ] - ), - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec, - supports_check_mode=spec.supports_check_mode - ) - if module._name == 'bigip_device_facts': - module.deprecate("The 'bigip_device_facts' module has been renamed to 'bigip_device_info'", version='2.13') - - try: - mm = ModuleManager(module=module) - results = mm.exec_module() - module.exit_json(**results) - except F5ModuleError as ex: - module.fail_json(msg=str(ex)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_device_traffic_group.py b/plugins/modules/network/f5/bigip_device_traffic_group.py deleted file mode 100644 index c5d459fb99..0000000000 --- a/plugins/modules/network/f5/bigip_device_traffic_group.py +++ /dev/null @@ -1,667 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2017, F5 Networks 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': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigip_device_traffic_group -short_description: Manages traffic groups on BIG-IP -description: - - Supports managing traffic groups and their attributes on a BIG-IP. -options: - name: - description: - - The name of the traffic group. - type: str - required: True - mac_address: - description: - - Specifies the floating Media Access Control (MAC) address associated with the floating IP addresses - defined for a traffic group. - - Primarily, a MAC masquerade address minimizes ARP communications or dropped packets as a result of failover. - - A MAC masquerade address ensures that any traffic destined for a specific traffic group reaches an available - device after failover, which happens because along with the traffic group, the MAC masquerade address floats - to the available device. - - Without a MAC masquerade address, the sending host must learn the MAC address for a newly-active device, - either by sending an ARP request or by relying on the gratuitous ARP from the newly-active device. - - To unset the MAC address, specify an empty value (C("")) to this parameter. - type: str - ha_order: - description: - - Specifies order in which you would like to assign devices for failover. - - If you configure this setting, you must configure the setting on every traffic group in the device group. - - The values should be device names of the devices that belong to the failover group configured beforehand. - - The order in which the devices are placed as arguments to this parameter, determines their HA order - on the device, in other words changing the order of the same elements will cause a change on the unit. - - To disable an HA order failover method , specify an empty string value (C("")) to this parameter. - - Disabling HA order will revert the device back to using Load Aware method as it is the default, - unless C(ha_group) setting is also configured. - - Device names will be prepended by a partition by the module, so you can provide either the full path format - name C(/Common/bigip1) or just the name string C(bigip1). - type: list - ha_group: - description: - - Specifies a configured C(HA group) to be associated with the traffic group. - - Once you create an HA group on a device and associate the HA group with a traffic group, - you must create an HA group and associate it with that same traffic group on every device in the device group. - - To disable an HA group failover method , specify an empty string value (C("")) to this parameter. - - Disabling HA group will revert the device back to using C(Load Aware) method as it is the default, - unless C(ha_order) setting is also configured. - - The C(auto_failback) and C(auto_failback_time) are not compatible with C(ha_group). - type: str - ha_load_factor: - description: - - The value of the load the traffic-group presents the system relative to other traffic groups. - - This parameter only takes effect when C(Load Aware) failover method is in use. - - The correct value range is C(1 - 1000) inclusive. - type: int - auto_failback: - description: - - Specifies whether the traffic group fails back to the initial device specified in C(ha_order). - type: bool - auto_failback_time: - description: - - Specifies the number of seconds the system delays before failing back to the initial device - specified in C(ha_order). - - The correct value range is C(0 - 300) inclusive. - type: int - partition: - description: - - Device partition to manage resources on. - type: str - default: Common - state: - description: - - When C(present), ensures that the traffic group exists. - - When C(absent), ensures the traffic group is removed. - type: str - choices: - - present - - absent - default: present -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -author: - - Tim Rupp (@caphrim007) - - Wojciech Wypior (@wojtek0806) -''' - -EXAMPLES = r''' -- name: Create a traffic group - bigip_device_traffic_group: - name: foo1 - state: present - provider: - user: admin - password: secret - server: lb.mydomain.com - delegate_to: localhost - -- name: Create a traffic group with ha_group failover - bigip_device_traffic_group: - name: foo2 - state: present - ha_group: foo_HA_grp - provider: - user: admin - password: secret - server: lb.mydomain.com - delegate_to: localhost - -- name: Create a traffic group with ha_order failover - bigip_device_traffic_group: - name: foo3 - state: present - ha_order: - - /Common/bigip1.lab.local - - /Common/bigip2.lab.local - auto_failback: yes - auto_failback_time: 40 - provider: - user: admin - password: secret - server: lb.mydomain.com - delegate_to: localhost - -- name: Change traffic group ha_order to ha_group - bigip_device_traffic_group: - name: foo3 - state: present - ha_group: foo_HA_grp - ha_order: "" - auto_failback: no - provider: - user: admin - password: secret - server: lb.mydomain.com - delegate_to: localhost - -- name: Remove traffic group - bigip_device_traffic_group: - name: foo - state: absent - provider: - user: admin - password: secret - server: lb.mydomain.com - delegate_to: localhost -''' - -RETURN = r''' -mac_address: - description: The MAC masquerade address - returned: changed - type: str - sample: "02:01:d7:93:35:08" -ha_group: - description: The configured HA group associated with traffic group - returned: changed - type: str - sample: foo_HA_grp -ha_order: - description: Specifies the order in which the devices will failover - returned: changed - type: list - sample: ['/Common/bigip1', '/Common/bigip2'] -ha_load_factor: - description: The value of the load the traffic-group presents the system relative to other traffic groups - returned: changed - type: int - sample: 20 -auto_failback: - description: Specifies whether the traffic group fails back to the initial device specified in ha_order - returned: changed - type: bool - sample: yes -auto_failback_time: - description: Specifies the number of seconds the system delays before failing back - returned: changed - type: int - sample: 60 -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.basic import env_fallback - -try: - from library.module_utils.network.f5.bigip import F5RestClient - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import AnsibleF5Parameters - from library.module_utils.network.f5.common import f5_argument_spec - from library.module_utils.network.f5.common import fq_name - from library.module_utils.network.f5.common import transform_name - from library.module_utils.network.f5.common import flatten_boolean -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.bigip import F5RestClient - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import fq_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import transform_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import flatten_boolean - - -class Parameters(AnsibleF5Parameters): - api_map = { - 'mac': 'mac_address', - 'haGroup': 'ha_group', - 'haOrder': 'ha_order', - 'haLoadFactor': 'ha_load_factor', - 'autoFailbackTime': 'auto_failback_time', - 'autoFailbackEnabled': 'auto_failback', - } - - api_attributes = [ - 'mac', - 'haGroup', - 'haOrder', - 'haLoadFactor', - 'autoFailbackTime', - 'autoFailbackEnabled', - - ] - - returnables = [ - 'mac_address', - 'ha_group', - 'ha_order', - 'ha_load_factor', - 'auto_failback_time', - 'auto_failback', - ] - - updatables = [ - 'mac_address', - 'ha_group', - 'ha_order', - 'ha_load_factor', - 'auto_failback_time', - 'auto_failback', - ] - - -class ApiParameters(Parameters): - pass - - -class ModuleParameters(Parameters): - @property - def mac_address(self): - if self._values['mac_address'] is None: - return None - if self._values['mac_address'] == '': - return 'none' - return self._values['mac_address'] - - @property - def ha_group(self): - if self._values['ha_group'] is None: - return None - if self._values['ha_group'] == '': - return 'none' - if self.auto_failback == 'true': - raise F5ModuleError( - "The auto_failback cannot be enabled when ha_group is specified." - ) - return self._values['ha_group'] - - @property - def ha_load_factor(self): - if self._values['ha_load_factor'] is None: - return None - value = self._values['ha_load_factor'] - if value < 1 or value > 1000: - raise F5ModuleError( - "Invalid ha_load_factor value, correct range is 1 - 1000, specified value: {0}.".format(value)) - return value - - @property - def auto_failback_time(self): - if self._values['auto_failback_time'] is None: - return None - value = self._values['auto_failback_time'] - if value < 0 or value > 300: - raise F5ModuleError( - "Invalid auto_failback_time value, correct range is 0 - 300, specified value: {0}.".format(value)) - return value - - @property - def auto_failback(self): - result = flatten_boolean(self._values['auto_failback']) - if result == 'yes': - return 'true' - if result == 'no': - return 'false' - return None - - @property - def ha_order(self): - if self._values['ha_order'] is None: - return None - if len(self._values['ha_order']) == 1 and self._values['ha_order'][0] == '': - if self.auto_failback == 'true': - raise F5ModuleError( - 'Cannot enable auto failback when HA order list is empty, at least one device must be specified.' - ) - return 'none' - result = [fq_name(self.partition, value) for value in self._values['ha_order']] - return result - - -class Changes(Parameters): - def to_return(self): - result = {} - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - - -class UsableChanges(Changes): - pass - - -class ReportableChanges(Changes): - - @property - def mac_address(self): - if self._values['mac_address'] is None: - return None - if self._values['mac_address'] == 'none': - return '' - return self._values['mac_address'] - - @property - def ha_group(self): - if self._values['ha_group'] is None: - return None - if self._values['ha_group'] == 'none': - return '' - return self._values['ha_group'] - - @property - def auto_failback(self): - result = self._values['auto_failback'] - if result == 'true': - return 'yes' - if result == 'false': - return 'no' - return None - - @property - def ha_order(self): - if self._values['ha_order'] is None: - return None - if self._values['ha_order'] == 'none': - return '' - return self._values['ha_order'] - - -class Difference(object): - def __init__(self, want, have=None): - self.want = want - self.have = have - - def compare(self, param): - try: - result = getattr(self, param) - return result - except AttributeError: - return self.__default(param) - - def __default(self, param): - attr1 = getattr(self.want, param) - try: - attr2 = getattr(self.have, param) - if attr1 != attr2: - return attr1 - except AttributeError: - return attr1 - - @property - def ha_group(self): - if self.want.ha_group is None: - return None - if self.have.ha_group is None and self.want.ha_group == 'none': - return None - if self.want.ha_group != self.have.ha_group: - if self.have.auto_failback == 'true' and self.want.auto_failback != 'false': - raise F5ModuleError( - "The auto_failback parameter on the device must disabled to use ha_group failover method." - ) - return self.want.ha_group - - @property - def ha_order(self): - # Device order is literally derived from the order in the array, - # hence lists with the same elements but in different order cannot be equal, so cmp_simple_list - # function will not work here. - if self.want.ha_order is None: - return None - if self.have.ha_order is None and self.want.ha_order == 'none': - return None - if self.want.ha_order != self.have.ha_order: - return self.want.ha_order - - @property - def partition(self): - raise F5ModuleError( - "Partition cannot be changed for a traffic group. Only /Common is allowed." - ) - - -class ModuleManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = F5RestClient(**self.module.params) - self.have = None - self.want = ModuleParameters(params=self.module.params) - self.changes = UsableChanges() - - def _set_changed_options(self): - changed = {} - for key in Parameters.returnables: - if getattr(self.want, key) is not None: - changed[key] = getattr(self.want, key) - if changed: - self.changes = UsableChanges(params=changed) - - def _update_changed_options(self): - diff = Difference(self.want, self.have) - updatables = Parameters.updatables - changed = dict() - for k in updatables: - change = diff.compare(k) - if change is None: - continue - else: - if isinstance(change, dict): - changed.update(change) - else: - changed[k] = change - if changed: - self.changes = UsableChanges(params=changed) - return True - return False - - def _announce_deprecations(self, result): - warnings = result.pop('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) - - def exec_module(self): - changed = False - result = dict() - state = self.want.state - - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - - reportable = ReportableChanges(params=self.changes.to_return()) - changes = reportable.to_return() - result.update(**changes) - result.update(dict(changed=changed)) - self._announce_deprecations(result) - return result - - def present(self): - if self.exists(): - return self.update() - else: - return self.create() - - def absent(self): - if self.exists(): - return self.remove() - return False - - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - - def update(self): - self.have = self.read_current_from_device() - if not self.should_update(): - return False - if self.module.check_mode: - return True - self.update_on_device() - return True - - def remove(self): - if self.module.check_mode: - return True - self.remove_from_device() - if self.exists(): - raise F5ModuleError("Failed to delete the resource.") - return True - - def create(self): - self._set_changed_options() - if self.want.partition.lower().strip('/') != 'common': - raise F5ModuleError( - "Traffic groups can only be created in the /Common partition" - ) - if self.module.check_mode: - return True - self.create_on_device() - return True - - def exists(self): - uri = "https://{0}:{1}/mgmt/tm/cm/traffic-group/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError: - return False - if resp.status == 404 or 'code' in response and response['code'] == 404: - return False - return True - - def create_on_device(self): - params = self.changes.api_params() - params['name'] = self.want.name - params['partition'] = self.want.partition - uri = "https://{0}:{1}/mgmt/tm/cm/traffic-group/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.post(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return response['selfLink'] - - def update_on_device(self): - params = self.changes.api_params() - uri = "https://{0}:{1}/mgmt/tm/cm/traffic-group/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.patch(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - def read_current_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/cm/traffic-group/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - - resp = self.client.api.get(uri) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return ApiParameters(params=response) - - def remove_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/cm/traffic-group/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - response = self.client.api.delete(uri) - if response.status == 200: - return True - raise F5ModuleError(response.content) - - -class ArgumentSpec(object): - def __init__(self): - self.supports_check_mode = True - argument_spec = dict( - name=dict(required=True), - mac_address=dict(), - ha_order=dict( - type='list' - ), - ha_group=dict(), - ha_load_factor=dict( - type='int' - ), - auto_failback=dict( - type='bool', - ), - auto_failback_time=dict( - type='int' - ), - state=dict( - default='present', - choices=['absent', 'present'] - ), - partition=dict( - default='Common', - fallback=(env_fallback, ['F5_PARTITION']) - ), - - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec, - supports_check_mode=spec.supports_check_mode, - ) - - try: - mm = ModuleManager(module=module) - results = mm.exec_module() - module.exit_json(**results) - except F5ModuleError as ex: - module.fail_json(msg=str(ex)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_facts.py b/plugins/modules/network/f5/bigip_facts.py deleted file mode 100644 index 5dc9d498e9..0000000000 --- a/plugins/modules/network/f5/bigip_facts.py +++ /dev/null @@ -1,1803 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2017 F5 Networks Inc. -# Copyright (c) 2013 Matt Hite -# 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': ['deprecated'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigip_facts -short_description: Collect facts from F5 BIG-IP devices -description: - - Collect facts from F5 BIG-IP devices via iControl SOAP API -author: - - Matt Hite (@mhite) - - Tim Rupp (@caphrim007) -notes: - - Requires BIG-IP software version >= 11.4 - - F5 developed module 'bigsuds' required (see http://devcentral.f5.com) - - Best run as a local_action in your playbook - - Tested with manager and above account privilege level - - C(provision) facts were added in 2.2 - - This module is deprecated. Use the C(bigip_device_info) module instead. -deprecated: - removed_in: '2.11' - alternative: bigip_device_info - why: > - The bigip_facts module relies on SOAP to communicate with the BIG-IP, - and has a large amount of code that does not conform to existing F5 standards. - The M(bigip_device_info) module is easier to maintain and use. -requirements: - - bigsuds -options: - session: - description: - - BIG-IP session support; may be useful to avoid concurrency - issues in certain circumstances. - default: no - type: bool - include: - description: - - Fact category or list of categories to collect - required: True - choices: - - address_class - - certificate - - client_ssl_profile - - device - - device_group - - interface - - key - - node - - pool - - provision - - rule - - self_ip - - software - - system_info - - traffic_group - - trunk - - virtual_address - - virtual_server - - vlan - filter: - description: - - Shell-style glob matching string used to filter fact keys. Not - applicable for software, provision, and system_info fact categories. -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -''' - -EXAMPLES = r''' -- name: Collect BIG-IP facts - bigip_facts: - server: lb.mydomain.com - user: admin - password: secret - include: - - interface - - vlan - delegate_to: localhost -''' - -import fnmatch -import re -import traceback - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import string_types -from ansible.module_utils.six.moves import zip - -try: - from library.module_utils.network.f5.legacy import bigip_api, bigsuds_found - from library.module_utils.network.f5.common import f5_argument_spec - from library.module_utils.network.f5.common import F5BaseClient -except ImportError: - from ansible_collections.community.general.plugins.module_utils.network.f5.legacy import bigip_api, bigsuds_found - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5BaseClient - -try: - from suds import MethodNotFound, WebFault -except ImportError: - pass # Handle via f5_utils.bigsuds_found - - -class F5(object): - """F5 iControl class. - - F5 BIG-IP iControl API class. - - Attributes: - api: iControl API instance. - """ - - def __init__(self, host, user, password, session=False, validate_certs=True, port=443): - self.api = bigip_api(host, user, password, validate_certs, port) - if session: - self.start_session() - - def start_session(self): - self.api = self.api.with_session_id() - - def get_api(self): - return self.api - - def set_recursive_query_state(self, state): - self.api.System.Session.set_recursive_query_state(state) - - def get_recursive_query_state(self): - return self.api.System.Session.get_recursive_query_state() - - def enable_recursive_query_state(self): - self.set_recursive_query_state('STATE_ENABLED') - - def disable_recursive_query_state(self): - self.set_recursive_query_state('STATE_DISABLED') - - def set_active_folder(self, folder): - self.api.System.Session.set_active_folder(folder=folder) - - def get_active_folder(self): - return self.api.System.Session.get_active_folder() - - -class Interfaces(object): - """Interfaces class. - - F5 BIG-IP interfaces class. - - Attributes: - api: iControl API instance. - interfaces: A list of BIG-IP interface names. - """ - - def __init__(self, api, regex=None): - self.api = api - self.interfaces = api.Networking.Interfaces.get_list() - if regex: - re_filter = re.compile(regex) - self.interfaces = filter(re_filter.search, self.interfaces) - - def get_list(self): - return self.interfaces - - def get_active_media(self): - return self.api.Networking.Interfaces.get_active_media(self.interfaces) - - def get_actual_flow_control(self): - return self.api.Networking.Interfaces.get_actual_flow_control(self.interfaces) - - def get_bundle_state(self): - return self.api.Networking.Interfaces.get_bundle_state(self.interfaces) - - def get_description(self): - return self.api.Networking.Interfaces.get_description(self.interfaces) - - def get_dual_media_state(self): - return self.api.Networking.Interfaces.get_dual_media_state(self.interfaces) - - def get_enabled_state(self): - return self.api.Networking.Interfaces.get_enabled_state(self.interfaces) - - def get_if_index(self): - return self.api.Networking.Interfaces.get_if_index(self.interfaces) - - def get_learning_mode(self): - return self.api.Networking.Interfaces.get_learning_mode(self.interfaces) - - def get_lldp_admin_status(self): - return self.api.Networking.Interfaces.get_lldp_admin_status(self.interfaces) - - def get_lldp_tlvmap(self): - return self.api.Networking.Interfaces.get_lldp_tlvmap(self.interfaces) - - def get_mac_address(self): - return self.api.Networking.Interfaces.get_mac_address(self.interfaces) - - def get_media(self): - return self.api.Networking.Interfaces.get_media(self.interfaces) - - def get_media_option(self): - return self.api.Networking.Interfaces.get_media_option(self.interfaces) - - def get_media_option_sfp(self): - return self.api.Networking.Interfaces.get_media_option_sfp(self.interfaces) - - def get_media_sfp(self): - return self.api.Networking.Interfaces.get_media_sfp(self.interfaces) - - def get_media_speed(self): - return self.api.Networking.Interfaces.get_media_speed(self.interfaces) - - def get_media_status(self): - return self.api.Networking.Interfaces.get_media_status(self.interfaces) - - def get_mtu(self): - return self.api.Networking.Interfaces.get_mtu(self.interfaces) - - def get_phy_master_slave_mode(self): - return self.api.Networking.Interfaces.get_phy_master_slave_mode(self.interfaces) - - def get_prefer_sfp_state(self): - return self.api.Networking.Interfaces.get_prefer_sfp_state(self.interfaces) - - def get_flow_control(self): - return self.api.Networking.Interfaces.get_requested_flow_control(self.interfaces) - - def get_sflow_poll_interval(self): - return self.api.Networking.Interfaces.get_sflow_poll_interval(self.interfaces) - - def get_sflow_poll_interval_global(self): - return self.api.Networking.Interfaces.get_sflow_poll_interval_global(self.interfaces) - - def get_sfp_media_state(self): - return self.api.Networking.Interfaces.get_sfp_media_state(self.interfaces) - - def get_stp_active_edge_port_state(self): - return self.api.Networking.Interfaces.get_stp_active_edge_port_state(self.interfaces) - - def get_stp_enabled_state(self): - return self.api.Networking.Interfaces.get_stp_enabled_state(self.interfaces) - - def get_stp_link_type(self): - return self.api.Networking.Interfaces.get_stp_link_type(self.interfaces) - - def get_stp_protocol_detection_reset_state(self): - return self.api.Networking.Interfaces.get_stp_protocol_detection_reset_state(self.interfaces) - - -class SelfIPs(object): - """Self IPs class. - - F5 BIG-IP Self IPs class. - - Attributes: - api: iControl API instance. - self_ips: List of self IPs. - """ - - def __init__(self, api, regex=None): - self.api = api - self.self_ips = api.Networking.SelfIPV2.get_list() - if regex: - re_filter = re.compile(regex) - self.self_ips = filter(re_filter.search, self.self_ips) - - def get_list(self): - return self.self_ips - - def get_address(self): - return self.api.Networking.SelfIPV2.get_address(self.self_ips) - - def get_allow_access_list(self): - return self.api.Networking.SelfIPV2.get_allow_access_list(self.self_ips) - - def get_description(self): - return self.api.Networking.SelfIPV2.get_description(self.self_ips) - - def get_enforced_firewall_policy(self): - return self.api.Networking.SelfIPV2.get_enforced_firewall_policy(self.self_ips) - - def get_floating_state(self): - return self.api.Networking.SelfIPV2.get_floating_state(self.self_ips) - - def get_fw_rule(self): - return self.api.Networking.SelfIPV2.get_fw_rule(self.self_ips) - - def get_netmask(self): - return self.api.Networking.SelfIPV2.get_netmask(self.self_ips) - - def get_staged_firewall_policy(self): - return self.api.Networking.SelfIPV2.get_staged_firewall_policy(self.self_ips) - - def get_traffic_group(self): - return self.api.Networking.SelfIPV2.get_traffic_group(self.self_ips) - - def get_vlan(self): - return self.api.Networking.SelfIPV2.get_vlan(self.self_ips) - - def get_is_traffic_group_inherited(self): - return self.api.Networking.SelfIPV2.is_traffic_group_inherited(self.self_ips) - - -class Trunks(object): - """Trunks class. - - F5 BIG-IP trunks class. - - Attributes: - api: iControl API instance. - trunks: List of trunks. - """ - - def __init__(self, api, regex=None): - self.api = api - self.trunks = api.Networking.Trunk.get_list() - if regex: - re_filter = re.compile(regex) - self.trunks = filter(re_filter.search, self.trunks) - - def get_list(self): - return self.trunks - - def get_active_lacp_state(self): - return self.api.Networking.Trunk.get_active_lacp_state(self.trunks) - - def get_configured_member_count(self): - return self.api.Networking.Trunk.get_configured_member_count(self.trunks) - - def get_description(self): - return self.api.Networking.Trunk.get_description(self.trunks) - - def get_distribution_hash_option(self): - return self.api.Networking.Trunk.get_distribution_hash_option(self.trunks) - - def get_interface(self): - return self.api.Networking.Trunk.get_interface(self.trunks) - - def get_lacp_enabled_state(self): - return self.api.Networking.Trunk.get_lacp_enabled_state(self.trunks) - - def get_lacp_timeout_option(self): - return self.api.Networking.Trunk.get_lacp_timeout_option(self.trunks) - - def get_link_selection_policy(self): - return self.api.Networking.Trunk.get_link_selection_policy(self.trunks) - - def get_media_speed(self): - return self.api.Networking.Trunk.get_media_speed(self.trunks) - - def get_media_status(self): - return self.api.Networking.Trunk.get_media_status(self.trunks) - - def get_operational_member_count(self): - return self.api.Networking.Trunk.get_operational_member_count(self.trunks) - - def get_stp_enabled_state(self): - return self.api.Networking.Trunk.get_stp_enabled_state(self.trunks) - - def get_stp_protocol_detection_reset_state(self): - return self.api.Networking.Trunk.get_stp_protocol_detection_reset_state(self.trunks) - - -class Vlans(object): - """Vlans class. - - F5 BIG-IP Vlans class. - - Attributes: - api: iControl API instance. - vlans: List of VLANs. - """ - - def __init__(self, api, regex=None): - self.api = api - self.vlans = api.Networking.VLAN.get_list() - if regex: - re_filter = re.compile(regex) - self.vlans = filter(re_filter.search, self.vlans) - - def get_list(self): - return self.vlans - - def get_auto_lasthop(self): - return self.api.Networking.VLAN.get_auto_lasthop(self.vlans) - - def get_cmp_hash_algorithm(self): - return self.api.Networking.VLAN.get_cmp_hash_algorithm(self.vlans) - - def get_description(self): - return self.api.Networking.VLAN.get_description(self.vlans) - - def get_dynamic_forwarding(self): - return self.api.Networking.VLAN.get_dynamic_forwarding(self.vlans) - - def get_failsafe_action(self): - return self.api.Networking.VLAN.get_failsafe_action(self.vlans) - - def get_failsafe_state(self): - return self.api.Networking.VLAN.get_failsafe_state(self.vlans) - - def get_failsafe_timeout(self): - return self.api.Networking.VLAN.get_failsafe_timeout(self.vlans) - - def get_if_index(self): - return self.api.Networking.VLAN.get_if_index(self.vlans) - - def get_learning_mode(self): - return self.api.Networking.VLAN.get_learning_mode(self.vlans) - - def get_mac_masquerade_address(self): - return self.api.Networking.VLAN.get_mac_masquerade_address(self.vlans) - - def get_member(self): - return self.api.Networking.VLAN.get_member(self.vlans) - - def get_mtu(self): - return self.api.Networking.VLAN.get_mtu(self.vlans) - - def get_sflow_poll_interval(self): - return self.api.Networking.VLAN.get_sflow_poll_interval(self.vlans) - - def get_sflow_poll_interval_global(self): - return self.api.Networking.VLAN.get_sflow_poll_interval_global(self.vlans) - - def get_sflow_sampling_rate(self): - return self.api.Networking.VLAN.get_sflow_sampling_rate(self.vlans) - - def get_sflow_sampling_rate_global(self): - return self.api.Networking.VLAN.get_sflow_sampling_rate_global(self.vlans) - - def get_source_check_state(self): - return self.api.Networking.VLAN.get_source_check_state(self.vlans) - - def get_true_mac_address(self): - return self.api.Networking.VLAN.get_true_mac_address(self.vlans) - - def get_vlan_id(self): - return self.api.Networking.VLAN.get_vlan_id(self.vlans) - - -class Software(object): - """Software class. - - F5 BIG-IP software class. - - Attributes: - api: iControl API instance. - """ - - def __init__(self, api): - self.api = api - - def get_all_software_status(self): - return self.api.System.SoftwareManagement.get_all_software_status() - - -class VirtualServers(object): - """Virtual servers class. - - F5 BIG-IP virtual servers class. - - Attributes: - api: iControl API instance. - virtual_servers: List of virtual servers. - """ - - def __init__(self, api, regex=None): - self.api = api - self.virtual_servers = api.LocalLB.VirtualServer.get_list() - if regex: - re_filter = re.compile(regex) - self.virtual_servers = filter(re_filter.search, self.virtual_servers) - - def get_list(self): - return self.virtual_servers - - def get_name(self): - return [x[x.rfind('/') + 1:] for x in self.virtual_servers] - - def get_actual_hardware_acceleration(self): - return self.api.LocalLB.VirtualServer.get_actual_hardware_acceleration(self.virtual_servers) - - def get_authentication_profile(self): - return self.api.LocalLB.VirtualServer.get_authentication_profile(self.virtual_servers) - - def get_auto_lasthop(self): - return self.api.LocalLB.VirtualServer.get_auto_lasthop(self.virtual_servers) - - def get_bw_controller_policy(self): - return self.api.LocalLB.VirtualServer.get_bw_controller_policy(self.virtual_servers) - - def get_clone_pool(self): - return self.api.LocalLB.VirtualServer.get_clone_pool(self.virtual_servers) - - def get_cmp_enable_mode(self): - return self.api.LocalLB.VirtualServer.get_cmp_enable_mode(self.virtual_servers) - - def get_connection_limit(self): - return self.api.LocalLB.VirtualServer.get_connection_limit(self.virtual_servers) - - def get_connection_mirror_state(self): - return self.api.LocalLB.VirtualServer.get_connection_mirror_state(self.virtual_servers) - - def get_default_pool_name(self): - return self.api.LocalLB.VirtualServer.get_default_pool_name(self.virtual_servers) - - def get_description(self): - return self.api.LocalLB.VirtualServer.get_description(self.virtual_servers) - - def get_destination(self): - return self.api.LocalLB.VirtualServer.get_destination_v2(self.virtual_servers) - - def get_enabled_state(self): - return self.api.LocalLB.VirtualServer.get_enabled_state(self.virtual_servers) - - def get_enforced_firewall_policy(self): - return self.api.LocalLB.VirtualServer.get_enforced_firewall_policy(self.virtual_servers) - - def get_fallback_persistence_profile(self): - return self.api.LocalLB.VirtualServer.get_fallback_persistence_profile(self.virtual_servers) - - def get_fw_rule(self): - return self.api.LocalLB.VirtualServer.get_fw_rule(self.virtual_servers) - - def get_gtm_score(self): - return self.api.LocalLB.VirtualServer.get_gtm_score(self.virtual_servers) - - def get_last_hop_pool(self): - return self.api.LocalLB.VirtualServer.get_last_hop_pool(self.virtual_servers) - - def get_nat64_state(self): - return self.api.LocalLB.VirtualServer.get_nat64_state(self.virtual_servers) - - def get_object_status(self): - return self.api.LocalLB.VirtualServer.get_object_status(self.virtual_servers) - - def get_persistence_profile(self): - return self.api.LocalLB.VirtualServer.get_persistence_profile(self.virtual_servers) - - def get_profile(self): - return self.api.LocalLB.VirtualServer.get_profile(self.virtual_servers) - - def get_protocol(self): - return self.api.LocalLB.VirtualServer.get_protocol(self.virtual_servers) - - def get_rate_class(self): - return self.api.LocalLB.VirtualServer.get_rate_class(self.virtual_servers) - - def get_rate_limit(self): - return self.api.LocalLB.VirtualServer.get_rate_limit(self.virtual_servers) - - def get_rate_limit_destination_mask(self): - return self.api.LocalLB.VirtualServer.get_rate_limit_destination_mask(self.virtual_servers) - - def get_rate_limit_mode(self): - return self.api.LocalLB.VirtualServer.get_rate_limit_mode(self.virtual_servers) - - def get_rate_limit_source_mask(self): - return self.api.LocalLB.VirtualServer.get_rate_limit_source_mask(self.virtual_servers) - - def get_related_rule(self): - return self.api.LocalLB.VirtualServer.get_related_rule(self.virtual_servers) - - def get_rule(self): - return self.api.LocalLB.VirtualServer.get_rule(self.virtual_servers) - - def get_security_log_profile(self): - return self.api.LocalLB.VirtualServer.get_security_log_profile(self.virtual_servers) - - def get_snat_pool(self): - return self.api.LocalLB.VirtualServer.get_snat_pool(self.virtual_servers) - - def get_snat_type(self): - return self.api.LocalLB.VirtualServer.get_snat_type(self.virtual_servers) - - def get_source_address(self): - return self.api.LocalLB.VirtualServer.get_source_address(self.virtual_servers) - - def get_source_address_translation_lsn_pool(self): - return self.api.LocalLB.VirtualServer.get_source_address_translation_lsn_pool(self.virtual_servers) - - def get_source_address_translation_snat_pool(self): - return self.api.LocalLB.VirtualServer.get_source_address_translation_snat_pool(self.virtual_servers) - - def get_source_address_translation_type(self): - return self.api.LocalLB.VirtualServer.get_source_address_translation_type(self.virtual_servers) - - def get_source_port_behavior(self): - return self.api.LocalLB.VirtualServer.get_source_port_behavior(self.virtual_servers) - - def get_staged_firewall_policy(self): - return self.api.LocalLB.VirtualServer.get_staged_firewall_policy(self.virtual_servers) - - def get_translate_address_state(self): - return self.api.LocalLB.VirtualServer.get_translate_address_state(self.virtual_servers) - - def get_translate_port_state(self): - return self.api.LocalLB.VirtualServer.get_translate_port_state(self.virtual_servers) - - def get_type(self): - return self.api.LocalLB.VirtualServer.get_type(self.virtual_servers) - - def get_vlan(self): - return self.api.LocalLB.VirtualServer.get_vlan(self.virtual_servers) - - def get_wildmask(self): - return self.api.LocalLB.VirtualServer.get_wildmask(self.virtual_servers) - - -class Pools(object): - """Pools class. - - F5 BIG-IP pools class. - - Attributes: - api: iControl API instance. - pool_names: List of pool names. - """ - - def __init__(self, api, regex=None): - self.api = api - self.pool_names = api.LocalLB.Pool.get_list() - if regex: - re_filter = re.compile(regex) - self.pool_names = filter(re_filter.search, self.pool_names) - - def get_list(self): - return self.pool_names - - def get_name(self): - return [x[x.rfind('/') + 1:] for x in self.pool_names] - - def get_action_on_service_down(self): - return self.api.LocalLB.Pool.get_action_on_service_down(self.pool_names) - - def get_active_member_count(self): - return self.api.LocalLB.Pool.get_active_member_count(self.pool_names) - - def get_aggregate_dynamic_ratio(self): - return self.api.LocalLB.Pool.get_aggregate_dynamic_ratio(self.pool_names) - - def get_allow_nat_state(self): - return self.api.LocalLB.Pool.get_allow_nat_state(self.pool_names) - - def get_allow_snat_state(self): - return self.api.LocalLB.Pool.get_allow_snat_state(self.pool_names) - - def get_client_ip_tos(self): - return self.api.LocalLB.Pool.get_client_ip_tos(self.pool_names) - - def get_client_link_qos(self): - return self.api.LocalLB.Pool.get_client_link_qos(self.pool_names) - - def get_description(self): - return self.api.LocalLB.Pool.get_description(self.pool_names) - - def get_gateway_failsafe_device(self): - return self.api.LocalLB.Pool.get_gateway_failsafe_device(self.pool_names) - - def get_ignore_persisted_weight_state(self): - return self.api.LocalLB.Pool.get_ignore_persisted_weight_state(self.pool_names) - - def get_lb_method(self): - result = [] - lb_choice = dict( - LB_METHOD_DYNAMIC_RATIO_MEMBER='dynamic-ratio-member', - LB_METHOD_DYNAMIC_RATIO='dynamic-ratio-node', - LB_METHOD_FASTEST_APP_RESPONSE='fastest-app-response', - LB_METHOD_FASTEST_NODE_ADDRESS='fastest-node', - LB_METHOD_LEAST_CONNECTION_MEMBER='least-connections-member', - LB_METHOD_LEAST_CONNECTION_NODE_ADDRESS='least-connections-node', - LB_METHOD_LEAST_SESSIONS='least-sessions', - LB_METHOD_OBSERVED_MEMBER='observed-member', - LB_METHOD_OBSERVED_NODE_ADDRESS='observed-node', - LB_METHOD_PREDICTIVE_MEMBER='predictive-member', - LB_METHOD_PREDICTIVE_NODE_ADDRESS='predictive-node', - LB_METHOD_RATIO_LEAST_CONNECTION_MEMBER='ratio-least-connections-member', - LB_METHOD_RATIO_LEAST_CONNECTION_NODE_ADDRESS='ratio-least-connections-node', - LB_METHOD_RATIO_MEMBER='ratio-member', - LB_METHOD_RATIO_NODE_ADDRESS='ratio-node', - LB_METHOD_RATIO_SESSION='ratio-session', - LB_METHOD_ROUND_ROBIN='round-robin', - LB_METHOD_WEIGHTED_LEAST_CONNECTION_MEMBER='weighted-least-connections-member', - LB_METHOD_WEIGHTED_LEAST_CONNECTION_NODE_ADDRESS='weighted-least-connections-node' - ) - methods = self.api.LocalLB.Pool.get_lb_method(self.pool_names) - for method in methods: - result.append(lb_choice.get(method, method)) - return result - - def get_member(self): - return self.api.LocalLB.Pool.get_member_v2(self.pool_names) - - def get_minimum_active_member(self): - return self.api.LocalLB.Pool.get_minimum_active_member(self.pool_names) - - def get_minimum_up_member(self): - return self.api.LocalLB.Pool.get_minimum_up_member(self.pool_names) - - def get_minimum_up_member_action(self): - return self.api.LocalLB.Pool.get_minimum_up_member_action(self.pool_names) - - def get_minimum_up_member_enabled_state(self): - return self.api.LocalLB.Pool.get_minimum_up_member_enabled_state(self.pool_names) - - def get_monitor_association(self): - return self.api.LocalLB.Pool.get_monitor_association(self.pool_names) - - def get_monitor_instance(self): - return self.api.LocalLB.Pool.get_monitor_instance(self.pool_names) - - def get_object_status(self): - return self.api.LocalLB.Pool.get_object_status(self.pool_names) - - def get_profile(self): - return self.api.LocalLB.Pool.get_profile(self.pool_names) - - def get_queue_depth_limit(self): - return self.api.LocalLB.Pool.get_queue_depth_limit(self.pool_names) - - def get_queue_on_connection_limit_state(self): - return self.api.LocalLB.Pool.get_queue_on_connection_limit_state(self.pool_names) - - def get_queue_time_limit(self): - return self.api.LocalLB.Pool.get_queue_time_limit(self.pool_names) - - def get_reselect_tries(self): - return self.api.LocalLB.Pool.get_reselect_tries(self.pool_names) - - def get_server_ip_tos(self): - return self.api.LocalLB.Pool.get_server_ip_tos(self.pool_names) - - def get_server_link_qos(self): - return self.api.LocalLB.Pool.get_server_link_qos(self.pool_names) - - def get_simple_timeout(self): - return self.api.LocalLB.Pool.get_simple_timeout(self.pool_names) - - def get_slow_ramp_time(self): - return self.api.LocalLB.Pool.get_slow_ramp_time(self.pool_names) - - -class Devices(object): - """Devices class. - - F5 BIG-IP devices class. - - Attributes: - api: iControl API instance. - devices: List of devices. - """ - - def __init__(self, api, regex=None): - self.api = api - self.devices = api.Management.Device.get_list() - if regex: - re_filter = re.compile(regex) - self.devices = filter(re_filter.search, self.devices) - - def get_list(self): - return self.devices - - def get_active_modules(self): - return self.api.Management.Device.get_active_modules(self.devices) - - def get_base_mac_address(self): - return self.api.Management.Device.get_base_mac_address(self.devices) - - def get_blade_addresses(self): - return self.api.Management.Device.get_blade_addresses(self.devices) - - def get_build(self): - return self.api.Management.Device.get_build(self.devices) - - def get_chassis_id(self): - return self.api.Management.Device.get_chassis_id(self.devices) - - def get_chassis_type(self): - return self.api.Management.Device.get_chassis_type(self.devices) - - def get_comment(self): - return self.api.Management.Device.get_comment(self.devices) - - def get_configsync_address(self): - return self.api.Management.Device.get_configsync_address(self.devices) - - def get_contact(self): - return self.api.Management.Device.get_contact(self.devices) - - def get_description(self): - return self.api.Management.Device.get_description(self.devices) - - def get_edition(self): - return self.api.Management.Device.get_edition(self.devices) - - def get_failover_state(self): - return self.api.Management.Device.get_failover_state(self.devices) - - def get_local_device(self): - return self.api.Management.Device.get_local_device() - - def get_hostname(self): - return self.api.Management.Device.get_hostname(self.devices) - - def get_inactive_modules(self): - return self.api.Management.Device.get_inactive_modules(self.devices) - - def get_location(self): - return self.api.Management.Device.get_location(self.devices) - - def get_management_address(self): - return self.api.Management.Device.get_management_address(self.devices) - - def get_marketing_name(self): - return self.api.Management.Device.get_marketing_name(self.devices) - - def get_multicast_address(self): - return self.api.Management.Device.get_multicast_address(self.devices) - - def get_optional_modules(self): - return self.api.Management.Device.get_optional_modules(self.devices) - - def get_platform_id(self): - return self.api.Management.Device.get_platform_id(self.devices) - - def get_primary_mirror_address(self): - return self.api.Management.Device.get_primary_mirror_address(self.devices) - - def get_product(self): - return self.api.Management.Device.get_product(self.devices) - - def get_secondary_mirror_address(self): - return self.api.Management.Device.get_secondary_mirror_address(self.devices) - - def get_software_version(self): - return self.api.Management.Device.get_software_version(self.devices) - - def get_timelimited_modules(self): - return self.api.Management.Device.get_timelimited_modules(self.devices) - - def get_timezone(self): - return self.api.Management.Device.get_timezone(self.devices) - - def get_unicast_addresses(self): - return self.api.Management.Device.get_unicast_addresses(self.devices) - - -class DeviceGroups(object): - """Device groups class. - - F5 BIG-IP device groups class. - - Attributes: - api: iControl API instance. - device_groups: List of device groups. - """ - - def __init__(self, api, regex=None): - self.api = api - self.device_groups = api.Management.DeviceGroup.get_list() - if regex: - re_filter = re.compile(regex) - self.device_groups = filter(re_filter.search, self.device_groups) - - def get_list(self): - return self.device_groups - - def get_all_preferred_active(self): - return self.api.Management.DeviceGroup.get_all_preferred_active(self.device_groups) - - def get_autosync_enabled_state(self): - return self.api.Management.DeviceGroup.get_autosync_enabled_state(self.device_groups) - - def get_description(self): - return self.api.Management.DeviceGroup.get_description(self.device_groups) - - def get_device(self): - return self.api.Management.DeviceGroup.get_device(self.device_groups) - - def get_full_load_on_sync_state(self): - return self.api.Management.DeviceGroup.get_full_load_on_sync_state(self.device_groups) - - def get_incremental_config_sync_size_maximum(self): - return self.api.Management.DeviceGroup.get_incremental_config_sync_size_maximum(self.device_groups) - - def get_network_failover_enabled_state(self): - return self.api.Management.DeviceGroup.get_network_failover_enabled_state(self.device_groups) - - def get_sync_status(self): - return self.api.Management.DeviceGroup.get_sync_status(self.device_groups) - - def get_type(self): - return self.api.Management.DeviceGroup.get_type(self.device_groups) - - -class TrafficGroups(object): - """Traffic groups class. - - F5 BIG-IP traffic groups class. - - Attributes: - api: iControl API instance. - traffic_groups: List of traffic groups. - """ - - def __init__(self, api, regex=None): - self.api = api - self.traffic_groups = api.Management.TrafficGroup.get_list() - if regex: - re_filter = re.compile(regex) - self.traffic_groups = filter(re_filter.search, self.traffic_groups) - - def get_list(self): - return self.traffic_groups - - def get_auto_failback_enabled_state(self): - return self.api.Management.TrafficGroup.get_auto_failback_enabled_state(self.traffic_groups) - - def get_auto_failback_time(self): - return self.api.Management.TrafficGroup.get_auto_failback_time(self.traffic_groups) - - def get_default_device(self): - return self.api.Management.TrafficGroup.get_default_device(self.traffic_groups) - - def get_description(self): - return self.api.Management.TrafficGroup.get_description(self.traffic_groups) - - def get_ha_load_factor(self): - return self.api.Management.TrafficGroup.get_ha_load_factor(self.traffic_groups) - - def get_ha_order(self): - return self.api.Management.TrafficGroup.get_ha_order(self.traffic_groups) - - def get_is_floating(self): - return self.api.Management.TrafficGroup.get_is_floating(self.traffic_groups) - - def get_mac_masquerade_address(self): - return self.api.Management.TrafficGroup.get_mac_masquerade_address(self.traffic_groups) - - def get_unit_id(self): - return self.api.Management.TrafficGroup.get_unit_id(self.traffic_groups) - - -class Rules(object): - """Rules class. - - F5 BIG-IP iRules class. - - Attributes: - api: iControl API instance. - rules: List of iRules. - """ - - def __init__(self, api, regex=None): - self.api = api - self.rules = api.LocalLB.Rule.get_list() - if regex: - re_filter = re.compile(regex) - self.traffic_groups = filter(re_filter.search, self.rules) - - def get_list(self): - return self.rules - - def get_description(self): - return self.api.LocalLB.Rule.get_description(rule_names=self.rules) - - def get_ignore_vertification(self): - return self.api.LocalLB.Rule.get_ignore_vertification(rule_names=self.rules) - - def get_verification_status(self): - return self.api.LocalLB.Rule.get_verification_status_v2(rule_names=self.rules) - - def get_definition(self): - return [x['rule_definition'] for x in self.api.LocalLB.Rule.query_rule(rule_names=self.rules)] - - -class Nodes(object): - """Nodes class. - - F5 BIG-IP nodes class. - - Attributes: - api: iControl API instance. - nodes: List of nodes. - """ - - def __init__(self, api, regex=None): - self.api = api - self.nodes = api.LocalLB.NodeAddressV2.get_list() - if regex: - re_filter = re.compile(regex) - self.nodes = filter(re_filter.search, self.nodes) - - def get_list(self): - return self.nodes - - def get_address(self): - return self.api.LocalLB.NodeAddressV2.get_address(nodes=self.nodes) - - def get_name(self): - return [x[x.rfind('/') + 1:] for x in self.nodes] - - def get_connection_limit(self): - return self.api.LocalLB.NodeAddressV2.get_connection_limit(nodes=self.nodes) - - def get_description(self): - return self.api.LocalLB.NodeAddressV2.get_description(nodes=self.nodes) - - def get_dynamic_ratio(self): - return self.api.LocalLB.NodeAddressV2.get_dynamic_ratio_v2(nodes=self.nodes) - - def get_monitor_instance(self): - return self.api.LocalLB.NodeAddressV2.get_monitor_instance(nodes=self.nodes) - - def get_monitor_rule(self): - return self.api.LocalLB.NodeAddressV2.get_monitor_rule(nodes=self.nodes) - - def get_monitor_status(self): - return self.api.LocalLB.NodeAddressV2.get_monitor_status(nodes=self.nodes) - - def get_object_status(self): - return self.api.LocalLB.NodeAddressV2.get_object_status(nodes=self.nodes) - - def get_rate_limit(self): - return self.api.LocalLB.NodeAddressV2.get_rate_limit(nodes=self.nodes) - - def get_ratio(self): - return self.api.LocalLB.NodeAddressV2.get_ratio(nodes=self.nodes) - - def get_session_status(self): - return self.api.LocalLB.NodeAddressV2.get_session_status(nodes=self.nodes) - - -class VirtualAddresses(object): - """Virtual addresses class. - - F5 BIG-IP virtual addresses class. - - Attributes: - api: iControl API instance. - virtual_addresses: List of virtual addresses. - """ - - def __init__(self, api, regex=None): - self.api = api - self.virtual_addresses = api.LocalLB.VirtualAddressV2.get_list() - if regex: - re_filter = re.compile(regex) - self.virtual_addresses = filter(re_filter.search, self.virtual_addresses) - - def get_list(self): - return self.virtual_addresses - - def get_address(self): - return self.api.LocalLB.VirtualAddressV2.get_address(self.virtual_addresses) - - def get_arp_state(self): - return self.api.LocalLB.VirtualAddressV2.get_arp_state(self.virtual_addresses) - - def get_auto_delete_state(self): - return self.api.LocalLB.VirtualAddressV2.get_auto_delete_state(self.virtual_addresses) - - def get_connection_limit(self): - return self.api.LocalLB.VirtualAddressV2.get_connection_limit(self.virtual_addresses) - - def get_description(self): - return self.api.LocalLB.VirtualAddressV2.get_description(self.virtual_addresses) - - def get_enabled_state(self): - return self.api.LocalLB.VirtualAddressV2.get_enabled_state(self.virtual_addresses) - - def get_icmp_echo_state(self): - return self.api.LocalLB.VirtualAddressV2.get_icmp_echo_state(self.virtual_addresses) - - def get_is_floating_state(self): - return self.api.LocalLB.VirtualAddressV2.get_is_floating_state(self.virtual_addresses) - - def get_netmask(self): - return self.api.LocalLB.VirtualAddressV2.get_netmask(self.virtual_addresses) - - def get_object_status(self): - return self.api.LocalLB.VirtualAddressV2.get_object_status(self.virtual_addresses) - - def get_route_advertisement_state(self): - return self.api.LocalLB.VirtualAddressV2.get_route_advertisement_state(self.virtual_addresses) - - def get_traffic_group(self): - return self.api.LocalLB.VirtualAddressV2.get_traffic_group(self.virtual_addresses) - - -class AddressClasses(object): - """Address group/class class. - - F5 BIG-IP address group/class class. - - In TMUI these things are known as Address Data Groups. Examples that ship with the - box include /Common/aol and /Common/private_net - - Attributes: - api: iControl API instance. - address_classes: List of address classes. - """ - - def __init__(self, api, regex=None): - self.api = api - self.address_classes = api.LocalLB.Class.get_address_class_list() - if regex: - re_filter = re.compile(regex) - self.address_classes = filter(re_filter.search, self.address_classes) - - def get_list(self): - return self.address_classes - - def get_address_class(self): - key = self.api.LocalLB.Class.get_address_class(self.address_classes) - value = self.api.LocalLB.Class.get_address_class_member_data_value(key) - - result = [] - for idx, v in enumerate(key): - for idx2, member in enumerate(v['members']): - dg_value = dict( - value=value[idx][idx2] - ) - dg_value.update(member) - result.append(dg_value) - return result - - def get_description(self): - return self.api.LocalLB.Class.get_description(self.address_classes) - - -class Certificates(object): - """Certificates class. - - F5 BIG-IP certificates class. - - Attributes: - api: iControl API instance. - certificates: List of certificate identifiers. - certificate_list: List of certificate information structures. - """ - - def __init__(self, api, regex=None, mode="MANAGEMENT_MODE_DEFAULT"): - self.api = api - self.certificate_list = api.Management.KeyCertificate.get_certificate_list(mode=mode) - self.certificates = [x['certificate']['cert_info']['id'] for x in self.certificate_list] - if regex: - re_filter = re.compile(regex) - self.certificates = filter(re_filter.search, self.certificates) - self.certificate_list = [x for x in self.certificate_list if x['certificate']['cert_info']['id'] in self.certificates] - - def get_list(self): - return self.certificates - - def get_certificate_list(self): - return self.certificate_list - - -class Keys(object): - """Keys class. - - F5 BIG-IP keys class. - - Attributes: - api: iControl API instance. - keys: List of key identifiers. - key_list: List of key information structures. - """ - - def __init__(self, api, regex=None, mode="MANAGEMENT_MODE_DEFAULT"): - self.api = api - self.key_list = api.Management.KeyCertificate.get_key_list(mode=mode) - self.keys = [x['key_info']['id'] for x in self.key_list] - if regex: - re_filter = re.compile(regex) - self.keys = filter(re_filter.search, self.keys) - self.key_list = [x for x in self.key_list if x['key_info']['id'] in self.keys] - - def get_list(self): - return self.keys - - def get_key_list(self): - return self.key_list - - -class ProfileClientSSL(object): - """Client SSL profiles class. - - F5 BIG-IP client SSL profiles class. - - Attributes: - api: iControl API instance. - profiles: List of client SSL profiles. - """ - - def __init__(self, api, regex=None): - self.api = api - self.profiles = api.LocalLB.ProfileClientSSL.get_list() - if regex: - re_filter = re.compile(regex) - self.profiles = filter(re_filter.search, self.profiles) - - def get_list(self): - return self.profiles - - def get_alert_timeout(self): - return self.api.LocalLB.ProfileClientSSL.get_alert_timeout(self.profiles) - - def get_allow_nonssl_state(self): - return self.api.LocalLB.ProfileClientSSL.get_allow_nonssl_state(self.profiles) - - def get_authenticate_depth(self): - return self.api.LocalLB.ProfileClientSSL.get_authenticate_depth(self.profiles) - - def get_authenticate_once_state(self): - return self.api.LocalLB.ProfileClientSSL.get_authenticate_once_state(self.profiles) - - def get_ca_file(self): - return self.api.LocalLB.ProfileClientSSL.get_ca_file_v2(self.profiles) - - def get_cache_size(self): - return self.api.LocalLB.ProfileClientSSL.get_cache_size(self.profiles) - - def get_cache_timeout(self): - return self.api.LocalLB.ProfileClientSSL.get_cache_timeout(self.profiles) - - def get_certificate_file(self): - return self.api.LocalLB.ProfileClientSSL.get_certificate_file_v2(self.profiles) - - def get_chain_file(self): - return self.api.LocalLB.ProfileClientSSL.get_chain_file_v2(self.profiles) - - def get_cipher_list(self): - return self.api.LocalLB.ProfileClientSSL.get_cipher_list(self.profiles) - - def get_client_certificate_ca_file(self): - return self.api.LocalLB.ProfileClientSSL.get_client_certificate_ca_file_v2(self.profiles) - - def get_crl_file(self): - return self.api.LocalLB.ProfileClientSSL.get_crl_file_v2(self.profiles) - - def get_default_profile(self): - return self.api.LocalLB.ProfileClientSSL.get_default_profile(self.profiles) - - def get_description(self): - return self.api.LocalLB.ProfileClientSSL.get_description(self.profiles) - - def get_forward_proxy_ca_certificate_file(self): - return self.api.LocalLB.ProfileClientSSL.get_forward_proxy_ca_certificate_file(self.profiles) - - def get_forward_proxy_ca_key_file(self): - return self.api.LocalLB.ProfileClientSSL.get_forward_proxy_ca_key_file(self.profiles) - - def get_forward_proxy_ca_passphrase(self): - return self.api.LocalLB.ProfileClientSSL.get_forward_proxy_ca_passphrase(self.profiles) - - def get_forward_proxy_certificate_extension_include(self): - return self.api.LocalLB.ProfileClientSSL.get_forward_proxy_certificate_extension_include(self.profiles) - - def get_forward_proxy_certificate_lifespan(self): - return self.api.LocalLB.ProfileClientSSL.get_forward_proxy_certificate_lifespan(self.profiles) - - def get_forward_proxy_enabled_state(self): - return self.api.LocalLB.ProfileClientSSL.get_forward_proxy_enabled_state(self.profiles) - - def get_forward_proxy_lookup_by_ipaddr_port_state(self): - return self.api.LocalLB.ProfileClientSSL.get_forward_proxy_lookup_by_ipaddr_port_state(self.profiles) - - def get_handshake_timeout(self): - return self.api.LocalLB.ProfileClientSSL.get_handshake_timeout(self.profiles) - - def get_key_file(self): - return self.api.LocalLB.ProfileClientSSL.get_key_file_v2(self.profiles) - - def get_modssl_emulation_state(self): - return self.api.LocalLB.ProfileClientSSL.get_modssl_emulation_state(self.profiles) - - def get_passphrase(self): - return self.api.LocalLB.ProfileClientSSL.get_passphrase(self.profiles) - - def get_peer_certification_mode(self): - return self.api.LocalLB.ProfileClientSSL.get_peer_certification_mode(self.profiles) - - def get_profile_mode(self): - return self.api.LocalLB.ProfileClientSSL.get_profile_mode(self.profiles) - - def get_renegotiation_maximum_record_delay(self): - return self.api.LocalLB.ProfileClientSSL.get_renegotiation_maximum_record_delay(self.profiles) - - def get_renegotiation_period(self): - return self.api.LocalLB.ProfileClientSSL.get_renegotiation_period(self.profiles) - - def get_renegotiation_state(self): - return self.api.LocalLB.ProfileClientSSL.get_renegotiation_state(self.profiles) - - def get_renegotiation_throughput(self): - return self.api.LocalLB.ProfileClientSSL.get_renegotiation_throughput(self.profiles) - - def get_retain_certificate_state(self): - return self.api.LocalLB.ProfileClientSSL.get_retain_certificate_state(self.profiles) - - def get_secure_renegotiation_mode(self): - return self.api.LocalLB.ProfileClientSSL.get_secure_renegotiation_mode(self.profiles) - - def get_server_name(self): - return self.api.LocalLB.ProfileClientSSL.get_server_name(self.profiles) - - def get_session_ticket_state(self): - return self.api.LocalLB.ProfileClientSSL.get_session_ticket_state(self.profiles) - - def get_sni_default_state(self): - return self.api.LocalLB.ProfileClientSSL.get_sni_default_state(self.profiles) - - def get_sni_require_state(self): - return self.api.LocalLB.ProfileClientSSL.get_sni_require_state(self.profiles) - - def get_ssl_option(self): - return self.api.LocalLB.ProfileClientSSL.get_ssl_option(self.profiles) - - def get_strict_resume_state(self): - return self.api.LocalLB.ProfileClientSSL.get_strict_resume_state(self.profiles) - - def get_unclean_shutdown_state(self): - return self.api.LocalLB.ProfileClientSSL.get_unclean_shutdown_state(self.profiles) - - def get_is_base_profile(self): - return self.api.LocalLB.ProfileClientSSL.is_base_profile(self.profiles) - - def get_is_system_profile(self): - return self.api.LocalLB.ProfileClientSSL.is_system_profile(self.profiles) - - -class SystemInfo(object): - """System information class. - - F5 BIG-IP system information class. - - Attributes: - api: iControl API instance. - """ - - def __init__(self, api): - self.api = api - - def get_base_mac_address(self): - return self.api.System.SystemInfo.get_base_mac_address() - - def get_blade_temperature(self): - return self.api.System.SystemInfo.get_blade_temperature() - - def get_chassis_slot_information(self): - return self.api.System.SystemInfo.get_chassis_slot_information() - - def get_globally_unique_identifier(self): - return self.api.System.SystemInfo.get_globally_unique_identifier() - - def get_group_id(self): - return self.api.System.SystemInfo.get_group_id() - - def get_hardware_information(self): - return self.api.System.SystemInfo.get_hardware_information() - - def get_marketing_name(self): - return self.api.System.SystemInfo.get_marketing_name() - - def get_product_information(self): - return self.api.System.SystemInfo.get_product_information() - - def get_pva_version(self): - return self.api.System.SystemInfo.get_pva_version() - - def get_system_id(self): - return self.api.System.SystemInfo.get_system_id() - - def get_system_information(self): - return self.api.System.SystemInfo.get_system_information() - - def get_time(self): - return self.api.System.SystemInfo.get_time() - - def get_time_zone(self): - return self.api.System.SystemInfo.get_time_zone() - - def get_uptime(self): - return self.api.System.SystemInfo.get_uptime() - - -class ProvisionInfo(object): - """Provision information class. - - F5 BIG-IP provision information class. - - Attributes: - api: iControl API instance. - """ - - def __init__(self, api): - self.api = api - - def get_list(self): - result = [] - list = self.api.Management.Provision.get_list() - for item in list: - item = item.lower().replace('tmos_module_', '') - result.append(item) - return result - - def get_provisioned_list(self): - result = [] - list = self.api.Management.Provision.get_provisioned_list() - for item in list: - item = item.lower().replace('tmos_module_', '') - result.append(item) - return result - - -def generate_dict(api_obj, fields): - result_dict = {} - lists = [] - supported_fields = [] - if api_obj.get_list(): - for field in fields: - try: - api_response = getattr(api_obj, "get_" + field)() - except (MethodNotFound, WebFault): - pass - else: - lists.append(api_response) - supported_fields.append(field) - for i, j in enumerate(api_obj.get_list()): - temp = {} - temp.update([(item[0], item[1][i]) for item in zip(supported_fields, lists)]) - result_dict[j] = temp - return result_dict - - -def generate_simple_dict(api_obj, fields): - result_dict = {} - for field in fields: - try: - api_response = getattr(api_obj, "get_" + field)() - except (MethodNotFound, WebFault): - pass - else: - result_dict[field] = api_response - return result_dict - - -def generate_interface_dict(f5, regex): - interfaces = Interfaces(f5.get_api(), regex) - fields = ['active_media', 'actual_flow_control', 'bundle_state', - 'description', 'dual_media_state', 'enabled_state', 'if_index', - 'learning_mode', 'lldp_admin_status', 'lldp_tlvmap', - 'mac_address', 'media', 'media_option', 'media_option_sfp', - 'media_sfp', 'media_speed', 'media_status', 'mtu', - 'phy_master_slave_mode', 'prefer_sfp_state', 'flow_control', - 'sflow_poll_interval', 'sflow_poll_interval_global', - 'sfp_media_state', 'stp_active_edge_port_state', - 'stp_enabled_state', 'stp_link_type', - 'stp_protocol_detection_reset_state'] - return generate_dict(interfaces, fields) - - -def generate_self_ip_dict(f5, regex): - self_ips = SelfIPs(f5.get_api(), regex) - fields = ['address', 'allow_access_list', 'description', - 'enforced_firewall_policy', 'floating_state', 'fw_rule', - 'netmask', 'staged_firewall_policy', 'traffic_group', - 'vlan', 'is_traffic_group_inherited'] - return generate_dict(self_ips, fields) - - -def generate_trunk_dict(f5, regex): - trunks = Trunks(f5.get_api(), regex) - fields = ['active_lacp_state', 'configured_member_count', 'description', - 'distribution_hash_option', 'interface', 'lacp_enabled_state', - 'lacp_timeout_option', 'link_selection_policy', 'media_speed', - 'media_status', 'operational_member_count', 'stp_enabled_state', - 'stp_protocol_detection_reset_state'] - return generate_dict(trunks, fields) - - -def generate_vlan_dict(f5, regex): - vlans = Vlans(f5.get_api(), regex) - fields = ['auto_lasthop', 'cmp_hash_algorithm', 'description', - 'dynamic_forwarding', 'failsafe_action', 'failsafe_state', - 'failsafe_timeout', 'if_index', 'learning_mode', - 'mac_masquerade_address', 'member', 'mtu', - 'sflow_poll_interval', 'sflow_poll_interval_global', - 'sflow_sampling_rate', 'sflow_sampling_rate_global', - 'source_check_state', 'true_mac_address', 'vlan_id'] - return generate_dict(vlans, fields) - - -def generate_vs_dict(f5, regex): - virtual_servers = VirtualServers(f5.get_api(), regex) - fields = ['actual_hardware_acceleration', 'authentication_profile', - 'auto_lasthop', 'bw_controller_policy', 'clone_pool', - 'cmp_enable_mode', 'connection_limit', 'connection_mirror_state', - 'default_pool_name', 'description', 'destination', - 'enabled_state', 'enforced_firewall_policy', - 'fallback_persistence_profile', 'fw_rule', 'gtm_score', - 'last_hop_pool', 'nat64_state', 'object_status', - 'persistence_profile', 'profile', 'protocol', - 'rate_class', 'rate_limit', 'rate_limit_destination_mask', - 'rate_limit_mode', 'rate_limit_source_mask', 'related_rule', - 'rule', 'security_log_profile', 'snat_pool', 'snat_type', - 'source_address', 'source_address_translation_lsn_pool', - 'source_address_translation_snat_pool', - 'source_address_translation_type', 'source_port_behavior', - 'staged_firewall_policy', 'translate_address_state', - 'translate_port_state', 'type', 'vlan', 'wildmask', - 'name'] - return generate_dict(virtual_servers, fields) - - -def generate_pool_dict(f5, regex): - pools = Pools(f5.get_api(), regex) - fields = ['action_on_service_down', 'active_member_count', - 'aggregate_dynamic_ratio', 'allow_nat_state', - 'allow_snat_state', 'client_ip_tos', 'client_link_qos', - 'description', 'gateway_failsafe_device', - 'ignore_persisted_weight_state', 'lb_method', 'member', - 'minimum_active_member', 'minimum_up_member', - 'minimum_up_member_action', 'minimum_up_member_enabled_state', - 'monitor_association', 'monitor_instance', 'object_status', - 'profile', 'queue_depth_limit', - 'queue_on_connection_limit_state', 'queue_time_limit', - 'reselect_tries', 'server_ip_tos', 'server_link_qos', - 'simple_timeout', 'slow_ramp_time', 'name'] - return generate_dict(pools, fields) - - -def generate_device_dict(f5, regex): - devices = Devices(f5.get_api(), regex) - fields = ['active_modules', 'base_mac_address', 'blade_addresses', - 'build', 'chassis_id', 'chassis_type', 'comment', - 'configsync_address', 'contact', 'description', 'edition', - 'failover_state', 'hostname', 'inactive_modules', 'location', - 'management_address', 'marketing_name', 'multicast_address', - 'optional_modules', 'platform_id', 'primary_mirror_address', - 'product', 'secondary_mirror_address', 'software_version', - 'timelimited_modules', 'timezone', 'unicast_addresses'] - return generate_dict(devices, fields) - - -def generate_device_group_dict(f5, regex): - device_groups = DeviceGroups(f5.get_api(), regex) - fields = ['all_preferred_active', 'autosync_enabled_state', 'description', - 'device', 'full_load_on_sync_state', - 'incremental_config_sync_size_maximum', - 'network_failover_enabled_state', 'sync_status', 'type'] - return generate_dict(device_groups, fields) - - -def generate_traffic_group_dict(f5, regex): - traffic_groups = TrafficGroups(f5.get_api(), regex) - fields = ['auto_failback_enabled_state', 'auto_failback_time', - 'default_device', 'description', 'ha_load_factor', - 'ha_order', 'is_floating', 'mac_masquerade_address', - 'unit_id'] - return generate_dict(traffic_groups, fields) - - -def generate_rule_dict(f5, regex): - rules = Rules(f5.get_api(), regex) - fields = ['definition', 'description', 'ignore_vertification', - 'verification_status'] - return generate_dict(rules, fields) - - -def generate_node_dict(f5, regex): - nodes = Nodes(f5.get_api(), regex) - fields = ['name', 'address', 'connection_limit', 'description', 'dynamic_ratio', - 'monitor_instance', 'monitor_rule', 'monitor_status', - 'object_status', 'rate_limit', 'ratio', 'session_status'] - return generate_dict(nodes, fields) - - -def generate_virtual_address_dict(f5, regex): - virtual_addresses = VirtualAddresses(f5.get_api(), regex) - fields = ['address', 'arp_state', 'auto_delete_state', 'connection_limit', - 'description', 'enabled_state', 'icmp_echo_state', - 'is_floating_state', 'netmask', 'object_status', - 'route_advertisement_state', 'traffic_group'] - return generate_dict(virtual_addresses, fields) - - -def generate_address_class_dict(f5, regex): - address_classes = AddressClasses(f5.get_api(), regex) - fields = ['address_class', 'description'] - return generate_dict(address_classes, fields) - - -def generate_certificate_dict(f5, regex): - certificates = Certificates(f5.get_api(), regex) - return dict(zip(certificates.get_list(), certificates.get_certificate_list())) - - -def generate_key_dict(f5, regex): - keys = Keys(f5.get_api(), regex) - return dict(zip(keys.get_list(), keys.get_key_list())) - - -def generate_client_ssl_profile_dict(f5, regex): - profiles = ProfileClientSSL(f5.get_api(), regex) - fields = ['alert_timeout', 'allow_nonssl_state', 'authenticate_depth', - 'authenticate_once_state', 'ca_file', 'cache_size', - 'cache_timeout', 'certificate_file', 'chain_file', - 'cipher_list', 'client_certificate_ca_file', 'crl_file', - 'default_profile', 'description', - 'forward_proxy_ca_certificate_file', 'forward_proxy_ca_key_file', - 'forward_proxy_ca_passphrase', - 'forward_proxy_certificate_extension_include', - 'forward_proxy_certificate_lifespan', - 'forward_proxy_enabled_state', - 'forward_proxy_lookup_by_ipaddr_port_state', 'handshake_timeout', - 'key_file', 'modssl_emulation_state', 'passphrase', - 'peer_certification_mode', 'profile_mode', - 'renegotiation_maximum_record_delay', 'renegotiation_period', - 'renegotiation_state', 'renegotiation_throughput', - 'retain_certificate_state', 'secure_renegotiation_mode', - 'server_name', 'session_ticket_state', 'sni_default_state', - 'sni_require_state', 'ssl_option', 'strict_resume_state', - 'unclean_shutdown_state', 'is_base_profile', 'is_system_profile'] - return generate_dict(profiles, fields) - - -def generate_system_info_dict(f5): - system_info = SystemInfo(f5.get_api()) - fields = ['base_mac_address', - 'blade_temperature', 'chassis_slot_information', - 'globally_unique_identifier', 'group_id', - 'hardware_information', - 'marketing_name', - 'product_information', 'pva_version', 'system_id', - 'system_information', 'time', - 'time_zone', 'uptime'] - return generate_simple_dict(system_info, fields) - - -def generate_software_list(f5): - software = Software(f5.get_api()) - software_list = software.get_all_software_status() - return software_list - - -def generate_provision_dict(f5): - provisioned = ProvisionInfo(f5.get_api()) - fields = ['list', 'provisioned_list'] - return generate_simple_dict(provisioned, fields) - - -class ArgumentSpec(object): - def __init__(self): - self.supports_check_mode = False - argument_spec = dict( - session=dict(type='bool', default='no'), - include=dict( - type='raw', - required=True, - choices=[ - 'address_class', 'certificate', 'client_ssl_profile', 'device', - 'device_group', 'interface', 'key', 'node', 'pool', 'provision', - 'rule', 'self_ip', 'software', 'system_info', 'traffic_group', - 'trunk', 'virtual_address', 'virtual_server', 'vlan' - ] - ), - filter=dict(type='str'), - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec - ) - client = F5BaseClient(**module.params) - provider = client.merge_provider_params() - - if not bigsuds_found: - module.fail_json(msg="the python suds and bigsuds modules are required") - - server = provider['server'] - server_port = provider['server_port'] - user = provider['user'] - password = provider['password'] - validate_certs = provider['validate_certs'] - - session = module.params['session'] - fact_filter = module.params['filter'] - - if validate_certs: - import ssl - if not hasattr(ssl, 'SSLContext'): - module.fail_json( - msg='bigsuds does not support verifying certificates with python < 2.7.9. Either update python or set validate_certs=False on the task' - ) - - if fact_filter: - regex = fnmatch.translate(fact_filter) - else: - regex = None - if isinstance(module.params['include'], string_types): - includes = module.params['include'].split(',') - else: - includes = module.params['include'] - include = [x.lower() for x in includes] - valid_includes = ('address_class', 'certificate', 'client_ssl_profile', - 'device', 'device_group', 'interface', 'key', 'node', - 'pool', 'provision', 'rule', 'self_ip', 'software', - 'system_info', 'traffic_group', 'trunk', - 'virtual_address', 'virtual_server', 'vlan') - include_test = (x in valid_includes for x in include) - if not all(include_test): - module.fail_json(msg="Value of include must be one or more of: %s, got: %s" % (",".join(valid_includes), ",".join(include))) - - try: - facts = {} - - if len(include) > 0: - f5 = F5(server, user, password, session, validate_certs, server_port) - saved_active_folder = f5.get_active_folder() - saved_recursive_query_state = f5.get_recursive_query_state() - if saved_active_folder != "/": - f5.set_active_folder("/") - if saved_recursive_query_state != "STATE_ENABLED": - f5.enable_recursive_query_state() - - if 'interface' in include: - facts['interface'] = generate_interface_dict(f5, regex) - if 'self_ip' in include: - facts['self_ip'] = generate_self_ip_dict(f5, regex) - if 'trunk' in include: - facts['trunk'] = generate_trunk_dict(f5, regex) - if 'vlan' in include: - facts['vlan'] = generate_vlan_dict(f5, regex) - if 'virtual_server' in include: - facts['virtual_server'] = generate_vs_dict(f5, regex) - if 'pool' in include: - facts['pool'] = generate_pool_dict(f5, regex) - if 'provision' in include: - facts['provision'] = generate_provision_dict(f5) - if 'device' in include: - facts['device'] = generate_device_dict(f5, regex) - if 'device_group' in include: - facts['device_group'] = generate_device_group_dict(f5, regex) - if 'traffic_group' in include: - facts['traffic_group'] = generate_traffic_group_dict(f5, regex) - if 'rule' in include: - facts['rule'] = generate_rule_dict(f5, regex) - if 'node' in include: - facts['node'] = generate_node_dict(f5, regex) - if 'virtual_address' in include: - facts['virtual_address'] = generate_virtual_address_dict(f5, regex) - if 'address_class' in include: - facts['address_class'] = generate_address_class_dict(f5, regex) - if 'software' in include: - facts['software'] = generate_software_list(f5) - if 'certificate' in include: - facts['certificate'] = generate_certificate_dict(f5, regex) - if 'key' in include: - facts['key'] = generate_key_dict(f5, regex) - if 'client_ssl_profile' in include: - facts['client_ssl_profile'] = generate_client_ssl_profile_dict(f5, regex) - if 'system_info' in include: - facts['system_info'] = generate_system_info_dict(f5) - - # restore saved state - if saved_active_folder and saved_active_folder != "/": - f5.set_active_folder(saved_active_folder) - if saved_recursive_query_state and \ - saved_recursive_query_state != "STATE_ENABLED": - f5.set_recursive_query_state(saved_recursive_query_state) - - result = dict( - ansible_facts=facts, - ) - result.update(**facts) - - except Exception as e: - module.fail_json(msg="received exception: %s\ntraceback: %s" % (e, traceback.format_exc())) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_firewall_address_list.py b/plugins/modules/network/f5/bigip_firewall_address_list.py deleted file mode 100644 index 9b1216965a..0000000000 --- a/plugins/modules/network/f5/bigip_firewall_address_list.py +++ /dev/null @@ -1,979 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright: (c) 2017, F5 Networks 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': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigip_firewall_address_list -short_description: Manage address lists on BIG-IP AFM -description: - - Manages the AFM address lists on a BIG-IP. This module can be used to add - and remove address list entries. -options: - name: - description: - - Specifies the name of the address list. - type: str - required: True - partition: - description: - - Device partition to manage resources on. - type: str - default: Common - description: - description: - - Description of the address list - type: str - geo_locations: - description: - - List of geolocations specified by their C(country) and C(region). - suboptions: - country: - description: - - The country name, or code, of the geolocation to use. - - In addition to the country full names, you may also specify their abbreviated - form, such as C(US) instead of C(United States). - - Valid country codes can be found here https://countrycode.org/. - type: str - required: True - choices: - - Any valid 2 character ISO country code. - - Any valid country name. - region: - description: - - Region name of the country to use. - type: str - type: list - addresses: - description: - - Individual addresses that you want to add to the list. These addresses differ - from ranges, and lists of lists such as what can be used in C(address_ranges) - and C(address_lists) respectively. - - This list can also include networks that have CIDR notation. - type: list - address_ranges: - description: - - A list of address ranges where the range starts with a port number, is followed - by a dash (-) and then a second number. - - If the first address is greater than the second number, the numbers will be - reversed so-as to be properly formatted. ie, C(2.2.2.2-1.1.1). would become - C(1.1.1.1-2.2.2.2). - type: list - address_lists: - description: - - Simple list of existing address lists to add to this list. Address lists can be - specified in either their fully qualified name (/Common/foo) or their short - name (foo). If a short name is used, the C(partition) argument will automatically - be prepended to the short name. - type: list - fqdns: - description: - - A list of fully qualified domain names (FQDNs). - - An FQDN has at least one decimal point in it, separating the host from the domain. - - To add FQDNs to a list requires that a global FQDN resolver be configured. - At the moment, this must either be done via C(bigip_command), or, in the GUI - of BIG-IP. If using C(bigip_command), this can be done with C(tmsh modify security - firewall global-fqdn-policy FOO) where C(FOO) is a DNS resolver configured - at C(tmsh create net dns-resolver FOO). - type: list - state: - description: - - When C(present), ensures that the address list and entries exists. - - When C(absent), ensures the address list is removed. - type: str - choices: - - present - - absent - default: present -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -author: - - Tim Rupp (@caphrim007) - - Wojciech Wypior (@wojtek0806) -''' - -EXAMPLES = r''' -- name: Create an address list - bigip_firewall_address_list: - name: foo - addresses: - - 3.3.3.3 - - 4.4.4.4 - - 5.5.5.5 - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost -''' - -RETURN = r''' -description: - description: The new description of the address list. - returned: changed - type: str - sample: My address list -addresses: - description: The new list of addresses applied to the address list. - returned: changed - type: list - sample: [1.1.1.1, 2.2.2.2] -address_ranges: - description: The new list of address ranges applied to the address list. - returned: changed - type: list - sample: [1.1.1.1-2.2.2.2, 3.3.3.3-4.4.4.4] -address_lists: - description: The new list of address list names applied to the address list. - returned: changed - type: list - sample: [/Common/list1, /Common/list2] -fqdns: - description: The new list of FQDN names applied to the address list. - returned: changed - type: list - sample: [google.com, mit.edu] -geo_locations: - description: The new list of geo locations applied to the address list. - returned: changed - type: complex - contains: - country: - description: Country of the geo location. - returned: changed - type: str - sample: US - region: - description: Region of the geo location. - returned: changed - type: str - sample: California -''' - -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.basic import env_fallback - -try: - from library.module_utils.network.f5.bigip import F5RestClient - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import AnsibleF5Parameters - from library.module_utils.network.f5.common import fq_name - from library.module_utils.network.f5.common import f5_argument_spec - from library.module_utils.network.f5.common import transform_name - from library.module_utils.compat.ipaddress import ip_address - from library.module_utils.compat.ipaddress import ip_interface - from library.module_utils.network.f5.ipaddress import is_valid_ip - from library.module_utils.network.f5.ipaddress import is_valid_ip_interface -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.bigip import F5RestClient - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import fq_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import transform_name - from ansible_collections.ansible.netcommon.plugins.module_utils.compat.ipaddress import ip_address - from ansible_collections.ansible.netcommon.plugins.module_utils.compat.ipaddress import ip_interface - from ansible_collections.f5networks.f5_modules.plugins.module_utils.ipaddress import is_valid_ip - from ansible_collections.f5networks.f5_modules.plugins.module_utils.ipaddress import is_valid_ip_interface - - -class Parameters(AnsibleF5Parameters): - api_map = { - 'addressLists': 'address_lists', - 'geo': 'geo_locations', - } - - api_attributes = [ - 'addressLists', - 'addresses', - 'description', - 'fqdns', - 'geo', - ] - - returnables = [ - 'addresses', - 'address_ranges', - 'address_lists', - 'description', - 'fqdns', - 'geo_locations', - ] - - updatables = [ - 'addresses', - 'address_ranges', - 'address_lists', - 'description', - 'fqdns', - 'geo_locations', - ] - - -class ApiParameters(Parameters): - @property - def address_ranges(self): - if self._values['addresses'] is None: - return None - result = [] - for address_range in self._values['addresses']: - if '-' not in address_range['name']: - continue - result.append(address_range['name'].strip()) - result = sorted(result) - return result - - @property - def address_lists(self): - if self._values['address_lists'] is None: - return None - result = [] - for x in self._values['address_lists']: - item = '/{0}/{1}'.format(x['partition'], x['name']) - result.append(item) - result = sorted(result) - return result - - @property - def addresses(self): - if self._values['addresses'] is None: - return None - result = [x['name'] for x in self._values['addresses'] if '-' not in x['name']] - result = sorted(result) - return result - - @property - def fqdns(self): - if self._values['fqdns'] is None: - return None - result = [str(x['name']) for x in self._values['fqdns']] - result = sorted(result) - return result - - @property - def geo_locations(self): - if self._values['geo_locations'] is None: - return None - result = [str(x['name']) for x in self._values['geo_locations']] - result = sorted(result) - return result - - -class ModuleParameters(Parameters): - def __init__(self, params=None): - super(ModuleParameters, self).__init__(params=params) - self.country_iso_map = { - 'Afghanistan': 'AF', - 'Albania': 'AL', - 'Algeria': 'DZ', - 'American Samoa': 'AS', - 'Andorra': 'AD', - 'Angola': 'AO', - 'Anguilla': 'AI', - 'Antarctica': 'AQ', - 'Antigua and Barbuda': 'AG', - 'Argentina': 'AR', - 'Armenia': 'AM', - 'Aruba': 'AW', - 'Australia': 'AU', - 'Austria': 'AT', - 'Azerbaijan': 'AZ', - 'Bahamas': 'BS', - 'Bahrain': 'BH', - 'Bangladesh': 'BD', - 'Barbados': 'BB', - 'Belarus': 'BY', - 'Belgium': 'BE', - 'Belize': 'BZ', - 'Benin': 'BJ', - 'Bermuda': 'BM', - 'Bhutan': 'BT', - 'Bolivia': 'BO', - 'Bosnia and Herzegovina': 'BA', - 'Botswana': 'BW', - 'Brazil': 'BR', - 'Brunei': 'BN', - 'Bulgaria': 'BG', - 'Burkina Faso': 'BF', - 'Burundi': 'BI', - 'Cameroon': 'CM', - 'Canada': 'CA', - 'Cape Verde': 'CV', - 'Central African Republic': 'CF', - 'Chile': 'CL', - 'China': 'CN', - 'Christmas Island': 'CX', - 'Cocos Islands': 'CC', - 'Colombia': 'CO', - 'Cook Islands': 'CK', - 'Costa Rica': 'CR', - 'Cuba': 'CU', - 'Curacao': 'CW', - 'Cyprus': 'CY', - 'Czech Republic': 'CZ', - 'Democratic Republic of the Congo': 'CD', - 'Denmark': 'DK', - 'Djibouti': 'DJ', - 'Dominica': 'DM', - 'Dominican Republic': 'DO', - 'Ecuador': 'EC', - 'Egypt': 'EG', - 'Eritrea': 'ER', - 'Estonia': 'EE', - 'Ethiopia': 'ET', - 'Falkland Islands': 'FK', - 'Faroe Islands': 'FO', - 'Fiji': 'FJ', - 'Finland': 'FI', - 'France': 'FR', - 'French Polynesia': 'PF', - 'Gabon': 'GA', - 'Gambia': 'GM', - 'Georgia': 'GE', - 'Germany': 'DE', - 'Ghana': 'GH', - 'Gilbraltar': 'GI', - 'Greece': 'GR', - 'Greenland': 'GL', - 'Grenada': 'GD', - 'Guam': 'GU', - 'Guatemala': 'GT', - 'Guernsey': 'GG', - 'Guinea': 'GN', - 'Guinea-Bissau': 'GW', - 'Guyana': 'GY', - 'Haiti': 'HT', - 'Honduras': 'HN', - 'Hong Kong': 'HK', - 'Hungary': 'HU', - 'Iceland': 'IS', - 'India': 'IN', - 'Indonesia': 'ID', - 'Iran': 'IR', - 'Iraq': 'IQ', - 'Ireland': 'IE', - 'Isle of Man': 'IM', - 'Israel': 'IL', - 'Italy': 'IT', - 'Ivory Coast': 'CI', - 'Jamaica': 'JM', - 'Japan': 'JP', - 'Jersey': 'JE', - 'Jordan': 'JO', - 'Kazakhstan': 'KZ', - 'Laos': 'LA', - 'Latvia': 'LV', - 'Lebanon': 'LB', - 'Lesotho': 'LS', - 'Liberia': 'LR', - 'Libya': 'LY', - 'Liechtenstein': 'LI', - 'Lithuania': 'LT', - 'Luxembourg': 'LU', - 'Macau': 'MO', - 'Macedonia': 'MK', - 'Madagascar': 'MG', - 'Malawi': 'MW', - 'Malaysia': 'MY', - 'Maldives': 'MV', - 'Mali': 'ML', - 'Malta': 'MT', - 'Marshall Islands': 'MH', - 'Mauritania': 'MR', - 'Mauritius': 'MU', - 'Mayotte': 'YT', - 'Mexico': 'MX', - 'Micronesia': 'FM', - 'Moldova': 'MD', - 'Monaco': 'MC', - 'Mongolia': 'MN', - 'Montenegro': 'ME', - 'Montserrat': 'MS', - 'Morocco': 'MA', - 'Mozambique': 'MZ', - 'Myanmar': 'MM', - 'Namibia': 'NA', - 'Nauru': 'NR', - 'Nepal': 'NP', - 'Netherlands': 'NL', - 'Netherlands Antilles': 'AN', - 'New Caledonia': 'NC', - 'New Zealand': 'NZ', - 'Nicaragua': 'NI', - 'Niger': 'NE', - 'Nigeria': 'NG', - 'Niue': 'NU', - 'North Korea': 'KP', - 'Northern Mariana Islands': 'MP', - 'Norway': 'NO', - 'Oman': 'OM', - 'Pakistan': 'PK', - 'Palau': 'PW', - 'Palestine': 'PS', - 'Panama': 'PA', - 'Papua New Guinea': 'PG', - 'Paraguay': 'PY', - 'Peru': 'PE', - 'Philippines': 'PH', - 'Pitcairn': 'PN', - 'Poland': 'PL', - 'Portugal': 'PT', - 'Puerto Rico': 'PR', - 'Qatar': 'QA', - 'Republic of the Congo': 'CG', - 'Reunion': 'RE', - 'Romania': 'RO', - 'Russia': 'RU', - 'Rwanda': 'RW', - 'Saint Barthelemy': 'BL', - 'Saint Helena': 'SH', - 'Saint Kitts and Nevis': 'KN', - 'Saint Lucia': 'LC', - 'Saint Martin': 'MF', - 'Saint Pierre and Miquelon': 'PM', - 'Saint Vincent and the Grenadines': 'VC', - 'Samoa': 'WS', - 'San Marino': 'SM', - 'Sao Tome and Principe': 'ST', - 'Saudi Arabia': 'SA', - 'Senegal': 'SN', - 'Serbia': 'RS', - 'Seychelles': 'SC', - 'Sierra Leone': 'SL', - 'Singapore': 'SG', - 'Sint Maarten': 'SX', - 'Slovakia': 'SK', - 'Slovenia': 'SI', - 'Solomon Islands': 'SB', - 'Somalia': 'SO', - 'South Africa': 'ZA', - 'South Korea': 'KR', - 'South Sudan': 'SS', - 'Spain': 'ES', - 'Sri Lanka': 'LK', - 'Sudan': 'SD', - 'Suriname': 'SR', - 'Svalbard and Jan Mayen': 'SJ', - 'Swaziland': 'SZ', - 'Sweden': 'SE', - 'Switzerland': 'CH', - 'Syria': 'SY', - 'Taiwan': 'TW', - 'Tajikstan': 'TJ', - 'Tanzania': 'TZ', - 'Thailand': 'TH', - 'Togo': 'TG', - 'Tokelau': 'TK', - 'Tonga': 'TO', - 'Trinidad and Tobago': 'TT', - 'Tunisia': 'TN', - 'Turkey': 'TR', - 'Turkmenistan': 'TM', - 'Turks and Caicos Islands': 'TC', - 'Tuvalu': 'TV', - 'U.S. Virgin Islands': 'VI', - 'Uganda': 'UG', - 'Ukraine': 'UA', - 'United Arab Emirates': 'AE', - 'United Kingdom': 'GB', - 'United States': 'US', - 'Uruguay': 'UY', - 'Uzbekistan': 'UZ', - 'Vanuatu': 'VU', - 'Vatican': 'VA', - 'Venezuela': 'VE', - 'Vietnam': 'VN', - 'Wallis and Futuna': 'WF', - 'Western Sahara': 'EH', - 'Yemen': 'YE', - 'Zambia': 'ZM', - 'Zimbabwe': 'ZW' - } - self.choices_iso_codes = self.country_iso_map.values() - - def is_valid_hostname(self, host): - """Reasonable attempt at validating a hostname - - Compiled from various paragraphs outlined here - https://tools.ietf.org/html/rfc3696#section-2 - https://tools.ietf.org/html/rfc1123 - - Notably, - * Host software MUST handle host names of up to 63 characters and - SHOULD handle host names of up to 255 characters. - * The "LDH rule", after the characters that it permits. (letters, digits, hyphen) - * If the hyphen is used, it is not permitted to appear at - either the beginning or end of a label - - :param host: - :return: - """ - if len(host) > 255: - return False - host = host.rstrip(".") - allowed = re.compile(r'(?!-)[A-Z0-9-]{1,63}(? int(stop): - stop, start = start, stop - item = '{0}-{1}'.format(str(start), str(stop)) - result.append(item) - result = sorted(result) - return result - - @property - def address_lists(self): - if self._values['address_lists'] is None: - return None - result = [] - for x in self._values['address_lists']: - item = fq_name(self.partition, x) - result.append(item) - result = sorted(result) - return result - - @property - def fqdns(self): - if self._values['fqdns'] is None: - return None - result = [] - for x in self._values['fqdns']: - if self.is_valid_hostname(x): - result.append(x) - else: - raise F5ModuleError( - "The hostname '{0}' looks invalid.".format(x) - ) - result = sorted(result) - return result - - @property - def geo_locations(self): - if self._values['geo_locations'] is None: - return None - result = [] - for x in self._values['geo_locations']: - if x['region'] is not None and x['region'].strip() != '': - tmp = '{0}:{1}'.format(x['country'], x['region']) - else: - tmp = x['country'] - result.append(tmp) - result = sorted(result) - return result - - -class Changes(Parameters): - def to_return(self): - result = {} - try: - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - except Exception: - pass - return result - - -class ReportableChanges(Changes): - @property - def addresses(self): - result = [] - for item in self._values['addresses']: - if '-' in item['name']: - continue - result.append(item['name']) - return result - - @property - def address_ranges(self): - result = [] - for item in self._values['addresses']: - if '-' not in item['name']: - continue - start, stop = item['name'].split('-') - start = start.strip() - stop = stop.strip() - - start = ip_address(u'{0}'.format(start)) - stop = ip_address(u'{0}'.format(stop)) - if start.version != stop.version: - raise F5ModuleError( - "When specifying a range, IP addresses must be of the same type; IPv4 or IPv6." - ) - if int(start) > int(stop): - stop, start = start, stop - item = '{0}-{1}'.format(str(start), str(stop)) - result.append(item) - result = sorted(result) - return result - - @property - def address_lists(self): - result = [] - for x in self._values['address_lists']: - item = '/{0}/{1}'.format(x['partition'], x['name']) - result.append(item) - result = sorted(result) - return result - - -class UsableChanges(Changes): - @property - def addresses(self): - if self._values['addresses'] is None and self._values['address_ranges'] is None: - return None - result = [] - if self._values['addresses']: - result += [dict(name=str(x)) for x in self._values['addresses']] - if self._values['address_ranges']: - result += [dict(name=str(x)) for x in self._values['address_ranges']] - return result - - @property - def address_lists(self): - if self._values['address_lists'] is None: - return None - result = [] - for x in self._values['address_lists']: - partition, name = x.split('/')[1:] - result.append(dict( - name=name, - partition=partition - )) - return result - - -class Difference(object): - def __init__(self, want, have=None): - self.want = want - self.have = have - - def compare(self, param): - try: - result = getattr(self, param) - return result - except AttributeError: - return self.__default(param) - - def __default(self, param): - attr1 = getattr(self.want, param) - try: - attr2 = getattr(self.have, param) - if attr1 != attr2: - return attr1 - except AttributeError: - return attr1 - - @property - def addresses(self): - if self.want.addresses is None: - return None - elif self.have.addresses is None: - return self.want.addresses - if sorted(self.want.addresses) != sorted(self.have.addresses): - return self.want.addresses - - @property - def address_lists(self): - if self.want.address_lists is None: - return None - elif self.have.address_lists is None: - return self.want.address_lists - if sorted(self.want.address_lists) != sorted(self.have.address_lists): - return self.want.address_lists - - @property - def address_ranges(self): - if self.want.address_ranges is None: - return None - elif self.have.address_ranges is None: - return self.want.address_ranges - if sorted(self.want.address_ranges) != sorted(self.have.address_ranges): - return self.want.address_ranges - - @property - def fqdns(self): - if self.want.fqdns is None: - return None - elif self.have.fqdns is None: - return self.want.fqdns - if sorted(self.want.fqdns) != sorted(self.have.fqdns): - return self.want.fqdns - - -class ModuleManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = F5RestClient(**self.module.params) - self.want = ModuleParameters(params=self.module.params) - self.have = ApiParameters() - self.changes = UsableChanges() - - def _update_changed_options(self): - diff = Difference(self.want, self.have) - updatables = Parameters.updatables - changed = dict() - for k in updatables: - change = diff.compare(k) - if change is None: - continue - else: - if isinstance(change, dict): - changed.update(change) - else: - changed[k] = change - if changed: - self.changes = UsableChanges(params=changed) - return True - return False - - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - - def exec_module(self): - changed = False - result = dict() - state = self.want.state - - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - - reportable = ReportableChanges(params=self.changes.to_return()) - changes = reportable.to_return() - result.update(**changes) - result.update(dict(changed=changed)) - self._announce_deprecations(result) - return result - - def _announce_deprecations(self, result): - warnings = result.pop('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) - - def present(self): - if self.exists(): - return self.update() - else: - return self.create() - - def absent(self): - if self.exists(): - return self.remove() - return False - - def update(self): - self.have = self.read_current_from_device() - if not self.should_update(): - return False - if self.module.check_mode: - return True - self.update_on_device() - return True - - def remove(self): - if self.module.check_mode: - return True - self.remove_from_device() - if self.exists(): - raise F5ModuleError("Failed to delete the resource.") - return True - - def create(self): - self.have = ApiParameters() - self._update_changed_options() - if self.module.check_mode: - return True - self.create_on_device() - return True - - def exists(self): - uri = "https://{0}:{1}/mgmt/tm/security/firewall/address-list/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError: - return False - if resp.status == 404 or 'code' in response and response['code'] == 404: - return False - return True - - def create_on_device(self): - params = self.changes.api_params() - params['name'] = self.want.name - params['partition'] = self.want.partition - uri = "https://{0}:{1}/mgmt/tm/security/firewall/address-list/".format( - self.client.provider['server'], - self.client.provider['server_port'] - ) - resp = self.client.api.post(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - def update_on_device(self): - params = self.changes.api_params() - uri = "https://{0}:{1}/mgmt/tm/security/firewall/address-list/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.patch(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - def remove_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/security/firewall/address-list/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.delete(uri) - if resp.status == 200: - return True - raise F5ModuleError(resp.content) - - def read_current_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/security/firewall/address-list/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return ApiParameters(params=response) - - -class ArgumentSpec(object): - def __init__(self): - self.supports_check_mode = True - argument_spec = dict( - description=dict(), - name=dict(required=True), - addresses=dict(type='list'), - address_ranges=dict(type='list'), - address_lists=dict(type='list'), - geo_locations=dict( - type='list', - elements='dict', - options=dict( - country=dict( - required=True, - ), - region=dict() - ) - ), - fqdns=dict(type='list'), - partition=dict( - default='Common', - fallback=(env_fallback, ['F5_PARTITION']) - ), - state=dict( - default='present', - choices=['present', 'absent'] - ) - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec, - supports_check_mode=spec.supports_check_mode - ) - - try: - mm = ModuleManager(module=module) - results = mm.exec_module() - module.exit_json(**results) - except F5ModuleError as ex: - module.fail_json(msg=str(ex)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_firewall_port_list.py b/plugins/modules/network/f5/bigip_firewall_port_list.py deleted file mode 100644 index 8f19495e65..0000000000 --- a/plugins/modules/network/f5/bigip_firewall_port_list.py +++ /dev/null @@ -1,646 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright: (c) 2017, F5 Networks 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': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigip_firewall_port_list -short_description: Manage port lists on BIG-IP AFM -description: - - Manages the AFM port lists on a BIG-IP. This module can be used to add - and remove port list entries. -options: - name: - description: - - Specifies the name of the port list. - type: str - required: True - partition: - description: - - Device partition to manage resources on. - type: str - default: Common - description: - description: - - Description of the port list - type: str - ports: - description: - - Simple list of port values to add to the list - type: list - port_ranges: - description: - - A list of port ranges where the range starts with a port number, is followed - by a dash (-) and then a second number. - - If the first number is greater than the second number, the numbers will be - reversed so-as to be properly formatted. ie, 90-78 would become 78-90. - type: list - port_lists: - description: - - Simple list of existing port lists to add to this list. Port lists can be - specified in either their fully qualified name (/Common/foo) or their short - name (foo). If a short name is used, the C(partition) argument will automatically - be prepended to the short name. - type: list - state: - description: - - When C(present), ensures that the address list and entries exists. - - When C(absent), ensures the address list is removed. - type: str - choices: - - present - - absent - default: present -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -author: - - Tim Rupp (@caphrim007) - - Wojciech Wypior (@wojtek0806) -''' - -EXAMPLES = r''' -- name: Create a simple port list - bigip_firewall_port_list: - name: foo - ports: - - 80 - - 443 - state: present - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost - -- name: Override the above list of ports with a new list - bigip_firewall_port_list: - name: foo - ports: - - 3389 - - 8080 - - 25 - state: present - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost - -- name: Create port list with series of ranges - bigip_firewall_port_list: - name: foo - port_ranges: - - 25-30 - - 80-500 - - 50-78 - state: present - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost - -- name: Use multiple types of port arguments - bigip_firewall_port_list: - name: foo - port_ranges: - - 25-30 - - 80-500 - - 50-78 - ports: - - 8080 - - 443 - state: present - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost - -- name: Remove port list - bigip_firewall_port_list: - name: foo - state: absent - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost - -- name: Create port list from a file with one port per line - bigip_firewall_port_list: - name: lot-of-ports - ports: "{{ lookup('file', 'my-large-port-list.txt').split('\n') }}" - state: present - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost -''' - -RETURN = r''' -description: - description: The new description of the port list. - returned: changed - type: str - sample: My port list -ports: - description: The new list of ports applied to the port list. - returned: changed - type: list - sample: [80, 443] -port_ranges: - description: The new list of port ranges applied to the port list. - returned: changed - type: list - sample: [80-100, 200-8080] -port_lists: - description: The new list of port list names applied to the port list. - returned: changed - type: list - sample: [/Common/list1, /Common/list2] -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.basic import env_fallback - -try: - from library.module_utils.network.f5.bigip import F5RestClient - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import AnsibleF5Parameters - from library.module_utils.network.f5.common import fq_name - from library.module_utils.network.f5.common import f5_argument_spec - from library.module_utils.network.f5.common import transform_name - from library.module_utils.network.f5.icontrol import module_provisioned -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.bigip import F5RestClient - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import fq_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import transform_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.icontrol import module_provisioned - - -class Parameters(AnsibleF5Parameters): - api_map = { - 'portLists': 'port_lists', - } - - api_attributes = [ - 'portLists', 'ports', 'description', - ] - - returnables = [ - 'ports', 'port_ranges', 'port_lists', 'description', - ] - - updatables = [ - 'description', 'ports', 'port_ranges', 'port_lists', - ] - - -class ApiParameters(Parameters): - @property - def port_ranges(self): - if self._values['ports'] is None: - return None - result = [] - for port_range in self._values['ports']: - if '-' not in port_range['name']: - continue - start, stop = port_range['name'].split('-') - start = int(start.strip()) - stop = int(stop.strip()) - if start > stop: - stop, start = start, stop - item = '{0}-{1}'.format(start, stop) - result.append(item) - return result - - @property - def port_lists(self): - if self._values['port_lists'] is None: - return None - result = [] - for x in self._values['port_lists']: - item = '/{0}/{1}'.format(x['partition'], x['name']) - result.append(item) - return result - - @property - def ports(self): - if self._values['ports'] is None: - return None - result = [int(x['name']) for x in self._values['ports'] if '-' not in x['name']] - return result - - -class ModuleParameters(Parameters): - @property - def ports(self): - if self._values['ports'] is None: - return None - if any(x for x in self._values['ports'] if '-' in str(x)): - raise F5ModuleError( - "Ports must be whole numbers between 0 and 65,535" - ) - if any(x for x in self._values['ports'] if 0 < int(x) > 65535): - raise F5ModuleError( - "Ports must be whole numbers between 0 and 65,535" - ) - result = [int(x) for x in self._values['ports']] - return result - - @property - def port_ranges(self): - if self._values['port_ranges'] is None: - return None - result = [] - for port_range in self._values['port_ranges']: - if '-' not in port_range: - continue - start, stop = port_range.split('-') - start = int(start.strip()) - stop = int(stop.strip()) - if start > stop: - stop, start = start, stop - if 0 < start > 65535 or 0 < stop > 65535: - raise F5ModuleError( - "Ports must be whole numbers between 0 and 65,535" - ) - item = '{0}-{1}'.format(start, stop) - result.append(item) - return result - - @property - def port_lists(self): - if self._values['port_lists'] is None: - return None - result = [] - for x in self._values['port_lists']: - item = fq_name(self.partition, x) - result.append(item) - return result - - -class Changes(Parameters): - def to_return(self): - result = {} - try: - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - except Exception: - pass - return result - - -class ReportableChanges(Changes): - @property - def ports(self): - result = [] - for item in self._values['ports']: - if '-' in item['name']: - continue - result.append(item['name']) - return result - - @property - def port_ranges(self): - result = [] - for item in self._values['ports']: - if '-' not in item['name']: - continue - result.append(item['name']) - return result - - -class UsableChanges(Changes): - @property - def ports(self): - if self._values['ports'] is None and self._values['port_ranges'] is None: - return None - result = [] - if self._values['ports']: - # The values of the 'key' index literally need to be string values. - # If they are not, on BIG-IP 12.1.0 they will raise this REST exception. - # - # { - # "code": 400, - # "message": "one or more configuration identifiers must be provided", - # "errorStack": [], - # "apiError": 26214401 - # } - result += [dict(name=str(x)) for x in self._values['ports']] - if self._values['port_ranges']: - result += [dict(name=str(x)) for x in self._values['port_ranges']] - return result - - @property - def port_lists(self): - if self._values['port_lists'] is None: - return None - result = [] - for x in self._values['port_lists']: - partition, name = x.split('/')[1:] - result.append(dict( - name=name, - partition=partition - )) - return result - - -class Difference(object): - def __init__(self, want, have=None): - self.want = want - self.have = have - - def compare(self, param): - try: - result = getattr(self, param) - return result - except AttributeError: - return self.__default(param) - - def __default(self, param): - attr1 = getattr(self.want, param) - try: - attr2 = getattr(self.have, param) - if attr1 != attr2: - return attr1 - except AttributeError: - return attr1 - - @property - def ports(self): - if self.want.ports is None: - return None - elif self.have.ports is None: - return self.want.ports - if sorted(self.want.ports) != sorted(self.have.ports): - return self.want.ports - - @property - def port_lists(self): - if self.want.port_lists is None: - return None - elif self.have.port_lists is None: - return self.want.port_lists - if sorted(self.want.port_lists) != sorted(self.have.port_lists): - return self.want.port_lists - - @property - def port_ranges(self): - if self.want.port_ranges is None: - return None - elif self.have.port_ranges is None: - return self.want.port_ranges - if sorted(self.want.port_ranges) != sorted(self.have.port_ranges): - return self.want.port_ranges - - -class ModuleManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = F5RestClient(**self.module.params) - self.want = ModuleParameters(params=self.module.params) - self.have = ApiParameters() - self.changes = UsableChanges() - - def _set_changed_options(self): - changed = {} - for key in Parameters.returnables: - if getattr(self.want, key) is not None: - changed[key] = getattr(self.want, key) - if changed: - self.changes = UsableChanges(params=changed) - - def _update_changed_options(self): - diff = Difference(self.want, self.have) - updatables = Parameters.updatables - changed = dict() - for k in updatables: - change = diff.compare(k) - if change is None: - continue - else: - if isinstance(change, dict): - changed.update(change) - else: - changed[k] = change - if changed: - self.changes = UsableChanges(params=changed) - return True - return False - - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - - def exec_module(self): - if not module_provisioned(self.client, 'afm'): - raise F5ModuleError( - "AFM must be provisioned to use this module." - ) - changed = False - result = dict() - state = self.want.state - - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - - reportable = ReportableChanges(params=self.changes.to_return()) - changes = reportable.to_return() - result.update(**changes) - result.update(dict(changed=changed)) - self._announce_deprecations(result) - return result - - def _announce_deprecations(self, result): - warnings = result.pop('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) - - def present(self): - if self.exists(): - return self.update() - else: - return self.create() - - def absent(self): - if self.exists(): - return self.remove() - return False - - def update(self): - self.have = self.read_current_from_device() - if not self.should_update(): - return False - if self.module.check_mode: - return True - self.update_on_device() - return True - - def remove(self): - if self.module.check_mode: - return True - self.remove_from_device() - if self.exists(): - raise F5ModuleError("Failed to delete the resource.") - return True - - def create(self): - self._set_changed_options() - if self.module.check_mode: - return True - self.create_on_device() - return True - - def exists(self): - uri = "https://{0}:{1}/mgmt/tm/security/firewall/port-list/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError: - return False - if resp.status == 404 or 'code' in response and response['code'] == 404: - return False - return True - - def update_on_device(self): - params = self.changes.api_params() - uri = "https://{0}:{1}/mgmt/tm/security/firewall/port-list/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.patch(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - def read_current_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/security/firewall/port-list/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - resp = self.client.api.get(uri) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return ApiParameters(params=response) - - def create_on_device(self): - params = self.changes.api_params() - params['name'] = self.want.name - params['partition'] = self.want.partition - uri = "https://{0}:{1}/mgmt/tm/security/firewall/port-list/".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.post(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return response['selfLink'] - - def remove_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/security/firewall/port-list/{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(self.want.partition, self.want.name) - ) - response = self.client.api.delete(uri) - if response.status == 200: - return True - raise F5ModuleError(response.content) - - -class ArgumentSpec(object): - def __init__(self): - self.supports_check_mode = True - argument_spec = dict( - name=dict(required=True), - description=dict(), - ports=dict(type='list'), - port_ranges=dict(type='list'), - port_lists=dict(type='list'), - partition=dict( - default='Common', - fallback=(env_fallback, ['F5_PARTITION']) - ), - state=dict( - default='present', - choices=['present', 'absent'] - ) - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec, - supports_check_mode=spec.supports_check_mode - ) - - try: - mm = ModuleManager(module=module) - results = mm.exec_module() - module.exit_json(**results) - except F5ModuleError as ex: - module.fail_json(msg=str(ex)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_gtm_facts.py b/plugins/modules/network/f5/bigip_gtm_facts.py deleted file mode 100644 index 7f93bc80ba..0000000000 --- a/plugins/modules/network/f5/bigip_gtm_facts.py +++ /dev/null @@ -1,986 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright: (c) 2017, F5 Networks 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': ['deprecated'], - 'supported_by': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigip_gtm_facts -short_description: Collect facts from F5 BIG-IP GTM devices -description: - - Collect facts from F5 BIG-IP GTM devices. -options: - include: - description: - - Fact category to collect. - required: True - choices: - - pool - - wide_ip - - server - filter: - description: - - Perform regex filter of response. Filtering is done on the name of - the resource. Valid filters are anything that can be provided to - Python's C(re) module. -deprecated: - removed_in: '2.11' - alternative: bigip_device_info - why: > - The bigip_gtm_facts module is an outlier as all facts are being collected - in the bigip_device_info module. Additionally, the M(bigip_device_info) - module is easier to maintain and use. -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -notes: - - This module is deprecated. Use the C(bigip_device_info) module instead. -author: - - Tim Rupp (@caphrim007) -''' - -EXAMPLES = r''' -- name: Get pool facts - bigip_gtm_facts: - server: lb.mydomain.com - user: admin - password: secret - include: pool - filter: my_pool - delegate_to: localhost -''' - -RETURN = r''' -wide_ip: - description: - Contains the lb method for the wide ip and the pools that are within the wide ip. - returned: changed - type: list - sample: - wide_ip: - - enabled: True - failure_rcode: noerror - failure_rcode_response: disabled - failure_rcode_ttl: 0 - full_path: /Common/foo.ok.com - last_resort_pool: "" - minimal_response: enabled - name: foo.ok.com - partition: Common - persist_cidr_ipv4: 32 - persist_cidr_ipv6: 128 - persistence: disabled - pool_lb_mode: round-robin - pools: - - name: d3qw - order: 0 - partition: Common - ratio: 1 - ttl_persistence: 3600 - type: naptr -pool: - description: Contains the pool object status and enabled status. - returned: changed - type: list - sample: - pool: - - alternate_mode: round-robin - dynamic_ratio: disabled - enabled: True - fallback_mode: return-to-dns - full_path: /Common/d3qw - load_balancing_mode: round-robin - manual_resume: disabled - max_answers_returned: 1 - members: - - disabled: True - flags: a - full_path: ok3.com - member_order: 0 - name: ok3.com - order: 10 - preference: 10 - ratio: 1 - service: 80 - name: d3qw - partition: Common - qos_hit_ratio: 5 - qos_hops: 0 - qos_kilobytes_second: 3 - qos_lcs: 30 - qos_packet_rate: 1 - qos_rtt: 50 - qos_topology: 0 - qos_vs_capacity: 0 - qos_vs_score: 0 - availability_state: offline - enabled_state: disabled - ttl: 30 - type: naptr - verify_member_availability: disabled -server: - description: - Contains the virtual server enabled and availability status, and address. - returned: changed - type: list - sample: - server: - - addresses: - - device_name: /Common/qweqwe - name: 10.10.10.10 - translation: none - datacenter: /Common/xfxgh - enabled: True - expose_route_domains: no - full_path: /Common/qweqwe - iq_allow_path: yes - iq_allow_service_check: yes - iq_allow_snmp: yes - limit_cpu_usage: 0 - limit_cpu_usage_status: disabled - limit_max_bps: 0 - limit_max_bps_status: disabled - limit_max_connections: 0 - limit_max_connections_status: disabled - limit_max_pps: 0 - limit_max_pps_status: disabled - limit_mem_avail: 0 - limit_mem_avail_status: disabled - link_discovery: disabled - monitor: /Common/bigip - name: qweqwe - partition: Common - product: single-bigip - virtual_server_discovery: disabled - virtual_servers: - - destination: 10.10.10.10:0 - enabled: True - full_path: jsdfhsd - limit_max_bps: 0 - limit_max_bps_status: disabled - limit_max_connections: 0 - limit_max_connections_status: disabled - limit_max_pps: 0 - limit_max_pps_status: disabled - name: jsdfhsd - translation_address: none - translation_port: 0 -''' - -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE -from distutils.version import LooseVersion - -try: - from f5.bigip import ManagementRoot - from icontrol.exceptions import iControlUnexpectedHTTPError - from f5.utils.responses.handlers import Stats - HAS_F5SDK = True -except ImportError: - HAS_F5SDK = False - -try: - from library.module_utils.network.f5.common import F5BaseClient -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5BaseClient - -try: - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import AnsibleF5Parameters - from library.module_utils.network.f5.common import f5_argument_spec -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - - -class F5Client(F5BaseClient): - def __init__(self, *args, **kwargs): - super(F5Client, self).__init__(*args, **kwargs) - self.provider = self.merge_provider_params() - - @property - def api(self): - if self._client: - return self._client - - try: - result = ManagementRoot( - self.provider['server'], - self.provider['user'], - self.provider['password'], - port=self.provider['server_port'], - verify=self.provider['validate_certs'], - token='tmos' - ) - self._client = result - return self._client - except Exception as ex: - error = 'Unable to connect to {0} on port {1}. The reported error was "{2}".'.format( - self.provider['server'], self.provider['server_port'], str(ex) - ) - raise F5ModuleError(error) - - -class BaseManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = kwargs.get('client', None) - self.kwargs = kwargs - - self.types = dict( - a_s='a', - aaaas='aaaa', - cnames='cname', - mxs='mx', - naptrs='naptr', - srvs='srv' - ) - - def filter_matches_name(self, name): - if self.want.filter is None: - return True - matches = re.match(self.want.filter, str(name)) - if matches: - return True - else: - return False - - def version_is_less_than_12(self): - version = self.client.api.tmos_version - if LooseVersion(version) < LooseVersion('12.0.0'): - return True - else: - return False - - def get_facts_from_collection(self, collection, collection_type=None): - results = [] - for item in collection: - if not self.filter_matches_name(item.name): - continue - facts = self.format_facts(item, collection_type) - results.append(facts) - return results - - def read_stats_from_device(self, resource): - stats = Stats(resource.stats.load()) - return stats.stat - - -class UntypedManager(BaseManager): - def exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - filtered = [(k, v) for k, v in iteritems(attrs) if self.filter_matches_name(k)] - if filtered: - results.append(dict(filtered)) - return results - - -class TypedManager(BaseManager): - def exec_module(self): - results = [] - for collection, type in iteritems(self.types): - facts = self.read_facts(collection) - if not facts: - continue - for x in facts: - x.update({'type': type}) - for item in facts: - attrs = item.to_return() - filtered = [(k, v) for k, v in iteritems(attrs) if self.filter_matches_name(k)] - if filtered: - results.append(dict(filtered)) - return results - - -class Parameters(AnsibleF5Parameters): - @property - def include(self): - requested = self._values['include'] - valid = ['pool', 'wide_ip', 'server', 'all'] - - if any(x for x in requested if x not in valid): - raise F5ModuleError( - "The valid 'include' choices are {0}".format(', '.join(valid)) - ) - - if 'all' in requested: - return ['all'] - else: - return requested - - -class BaseParameters(Parameters): - @property - def enabled(self): - if self._values['enabled'] is None: - return None - elif self._values['enabled'] in BOOLEANS_TRUE: - return True - else: - return False - - @property - def disabled(self): - if self._values['disabled'] is None: - return None - elif self._values['disabled'] in BOOLEANS_TRUE: - return True - else: - return False - - def _remove_internal_keywords(self, resource): - resource.pop('kind', None) - resource.pop('generation', None) - resource.pop('selfLink', None) - resource.pop('isSubcollection', None) - resource.pop('fullPath', None) - - def to_return(self): - result = {} - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - - -class PoolParameters(BaseParameters): - api_map = { - 'alternateMode': 'alternate_mode', - 'dynamicRatio': 'dynamic_ratio', - 'fallbackMode': 'fallback_mode', - 'fullPath': 'full_path', - 'loadBalancingMode': 'load_balancing_mode', - 'manualResume': 'manual_resume', - 'maxAnswersReturned': 'max_answers_returned', - 'qosHitRatio': 'qos_hit_ratio', - 'qosHops': 'qos_hops', - 'qosKilobytesSecond': 'qos_kilobytes_second', - 'qosLcs': 'qos_lcs', - 'qosPacketRate': 'qos_packet_rate', - 'qosRtt': 'qos_rtt', - 'qosTopology': 'qos_topology', - 'qosVsCapacity': 'qos_vs_capacity', - 'qosVsScore': 'qos_vs_score', - 'verifyMemberAvailability': 'verify_member_availability', - 'membersReference': 'members' - } - - returnables = [ - 'alternate_mode', 'dynamic_ratio', 'enabled', 'disabled', 'fallback_mode', - 'load_balancing_mode', 'manual_resume', 'max_answers_returned', 'members', - 'name', 'partition', 'qos_hit_ratio', 'qos_hops', 'qos_kilobytes_second', - 'qos_lcs', 'qos_packet_rate', 'qos_rtt', 'qos_topology', 'qos_vs_capacity', - 'qos_vs_score', 'ttl', 'type', 'full_path', 'availability_state', - 'enabled_state', 'availability_status' - ] - - @property - def max_answers_returned(self): - if self._values['max_answers_returned'] is None: - return None - return int(self._values['max_answers_returned']) - - @property - def members(self): - result = [] - if self._values['members'] is None or 'items' not in self._values['members']: - return result - for item in self._values['members']['items']: - self._remove_internal_keywords(item) - if 'disabled' in item: - if item['disabled'] in BOOLEANS_TRUE: - item['disabled'] = True - else: - item['disabled'] = False - if 'enabled' in item: - if item['enabled'] in BOOLEANS_TRUE: - item['enabled'] = True - else: - item['enabled'] = False - if 'fullPath' in item: - item['full_path'] = item.pop('fullPath') - if 'memberOrder' in item: - item['member_order'] = int(item.pop('memberOrder')) - # Cast some attributes to integer - for x in ['order', 'preference', 'ratio', 'service']: - if x in item: - item[x] = int(item[x]) - result.append(item) - return result - - @property - def qos_hit_ratio(self): - if self._values['qos_hit_ratio'] is None: - return None - return int(self._values['qos_hit_ratio']) - - @property - def qos_hops(self): - if self._values['qos_hops'] is None: - return None - return int(self._values['qos_hops']) - - @property - def qos_kilobytes_second(self): - if self._values['qos_kilobytes_second'] is None: - return None - return int(self._values['qos_kilobytes_second']) - - @property - def qos_lcs(self): - if self._values['qos_lcs'] is None: - return None - return int(self._values['qos_lcs']) - - @property - def qos_packet_rate(self): - if self._values['qos_packet_rate'] is None: - return None - return int(self._values['qos_packet_rate']) - - @property - def qos_rtt(self): - if self._values['qos_rtt'] is None: - return None - return int(self._values['qos_rtt']) - - @property - def qos_topology(self): - if self._values['qos_topology'] is None: - return None - return int(self._values['qos_topology']) - - @property - def qos_vs_capacity(self): - if self._values['qos_vs_capacity'] is None: - return None - return int(self._values['qos_vs_capacity']) - - @property - def qos_vs_score(self): - if self._values['qos_vs_score'] is None: - return None - return int(self._values['qos_vs_score']) - - @property - def availability_state(self): - if self._values['stats'] is None: - return None - try: - result = self._values['stats']['status_availabilityState'] - return result['description'] - except AttributeError: - return None - - @property - def enabled_state(self): - if self._values['stats'] is None: - return None - try: - result = self._values['stats']['status_enabledState'] - return result['description'] - except AttributeError: - return None - - @property - def availability_status(self): - # This fact is a combination of the availability_state and enabled_state - # - # The purpose of the fact is to give a higher-level view of the availability - # of the pool, that can be used in playbooks. If you need further detail, - # consider using the following facts together. - # - # - availability_state - # - enabled_state - # - if self.enabled_state == 'enabled': - if self.availability_state == 'offline': - return 'red' - elif self.availability_state == 'available': - return 'green' - elif self.availability_state == 'unknown': - return 'blue' - else: - return 'none' - else: - # disabled - return 'black' - - -class WideIpParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'failureRcode': 'failure_return_code', - 'failureRcodeResponse': 'failure_return_code_response', - 'failureRcodeTtl': 'failure_return_code_ttl', - 'lastResortPool': 'last_resort_pool', - 'minimalResponse': 'minimal_response', - 'persistCidrIpv4': 'persist_cidr_ipv4', - 'persistCidrIpv6': 'persist_cidr_ipv6', - 'poolLbMode': 'pool_lb_mode', - 'ttlPersistence': 'ttl_persistence' - } - - returnables = [ - 'full_path', 'description', 'enabled', 'disabled', 'failure_return_code', - 'failure_return_code_response', 'failure_return_code_ttl', 'last_resort_pool', - 'minimal_response', 'persist_cidr_ipv4', 'persist_cidr_ipv6', 'pool_lb_mode', - 'ttl_persistence', 'pools' - ] - - @property - def pools(self): - result = [] - if self._values['pools'] is None: - return [] - for pool in self._values['pools']: - del pool['nameReference'] - for x in ['order', 'ratio']: - if x in pool: - pool[x] = int(pool[x]) - result.append(pool) - return result - - @property - def failure_return_code_ttl(self): - if self._values['failure_return_code_ttl'] is None: - return None - return int(self._values['failure_return_code_ttl']) - - @property - def persist_cidr_ipv4(self): - if self._values['persist_cidr_ipv4'] is None: - return None - return int(self._values['persist_cidr_ipv4']) - - @property - def persist_cidr_ipv6(self): - if self._values['persist_cidr_ipv6'] is None: - return None - return int(self._values['persist_cidr_ipv6']) - - @property - def ttl_persistence(self): - if self._values['ttl_persistence'] is None: - return None - return int(self._values['ttl_persistence']) - - -class ServerParameters(BaseParameters): - api_map = { - 'fullPath': 'full_path', - 'exposeRouteDomains': 'expose_route_domains', - 'iqAllowPath': 'iq_allow_path', - 'iqAllowServiceCheck': 'iq_allow_service_check', - 'iqAllowSnmp': 'iq_allow_snmp', - 'limitCpuUsage': 'limit_cpu_usage', - 'limitCpuUsageStatus': 'limit_cpu_usage_status', - 'limitMaxBps': 'limit_max_bps', - 'limitMaxBpsStatus': 'limit_max_bps_status', - 'limitMaxConnections': 'limit_max_connections', - 'limitMaxConnectionsStatus': 'limit_max_connections_status', - 'limitMaxPps': 'limit_max_pps', - 'limitMaxPpsStatus': 'limit_max_pps_status', - 'limitMemAvail': 'limit_mem_available', - 'limitMemAvailStatus': 'limit_mem_available_status', - 'linkDiscovery': 'link_discovery', - 'proberFallback': 'prober_fallback', - 'proberPreference': 'prober_preference', - 'virtualServerDiscovery': 'virtual_server_discovery', - 'devicesReference': 'devices', - 'virtualServersReference': 'virtual_servers' - } - - returnables = [ - 'datacenter', 'enabled', 'disabled', 'expose_route_domains', 'iq_allow_path', - 'full_path', 'iq_allow_service_check', 'iq_allow_snmp', 'limit_cpu_usage', - 'limit_cpu_usage_status', 'limit_max_bps', 'limit_max_bps_status', - 'limit_max_connections', 'limit_max_connections_status', 'limit_max_pps', - 'limit_max_pps_status', 'limit_mem_available', 'limit_mem_available_status', - 'link_discovery', 'monitor', 'product', 'prober_fallback', 'prober_preference', - 'virtual_server_discovery', 'addresses', 'devices', 'virtual_servers' - ] - - @property - def product(self): - if self._values['product'] is None: - return None - if self._values['product'] in ['single-bigip', 'redundant-bigip']: - return 'bigip' - return self._values['product'] - - @property - def devices(self): - result = [] - if self._values['devices'] is None or 'items' not in self._values['devices']: - return result - for item in self._values['devices']['items']: - self._remove_internal_keywords(item) - if 'fullPath' in item: - item['full_path'] = item.pop('fullPath') - result.append(item) - return result - - @property - def virtual_servers(self): - result = [] - if self._values['virtual_servers'] is None or 'items' not in self._values['virtual_servers']: - return result - for item in self._values['virtual_servers']['items']: - self._remove_internal_keywords(item) - if 'disabled' in item: - if item['disabled'] in BOOLEANS_TRUE: - item['disabled'] = True - else: - item['disabled'] = False - if 'enabled' in item: - if item['enabled'] in BOOLEANS_TRUE: - item['enabled'] = True - else: - item['enabled'] = False - if 'fullPath' in item: - item['full_path'] = item.pop('fullPath') - if 'limitMaxBps' in item: - item['limit_max_bps'] = int(item.pop('limitMaxBps')) - if 'limitMaxBpsStatus' in item: - item['limit_max_bps_status'] = item.pop('limitMaxBpsStatus') - if 'limitMaxConnections' in item: - item['limit_max_connections'] = int(item.pop('limitMaxConnections')) - if 'limitMaxConnectionsStatus' in item: - item['limit_max_connections_status'] = item.pop('limitMaxConnectionsStatus') - if 'limitMaxPps' in item: - item['limit_max_pps'] = int(item.pop('limitMaxPps')) - if 'limitMaxPpsStatus' in item: - item['limit_max_pps_status'] = item.pop('limitMaxPpsStatus') - if 'translationAddress' in item: - item['translation_address'] = item.pop('translationAddress') - if 'translationPort' in item: - item['translation_port'] = int(item.pop('translationPort')) - result.append(item) - return result - - @property - def limit_cpu_usage(self): - if self._values['limit_cpu_usage'] is None: - return None - return int(self._values['limit_cpu_usage']) - - @property - def limit_max_bps(self): - if self._values['limit_max_bps'] is None: - return None - return int(self._values['limit_max_bps']) - - @property - def limit_max_connections(self): - if self._values['limit_max_connections'] is None: - return None - return int(self._values['limit_max_connections']) - - @property - def limit_max_pps(self): - if self._values['limit_max_pps'] is None: - return None - return int(self._values['limit_max_pps']) - - @property - def limit_mem_available(self): - if self._values['limit_mem_available'] is None: - return None - return int(self._values['limit_mem_available']) - - -class PoolFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = kwargs.get('client', None) - super(PoolFactManager, self).__init__(**kwargs) - self.kwargs = kwargs - - def exec_module(self): - if self.version_is_less_than_12(): - manager = self.get_manager('untyped') - else: - manager = self.get_manager('typed') - facts = manager.exec_module() - result = dict(pool=facts) - return result - - def get_manager(self, type): - if type == 'typed': - return TypedPoolFactManager(**self.kwargs) - elif type == 'untyped': - return UntypedPoolFactManager(**self.kwargs) - - -class TypedPoolFactManager(TypedManager): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = kwargs.get('client', None) - super(TypedPoolFactManager, self).__init__(**kwargs) - self.want = PoolParameters(params=self.module.params) - - def read_facts(self, collection): - results = [] - collection = self.read_collection_from_device(collection) - for resource in collection: - attrs = resource.attrs - attrs['stats'] = self.read_stats_from_device(resource) - params = PoolParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self, collection_name): - pools = self.client.api.tm.gtm.pools - collection = getattr(pools, collection_name) - result = collection.get_collection( - requests_params=dict( - params='expandSubcollections=true' - ) - ) - return result - - -class UntypedPoolFactManager(UntypedManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(UntypedPoolFactManager, self).__init__(**kwargs) - self.want = PoolParameters(params=self.module.params) - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource.attrs - attrs['stats'] = self.read_stats_from_device(resource) - params = PoolParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - result = self.client.api.tm.gtm.pools.get_collection( - requests_params=dict( - params='expandSubcollections=true' - ) - ) - return result - - -class WideIpFactManager(BaseManager): - def exec_module(self): - if self.version_is_less_than_12(): - manager = self.get_manager('untyped') - else: - manager = self.get_manager('typed') - facts = manager.exec_module() - result = dict(wide_ip=facts) - return result - - def get_manager(self, type): - if type == 'typed': - return TypedWideIpFactManager(**self.kwargs) - elif type == 'untyped': - return UntypedWideIpFactManager(**self.kwargs) - - -class TypedWideIpFactManager(TypedManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(TypedWideIpFactManager, self).__init__(**kwargs) - self.want = WideIpParameters(params=self.module.params) - - def read_facts(self, collection): - results = [] - collection = self.read_collection_from_device(collection) - for resource in collection: - attrs = resource.attrs - params = WideIpParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self, collection_name): - wideips = self.client.api.tm.gtm.wideips - collection = getattr(wideips, collection_name) - result = collection.get_collection( - requests_params=dict( - params='expandSubcollections=true' - ) - ) - return result - - -class UntypedWideIpFactManager(UntypedManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(UntypedWideIpFactManager, self).__init__(**kwargs) - self.want = WideIpParameters(params=self.module.params) - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource.attrs - params = WideIpParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - result = self.client.api.tm.gtm.wideips.get_collection( - requests_params=dict( - params='expandSubcollections=true' - ) - ) - return result - - -class ServerFactManager(UntypedManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(ServerFactManager, self).__init__(**kwargs) - self.want = ServerParameters(params=self.module.params) - - def exec_module(self): - facts = super(ServerFactManager, self).exec_module() - result = dict(server=facts) - return result - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - attrs = resource.attrs - params = ServerParameters(params=attrs) - results.append(params) - return results - - def read_collection_from_device(self): - result = self.client.api.tm.gtm.servers.get_collection( - requests_params=dict( - params='expandSubcollections=true' - ) - ) - return result - - -class ModuleManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = kwargs.get('client', None) - self.kwargs = kwargs - self.want = Parameters(params=self.module.params) - - def exec_module(self): - if not self.gtm_provisioned(): - raise F5ModuleError( - "GTM must be provisioned to use this module." - ) - - if 'all' in self.want.include: - names = ['pool', 'wide_ip', 'server'] - else: - names = self.want.include - managers = [self.get_manager(name) for name in names] - result = self.execute_managers(managers) - if result: - result['changed'] = True - else: - result['changed'] = False - self._announce_deprecations() - return result - - def _announce_deprecations(self): - warnings = [] - if self.want: - warnings += self.want._values.get('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) - - def execute_managers(self, managers): - results = dict() - for manager in managers: - result = manager.exec_module() - results.update(result) - return results - - def get_manager(self, which): - if 'pool' == which: - return PoolFactManager(**self.kwargs) - if 'wide_ip' == which: - return WideIpFactManager(**self.kwargs) - if 'server' == which: - return ServerFactManager(**self.kwargs) - - def gtm_provisioned(self): - resource = self.client.api.tm.sys.dbs.db.load( - name='provisioned.cpu.gtm' - ) - if int(resource.value) == 0: - return False - return True - - -class ArgumentSpec(object): - def __init__(self): - self.supports_check_mode = False - argument_spec = dict( - include=dict( - type='list', - choices=[ - 'pool', - 'wide_ip', - 'server', - ], - required=True - ), - filter=dict() - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec, - supports_check_mode=spec.supports_check_mode - ) - if not HAS_F5SDK: - module.fail_json(msg="The python f5-sdk module is required") - - client = F5Client(**module.params) - - try: - mm = ModuleManager(module=module, client=client) - results = mm.exec_module() - module.exit_json(**results) - except F5ModuleError as ex: - module.fail_json(msg=str(ex)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_iapplx_package.py b/plugins/modules/network/f5/bigip_iapplx_package.py deleted file mode 120000 index 45a77871e2..0000000000 --- a/plugins/modules/network/f5/bigip_iapplx_package.py +++ /dev/null @@ -1 +0,0 @@ -bigip_lx_package.py \ No newline at end of file diff --git a/plugins/modules/network/f5/bigip_lx_package.py b/plugins/modules/network/f5/bigip_lx_package.py deleted file mode 100644 index 2eb3b51b43..0000000000 --- a/plugins/modules/network/f5/bigip_lx_package.py +++ /dev/null @@ -1,481 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright: (c) 2017, F5 Networks 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': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigip_lx_package -short_description: Manages Javascript LX packages on a BIG-IP -description: - - Manages Javascript LX packages on a BIG-IP. This module will allow - you to deploy LX packages to the BIG-IP and manage their lifecycle. -options: - package: - description: - - The LX package that you want to upload or remove. When C(state) is C(present), - and you intend to use this module in a C(role), it is recommended that you use - the C({{ role_path }}) variable. An example is provided in the C(EXAMPLES) section. - - When C(state) is C(absent), it is not necessary for the package to exist on the - Ansible controller. If the full path to the package is provided, the filename will - specifically be cherry picked from it to properly remove the package. - type: path - state: - description: - - Whether the LX package should exist or not. - type: str - default: present - choices: - - present - - absent -notes: - - Requires the rpm tool be installed on the host. This can be accomplished through - different ways on each platform. On Debian based systems with C(apt); - C(apt-get install rpm). On Mac with C(brew); C(brew install rpm). - This command is already present on RedHat based systems. - - Requires BIG-IP >= 12.1.0 because the required functionality is missing - on versions earlier than that. - - The module name C(bigip_iapplx_package) has been deprecated in favor of C(bigip_lx_package). -requirements: - - Requires BIG-IP >= 12.1.0 - - The 'rpm' tool installed on the Ansible controller -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -author: - - Tim Rupp (@caphrim007) - - Wojciech Wypior (@wojtek0806) -''' - -EXAMPLES = r''' -- name: Install AS3 - bigip_lx_package: - package: f5-appsvcs-3.5.0-3.noarch.rpm - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost - -- name: Add an LX package stored in a role - bigip_lx_package: - package: "{{ roles_path }}/files/MyApp-0.1.0-0001.noarch.rpm'" - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost - -- name: Remove an LX package - bigip_lx_package: - package: MyApp-0.1.0-0001.noarch.rpm - state: absent - provider: - password: secret - server: lb.mydomain.com - user: admin - delegate_to: localhost -''' - -RETURN = r''' -# only common fields returned -''' - -import os -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import urlparse -from distutils.version import LooseVersion - -try: - from library.module_utils.network.f5.bigip import F5RestClient - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import AnsibleF5Parameters - from library.module_utils.network.f5.common import f5_argument_spec - from library.module_utils.network.f5.icontrol import tmos_version - from library.module_utils.network.f5.icontrol import upload_file -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.bigip import F5RestClient - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - from ansible_collections.f5networks.f5_modules.plugins.module_utils.icontrol import tmos_version - from ansible_collections.f5networks.f5_modules.plugins.module_utils.icontrol import upload_file - - -class Parameters(AnsibleF5Parameters): - api_attributes = [] - returnables = [] - - @property - def package(self): - if self._values['package'] is None: - return None - return self._values['package'] - - @property - def package_file(self): - if self._values['package'] is None: - return None - return os.path.basename(self._values['package']) - - @property - def package_name(self): - """Return a valid name for the package - - BIG-IP determines the package name by the content of the RPM info. - It does not use the filename. Therefore, we do the same. This method - is only used though when the file actually exists on your Ansible - controller. - - If the package does not exist, then we instead use the filename - portion of the 'package' argument that is provided. - - Non-existence typically occurs when using 'state' = 'absent' - - :return: - """ - cmd = ['rpm', '-qp', '--queryformat', '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}', self.package] - rc, out, err = self._module.run_command(cmd) - if not out: - return str(self.package_file) - return out - - @property - def package_root(self): - if self._values['package'] is None: - return None - base = os.path.basename(self._values['package']) - result = os.path.splitext(base) - return result[0] - - -class ApiParameters(Parameters): - pass - - -class ModuleParameters(Parameters): - pass - - -class Changes(Parameters): - def to_return(self): - result = {} - try: - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - except Exception: - pass - return result - - -class UsableChanges(Changes): - pass - - -class ReportableChanges(Changes): - pass - - -class ModuleManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = F5RestClient(**self.module.params) - self.want = ModuleParameters(module=self.module, params=self.module.params) - self.changes = UsableChanges() - - def exec_module(self): - result = dict() - changed = False - state = self.want.state - - version = tmos_version(self.client) - if LooseVersion(version) <= LooseVersion('12.0.0'): - raise F5ModuleError( - "This version of BIG-IP is not supported." - ) - - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - - changes = self.changes.to_return() - result.update(**changes) - result.update(dict(changed=changed)) - return result - - def present(self): - if self.exists(): - return False - else: - return self.create() - - def absent(self): - changed = False - if self.exists(): - changed = self.remove() - return changed - - def remove(self): - if self.module.check_mode: - return True - self.remove_from_device() - if self.exists(): - raise F5ModuleError("Failed to delete the LX package.") - return True - - def create(self): - if self.module.check_mode: - return True - if not os.path.exists(self.want.package): - if self.want.package.startswith('/'): - raise F5ModuleError( - "The specified LX package was not found at {0}.".format(self.want.package) - ) - else: - raise F5ModuleError( - "The specified LX package was not found in {0}.".format(os.getcwd()) - ) - self.upload_to_device() - self.create_on_device() - self.enable_iapplx_on_device() - self.remove_package_file_from_device() - if self.exists(): - return True - else: - raise F5ModuleError("Failed to install LX package.") - - def exists(self): - exists = False - packages = self.get_installed_packages_on_device() - if os.path.exists(self.want.package): - exists = True - for package in packages: - if exists: - if self.want.package_name == package['packageName']: - return True - else: - if self.want.package_root == package['packageName']: - return True - return False - - def get_installed_packages_on_device(self): - uri = "https://{0}:{1}/mgmt/shared/iapp/package-management-tasks".format( - self.client.provider['server'], - self.client.provider['server_port'] - ) - params = dict(operation='QUERY') - resp = self.client.api.post(uri, json=params) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - path = urlparse(response["selfLink"]).path - task = self._wait_for_task(path) - - if task['status'] == 'FINISHED': - return task['queryResponse'] - raise F5ModuleError( - "Failed to find the installed packages on the device." - ) - - def _wait_for_task(self, path): - task = None - for x in range(0, 60): - task = self.check_task_on_device(path) - if task['status'] in ['FINISHED', 'FAILED']: - return task - time.sleep(1) - return task - - def check_task_on_device(self, path): - uri = "https://{0}:{1}{2}".format( - self.client.provider['server'], - self.client.provider['server_port'], - path - ) - resp = self.client.api.get(uri) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - return response - - def upload_to_device(self): - url = 'https://{0}:{1}/mgmt/shared/file-transfer/uploads'.format( - self.client.provider['server'], - self.client.provider['server_port'] - ) - try: - upload_file(self.client, url, self.want.package) - except F5ModuleError: - raise F5ModuleError( - "Failed to upload the file." - ) - - def remove_package_file_from_device(self): - params = dict( - command="run", - utilCmdArgs="/var/config/rest/downloads/{0}".format(self.want.package_file) - ) - uri = "https://{0}:{1}/mgmt/tm/util/unix-rm".format( - self.client.provider['server'], - self.client.provider['server_port'] - ) - resp = self.client.api.post(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - def create_on_device(self): - remote_path = "/var/config/rest/downloads/{0}".format(self.want.package_file) - params = dict( - operation='INSTALL', packageFilePath=remote_path - ) - uri = "https://{0}:{1}/mgmt/shared/iapp/package-management-tasks".format( - self.client.provider['server'], - self.client.provider['server_port'] - ) - - resp = self.client.api.post(uri, json=params) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - path = urlparse(response["selfLink"]).path - task = self._wait_for_task(path) - - if task['status'] == 'FINISHED': - return True - else: - raise F5ModuleError(task['errorMessage']) - - def remove_from_device(self): - params = dict( - operation='UNINSTALL', - packageName=self.want.package_root - ) - uri = "https://{0}:{1}/mgmt/shared/iapp/package-management-tasks".format( - self.client.provider['server'], - self.client.provider['server_port'] - ) - - resp = self.client.api.post(uri, json=params) - - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - path = urlparse(response["selfLink"]).path - task = self._wait_for_task(path) - - if task['status'] == 'FINISHED': - return True - return False - - def enable_iapplx_on_device(self): - params = dict( - command="run", - utilCmdArgs='-c "touch /var/config/rest/iapps/enable"' - ) - uri = "https://{0}:{1}/mgmt/tm/util/bash".format( - self.client.provider['server'], - self.client.provider['server_port'] - ) - resp = self.client.api.post(uri, json=params) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] in [400, 403]: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - -class ArgumentSpec(object): - def __init__(self): - self.supports_check_mode = True - argument_spec = dict( - state=dict( - default='present', - choices=['present', 'absent'] - ), - package=dict(type='path') - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - self.required_if = [ - ['state', 'present', ['package']] - ] - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec, - supports_check_mode=spec.supports_check_mode, - required_if=spec.required_if - ) - - try: - mm = ModuleManager(module=module) - results = mm.exec_module() - module.exit_json(**results) - except F5ModuleError as ex: - module.fail_json(msg=str(ex)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/f5/bigip_security_address_list.py b/plugins/modules/network/f5/bigip_security_address_list.py deleted file mode 120000 index 6bef19600f..0000000000 --- a/plugins/modules/network/f5/bigip_security_address_list.py +++ /dev/null @@ -1 +0,0 @@ -bigip_firewall_address_list.py \ No newline at end of file diff --git a/plugins/modules/network/f5/bigip_security_port_list.py b/plugins/modules/network/f5/bigip_security_port_list.py deleted file mode 120000 index a78176b5bf..0000000000 --- a/plugins/modules/network/f5/bigip_security_port_list.py +++ /dev/null @@ -1 +0,0 @@ -bigip_firewall_port_list.py \ No newline at end of file diff --git a/plugins/modules/network/f5/bigip_traffic_group.py b/plugins/modules/network/f5/bigip_traffic_group.py deleted file mode 120000 index 7caabb783b..0000000000 --- a/plugins/modules/network/f5/bigip_traffic_group.py +++ /dev/null @@ -1 +0,0 @@ -bigip_device_traffic_group.py \ No newline at end of file diff --git a/plugins/modules/network/f5/bigiq_device_facts.py b/plugins/modules/network/f5/bigiq_device_facts.py deleted file mode 120000 index e6543531fc..0000000000 --- a/plugins/modules/network/f5/bigiq_device_facts.py +++ /dev/null @@ -1 +0,0 @@ -bigiq_device_info.py \ No newline at end of file diff --git a/plugins/modules/network/f5/bigiq_device_info.py b/plugins/modules/network/f5/bigiq_device_info.py deleted file mode 100644 index 863fc4eccd..0000000000 --- a/plugins/modules/network/f5/bigiq_device_info.py +++ /dev/null @@ -1,2314 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2018 F5 Networks 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': 'certified'} - -DOCUMENTATION = r''' ---- -module: bigiq_device_info -short_description: Collect information from F5 BIG-IQ devices -description: - - Collect information from F5 BIG-IQ devices. - - This module was called C(bigiq_device_facts) before Ansible 2.9. The usage did not change. -options: - gather_subset: - description: - - When supplied, this argument will restrict the information returned to a given subset. - - Can specify a list of values to include a larger subset. - - Values can also be used with an initial C(!) to specify that a specific subset - should not be collected. - type: list - required: True - choices: - - all - - applications - - managed-devices - - purchased-pool-licenses - - regkey-pools - - system-info - - vlans - - "!all" - - "!applications" - - "!managed-devices" - - "!purchased-pool-licenses" - - "!regkey-pools" - - "!system-info" - - "!vlans" -extends_documentation_fragment: -- f5networks.f5_modules.f5 - -author: - - Tim Rupp (@caphrim007) -''' - -EXAMPLES = r''' -- name: Collect BIG-IQ information - bigiq_device_info: - gather_subset: - - system-info - - vlans - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Collect all BIG-IQ information - bigiq_device_info: - gather_subset: - - all - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost - -- name: Collect all BIG-IP information except trunks - bigiq_device_info: - gather_subset: - - all - - "!trunks" - provider: - server: lb.mydomain.com - user: admin - password: secret - delegate_to: localhost -''' - -RETURN = r''' -applications: - description: Application related information - returned: When C(managed-devices) is specified in C(gather_subset). - type: complex - contains: - protection_mode: - description: - - The type of F5 Web Application Security Service protection on the application. - returned: changed - type: str - sample: Not Protected - id: - description: - - ID of the application as known to the BIG-IQ. - returned: changed - type: str - sample: 996baae8-5d1d-3662-8a2d-3612fa2aceae - name: - description: - - Name of the application. - returned: changed - type: str - sample: site12http.example.com - status: - description: - - Current state of the application. - returned: changed - type: str - sample: DEPLOYED - transactions_per_second: - description: - - Current measurement of Transactions Per second being handled by the application. - returned: changed - type: float - sample: 0.87 - connections: - description: - - Current number of connections established to the application. - returned: changed - type: float - sample: 3.06 - new_connections: - description: - - Number of new connections being established per second. - returned: changed - type: float - sample: 0.35 - response_time: - description: - - Measured response time of the application in milliseconds. - returned: changed - type: float - sample: 0.02 - health: - description: - - Health of the application. - returned: changed - type: str - sample: Good - active_alerts: - description: - - Number of alerts active on the application. - returned: changed - type: int - sample: 0 - bad_traffic: - description: - - Percent of traffic to application that is determined to be 'bad'. - - This value is dependent on C(protection_mode) being enabled. - returned: changed - type: float - sample: 1.7498 - enhanced_analytics: - description: - - Whether enhanced analytics is enabled for the application or not. - returned: changed - type: bool - sample: yes - bad_traffic_growth: - description: - - Whether or not Bad Traffic Growth alerts are configured to be triggered or not. - returned: changed - type: bool - sample: no - sample: hash/dictionary of values -managed_devices: - description: Managed device related information. - returned: When C(managed-devices) is specified in C(gather_subset). - type: complex - contains: - address: - description: - - Address where the device was discovered. - returned: changed - type: str - sample: 10.10.10.10 - build: - description: - - Build of the version. - returned: changed - type: str - sample: 0.0.4 - device_uri: - description: - - URI to reach the management interface of the device. - returned: changed - type: str - sample: "https://10.10.10.10:443" - edition: - description: - - Edition string of the product version. - returned: changed - type: str - sample: Final - group_name: - description: - - BIG-IQ group that the device is a member of. - returned: changed - type: str - sample: cm-bigip-allBigIpDevices - hostname: - description: - - Discovered hostname of the device. - returned: changed - type: str - sample: tier2labB1.lab.fp.foo.com - https_port: - description: - - HTTPS port available on the management interface of the device. - returned: changed - type: int - sample: 443 - is_clustered: - description: - - Whether the device is clustered or not. - returned: changed - type: bool - sample: no - is_license_expired: - description: - - Whether the license on the device is expired or not. - returned: changed - type: bool - sample: yes - is_virtual: - description: - - Whether the device is a virtual edition or not. - returned: changed - type: bool - sample: yes - machine_id: - description: - - Machine specific ID assigned to this device by BIG-IQ. - returned: changed - type: str - sample: c141bc88-f734-4434-be64-a3e9ea98356e - management_address: - description: - - IP address of the management interface on the device. - returned: changed - type: str - sample: 10.10.10.10 - mcp_device_name: - description: - - Device name as known by MCPD on the BIG-IP. - returned: changed - type: str - sample: /Common/tier2labB1.lab.fp.foo.com - product: - description: - - Product that the managed device is identified as. - returned: changed - type: str - sample: BIG-IP - rest_framework_version: - description: - - REST framework version running on the device - returned: changed - type: str - sample: 13.1.1-0.0.4 - self_link: - description: - - Internal reference to the managed device in BIG-IQ. - returned: changed - type: str - sample: "https://localhost/mgmt/shared/resolver/device-groups/cm-bigip-allBigIpDevices/devices/c141bc88-f734-4434-be64-a3e9ea98356e" - slots: - description: - - Volumes on the device and versions of software installed in those volumes. - returned: changed - type: complex - sample: {"volume": "HD1.1", "product": "BIG-IP", "version": "13.1.1", "build": "0.0.4", "isActive": "yes"} - state: - description: - - State of the device. - returned: changed - type: str - sample: ACTIVE - tags: - description: - - Misc tags that are assigned to the device. - returned: changed - type: complex - sample: {'BIGIQ_tier_2_device': '2018-08-22T13:30:47.693-07:00', 'BIGIQ_SSG_name': 'tim-ssg'} - trust_domain_guid: - description: - - GUID of the trust domain the device is part of. - returned: changed - type: str - sample: 40ddf541-e604-4905-bde3005056813e36 - uuid: - description: - - UUID of the device in BIG-IQ. - returned: changed - type: str - sample: c141bc88-f734-4434-be64-a3e9ea98356e - version: - description: - - Version of TMOS installed on the device. - returned: changed - type: str - sample: 13.1.1 - sample: hash/dictionary of values -purchased_pool_licenses: - description: Purchased Pool License related information. - returned: When C(purchased-pool-licenses) is specified in C(gather_subset). - type: complex - contains: - base_reg_key: - description: - - Base registration key of the purchased pool - returned: changed - type: str - sample: XXXXX-XXXXX-XXXXX-XXXXX-XXXXXXX - dossier: - description: - - Dossier of the purchased pool license - returned: changed - type: str - sample: d6bd4b8ba5...e9a1a1199b73af9932948a - free_device_licenses: - description: - - Number of free licenses remaining. - returned: changed - type: int - sample: 34 - name: - description: - - Name of the purchased pool - returned: changed - type: str - sample: my-pool1 - state: - description: - - State of the purchased pool license - returned: changed - type: str - sample: LICENSED - total_device_licenses: - description: - - Total number of licenses in the pool. - returned: changed - type: int - sample: 40 - uuid: - description: - - UUID of the purchased pool license - returned: changed - type: str - sample: b2112329-cba7-4f1f-9a26-fab9be416d60 - vendor: - description: - - Vendor who provided the license - returned: changed - type: str - sample: F5 Networks, Inc - licensed_date_time: - description: - - Timestamp that the pool was licensed. - returned: changed - type: str - sample: "2018-09-10T00:00:00-07:00" - licensed_version: - description: - - Version of BIG-IQ that is licensed. - returned: changed - type: str - sample: 6.0.1 - evaluation_start_date_time: - description: - - Date that evaluation license starts. - returned: changed - type: str - sample: "2018-09-09T00:00:00-07:00" - evaluation_end_date_time: - description: - - Date that evaluation license ends. - returned: changed - type: str - sample: "2018-10-11T00:00:00-07:00" - license_end_date_time: - description: - - Date that the license expires. - returned: changed - type: str - sample: "2018-10-11T00:00:00-07:00" - license_start_date_time: - description: - - Date that the license starts. - returned: changed - type: str - sample: "2018-09-09T00:00:00-07:00" - registration_key: - description: - - Purchased pool license key. - returned: changed - type: str - sample: XXXXX-XXXXX-XXXXX-XXXXX-XXXXXXX - sample: hash/dictionary of values -regkey_pools: - description: Regkey Pool related information. - returned: When C(regkey-pools) is specified in C(gather_subset). - type: complex - contains: - name: - description: - - Name of the regkey pool. - returned: changed - type: str - sample: pool1 - id: - description: - - ID of the regkey pool. - returned: changed - type: str - sample: 4f9b565c-0831-4657-b6c2-6dde6182a502 - total_offerings: - description: - - Total number of offerings in the pool - returned: changed - type: int - sample: 10 - offerings: - description: List of the offerings in the pool. - type: complex - contains: - dossier: - description: - - Dossier of the license. - returned: changed - type: str - sample: d6bd4b8ba5...e9a1a1199b73af9932948a - name: - description: - - Name of the regkey. - returned: changed - type: str - sample: regkey1 - state: - description: - - State of the regkey license - returned: changed - type: str - sample: LICENSED - licensed_date_time: - description: - - Timestamp that the regkey was licensed. - returned: changed - type: str - sample: "2018-09-10T00:00:00-07:00" - licensed_version: - description: - - Version of BIG-IQ that is licensed. - returned: changed - type: str - sample: 6.0.1 - evaluation_start_date_time: - description: - - Date that evaluation license starts. - returned: changed - type: str - sample: "2018-09-09T00:00:00-07:00" - evaluation_end_date_time: - description: - - Date that evaluation license ends. - returned: changed - type: str - sample: "2018-10-11T00:00:00-07:00" - license_end_date_time: - description: - - Date that the license expires. - returned: changed - type: str - sample: "2018-10-11T00:00:00-07:00" - license_start_date_time: - description: - - Date that the license starts. - returned: changed - type: str - sample: "2018-09-09T00:00:00-07:00" - registration_key: - description: - - Registration license key. - returned: changed - type: str - sample: XXXXX-XXXXX-XXXXX-XXXXX-XXXXXXX - sample: hash/dictionary of values - sample: hash/dictionary of values -system_info: - description: System info related information. - returned: When C(system-info) is specified in C(gather_subset). - type: complex - contains: - base_mac_address: - description: - - Media Access Control address (MAC address) of the device. - returned: changed - type: str - sample: "fa:16:3e:c3:42:6f" - marketing_name: - description: - - Marketing name of the device platform. - returned: changed - type: str - sample: BIG-IQ Virtual Edition - time: - description: - - Mapping of the current time information to specific time-named keys. - returned: changed - type: complex - contains: - day: - description: - - The current day of the month, in numeric form. - returned: changed - type: int - sample: 7 - hour: - description: - - The current hour of the day in 24-hour form. - returned: changed - type: int - sample: 18 - minute: - description: - - The current minute of the hour. - returned: changed - type: int - sample: 16 - month: - description: - - The current month, in numeric form. - returned: changed - type: int - sample: 6 - second: - description: - - The current second of the minute. - returned: changed - type: int - sample: 51 - year: - description: - - The current year in 4-digit form. - returned: changed - type: int - sample: 2018 - hardware_information: - description: - - Information related to the hardware (drives and CPUs) of the system. - type: complex - returned: changed - contains: - model: - description: - - The model of the hardware. - type: str - sample: Virtual Disk - name: - description: - - The name of the hardware. - type: str - sample: HD1 - type: - description: - - The type of hardware. - type: str - sample: physical-disk - versions: - description: - - Hardware specific properties - type: complex - contains: - name: - description: - - Name of the property - type: str - sample: Size - version: - description: - - Value of the property - type: str - sample: 154.00G - is_admin_password_changed: - description: - - Whether the admin password was changed from its default or not. - returned: changed - type: bool - sample: yes - is_root_password_changed: - description: - - Whether the root password was changed from its default or not. - returned: changed - type: bool - sample: no - is_system_setup: - description: - - Whether the system has been setup or not. - returned: changed - type: bool - sample: yes - package_edition: - description: - - Displays the software edition. - returned: changed - type: str - sample: Point Release 7 - package_version: - description: - - A string combining the C(product_build) and C(product_build_date). - type: str - sample: "Build 0.0.1 - Tue May 15 15:26:30 PDT 2018" - product_code: - description: - - Code identifying the product. - type: str - sample: BIG-IQ - product_build: - description: - - Build version of the release version. - type: str - sample: 0.0.1 - product_version: - description: - - Major product version of the running software. - type: str - sample: 6.0.0 - product_built: - description: - - Unix timestamp of when the product was built. - type: int - sample: 180515152630 - product_build_date: - description: - - Human readable build date. - type: str - sample: "Tue May 15 15:26:30 PDT 2018" - product_changelist: - description: - - Changelist that product branches from. - type: int - sample: 2557198 - product_jobid: - description: - - ID of the job that built the product version. - type: int - sample: 1012030 - chassis_serial: - description: - - Serial of the chassis - type: str - sample: 11111111-2222-3333-444444444444 - host_board_part_revision: - description: - - Revision of the host board. - type: str - host_board_serial: - description: - - Serial of the host board. - type: str - platform: - description: - - Platform identifier. - type: str - sample: Z100 - switch_board_part_revision: - description: - - Switch board revision. - type: str - switch_board_serial: - description: - - Serial of the switch board. - type: str - uptime: - description: - - Time, in seconds, since the system booted. - type: int - sample: 603202 - sample: hash/dictionary of values -vlans: - description: List of VLAN information. - returned: When C(vlans) is specified in C(gather_subset). - type: complex - contains: - auto_lasthop: - description: - - Allows the system to send return traffic to the MAC address that transmitted the - request, even if the routing table points to a different network or interface. - returned: changed - type: str - sample: enabled - cmp_hash_algorithm: - description: - - Specifies how the traffic on the VLAN will be disaggregated. - returned: changed - type: str - sample: default - description: - description: - - Description of the VLAN. - returned: changed - type: str - sample: My vlan - failsafe_action: - description: - - Action for the system to take when the fail-safe mechanism is triggered. - returned: changed - type: str - sample: reboot - failsafe_enabled: - description: - - Whether failsafe is enabled or not. - returned: changed - type: bool - sample: yes - failsafe_timeout: - description: - - Number of seconds that an active unit can run without detecting network traffic - on this VLAN before it starts a failover. - returned: changed - type: int - sample: 90 - if_index: - description: - - Index assigned to this VLAN. It is a unique identifier assigned for all objects - displayed in the SNMP IF-MIB. - returned: changed - type: int - sample: 176 - learning_mode: - description: - - Whether switch ports placed in the VLAN are configured for switch learning, - forwarding only, or dropped. - returned: changed - type: str - sample: enable-forward - interfaces: - description: - - List of tagged or untagged interfaces and trunks that you want to configure for the VLAN. - returned: changed - type: complex - contains: - full_path: - description: - - Full name of the resource as known to BIG-IP. - returned: changed - type: str - sample: 1.3 - name: - description: - - Relative name of the resource in BIG-IP. - returned: changed - type: str - sample: 1.3 - tagged: - description: - - Whether the interface is tagged or not. - returned: changed - type: bool - sample: no - mtu: - description: - - Specific maximum transition unit (MTU) for the VLAN. - returned: changed - type: int - sample: 1500 - sflow_poll_interval: - description: - - Maximum interval in seconds between two pollings. - returned: changed - type: int - sample: 0 - sflow_poll_interval_global: - description: - - Whether the global VLAN poll-interval setting, overrides the object-level - poll-interval setting. - returned: changed - type: bool - sample: no - sflow_sampling_rate: - description: - - Ratio of packets observed to the samples generated. - returned: changed - type: int - sample: 0 - sflow_sampling_rate_global: - description: - - Whether the global VLAN sampling-rate setting, overrides the object-level - sampling-rate setting. - returned: changed - type: bool - sample: yes - source_check_enabled: - description: - - Specifies that only connections that have a return route in the routing table are accepted. - returned: changed - type: bool - sample: yes - true_mac_address: - description: - - Media access control (MAC) address for the lowest-numbered interface assigned to this VLAN. - returned: changed - type: str - sample: "fa:16:3e:10:da:ff" - tag: - description: - - Tag number for the VLAN. - returned: changed - type: int - sample: 30 - sample: hash/dictionary of values -''' - -import datetime -import math -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible.module_utils.six import string_types - -try: - from library.module_utils.network.f5.bigiq import F5RestClient - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import AnsibleF5Parameters - from library.module_utils.network.f5.common import f5_argument_spec - from library.module_utils.network.f5.common import fq_name - from library.module_utils.network.f5.common import flatten_boolean - from library.module_utils.network.f5.ipaddress import is_valid_ip - from library.module_utils.network.f5.common import transform_name -except ImportError: - from ansible_collections.f5networks.f5_modules.plugins.module_utils.bigiq import F5RestClient - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import fq_name - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import flatten_boolean - from ansible_collections.f5networks.f5_modules.plugins.module_utils.ipaddress import is_valid_ip - from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import transform_name - - -def parseStats(entry): - if 'description' in entry: - return entry['description'] - elif 'value' in entry: - return entry['value'] - elif 'entries' in entry or 'nestedStats' in entry and 'entries' in entry['nestedStats']: - if 'entries' in entry: - entries = entry['entries'] - else: - entries = entry['nestedStats']['entries'] - result = None - - for name in entries: - entry = entries[name] - if 'https://localhost' in name: - name = name.split('/') - name = name[-1] - if result and isinstance(result, list): - result.append(parseStats(entry)) - elif result and isinstance(result, dict): - result[name] = parseStats(entry) - else: - try: - int(name) - result = list() - result.append(parseStats(entry)) - except ValueError: - result = dict() - result[name] = parseStats(entry) - else: - if '.' in name: - names = name.split('.') - key = names[0] - value = names[1] - if not result[key]: - result[key] = {} - result[key][value] = parseStats(entry) - else: - if result and isinstance(result, list): - result.append(parseStats(entry)) - elif result and isinstance(result, dict): - result[name] = parseStats(entry) - else: - try: - int(name) - result = list() - result.append(parseStats(entry)) - except ValueError: - result = dict() - result[name] = parseStats(entry) - return result - - -class BaseManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = kwargs.get('client', None) - self.kwargs = kwargs - - def exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - return results - - -class Parameters(AnsibleF5Parameters): - @property - def gather_subset(self): - if isinstance(self._values['gather_subset'], string_types): - self._values['gather_subset'] = [self._values['gather_subset']] - elif not isinstance(self._values['gather_subset'], list): - raise F5ModuleError( - "The specified gather_subset must be a list." - ) - tmp = list(set(self._values['gather_subset'])) - tmp.sort() - self._values['gather_subset'] = tmp - - return self._values['gather_subset'] - - -class BaseParameters(Parameters): - @property - def enabled(self): - return flatten_boolean(self._values['enabled']) - - @property - def disabled(self): - return flatten_boolean(self._values['disabled']) - - def _remove_internal_keywords(self, resource): - resource.pop('kind', None) - resource.pop('generation', None) - resource.pop('selfLink', None) - resource.pop('isSubcollection', None) - resource.pop('fullPath', None) - - def to_return(self): - result = {} - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - - -class ApplicationsParameters(BaseParameters): - api_map = { - 'protectionMode': 'protection_mode', - 'transactionsPerSecond': 'transactions_per_second', - 'newConnections': 'new_connections', - 'responseTime': 'response_time', - 'activeAlerts': 'active_alerts', - 'badTraffic': 'bad_traffic', - 'enhancedAnalytics': 'enhanced_analytics', - 'badTrafficGrowth': 'bad_traffic_growth' - } - - returnables = [ - 'protection_mode', - 'id', - 'name', - 'status', - 'transactions_per_second', - 'connections', - 'new_connections', - 'response_time', - 'health', - 'active_alerts', - 'bad_traffic', - 'enhanced_analytics', - 'bad_traffic_growth', - ] - - @property - def enhanced_analytics(self): - return flatten_boolean(self._values['enhanced_analytics']) - - @property - def bad_traffic_growth(self): - return flatten_boolean(self._values['bad_traffic_growth']) - - -class ApplicationsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(ApplicationsFactManager, self).__init__(**kwargs) - self.want = ApplicationsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(applications=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['name']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = ApplicationsParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/ap/query/v1/tenants/default/reports/AllApplicationsList".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - try: - return response['result']['items'] - except KeyError: - return [] - - -class ManagedDevicesParameters(BaseParameters): - api_map = { - 'deviceUri': 'device_uri', - 'groupName': 'group_name', - 'httpsPort': 'https_port', - 'isClustered': 'is_clustered', - 'isLicenseExpired': 'is_license_expired', - 'isVirtual': 'is_virtual', - 'machineId': 'machine_id', - 'managementAddress': 'management_address', - 'mcpDeviceName': 'mcp_device_name', - 'restFrameworkVersion': 'rest_framework_version', - 'selfLink': 'self_link', - 'trustDomainGuid': 'trust_domain_guid', - } - - returnables = [ - 'address', - 'build', - 'device_uri', - 'edition', - 'group_name', - 'hostname', - 'https_port', - 'is_clustered', - 'is_license_expired', - 'is_virtual', - 'machine_id', - 'management_address', - 'mcp_device_name', - 'product', - 'rest_framework_version', - 'self_link', - 'slots', - 'state', - 'tags', - 'trust_domain_guid', - 'uuid', - 'version', - ] - - @property - def slots(self): - result = [] - if self._values['slots'] is None: - return None - for x in self._values['slots']: - x['is_active'] = flatten_boolean(x.pop('isActive', False)) - result.append(x) - return result - - @property - def tags(self): - if self._values['tags'] is None: - return None - result = dict((x['name'], x['value']) for x in self._values['tags']) - return result - - @property - def https_port(self): - return int(self._values['https_port']) - - @property - def is_clustered(self): - return flatten_boolean(self._values['is_clustered']) - - @property - def is_license_expired(self): - return flatten_boolean(self._values['is_license_expired']) - - @property - def is_virtual(self): - return flatten_boolean(self._values['is_virtual']) - - -class ManagedDevicesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(ManagedDevicesFactManager, self).__init__(**kwargs) - self.want = ManagedDevicesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(managed_devices=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['hostname']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = ManagedDevicesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/shared/resolver/device-groups/cm-bigip-allBigIpDevices/devices".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class PurchasedPoolLicensesParameters(BaseParameters): - api_map = { - 'baseRegKey': 'base_reg_key', - 'freeDeviceLicenses': 'free_device_licenses', - 'licenseState': 'license_state', - 'totalDeviceLicenses': 'total_device_licenses', - } - - returnables = [ - 'base_reg_key', - 'dossier', - 'free_device_licenses', - 'name', - 'state', - 'total_device_licenses', - 'uuid', - - # license_state facts - 'vendor', - 'licensed_date_time', - 'licensed_version', - 'evaluation_start_date_time', - 'evaluation_end_date_time', - 'license_end_date_time', - 'license_start_date_time', - 'registration_key', - ] - - @property - def registration_key(self): - try: - return self._values['license_state']['registrationKey'] - except KeyError: - return None - - @property - def license_start_date_time(self): - try: - return self._values['license_state']['licenseStartDateTime'] - except KeyError: - return None - - @property - def license_end_date_time(self): - try: - return self._values['license_state']['licenseEndDateTime'] - except KeyError: - return None - - @property - def evaluation_end_date_time(self): - try: - return self._values['license_state']['evaluationEndDateTime'] - except KeyError: - return None - - @property - def evaluation_start_date_time(self): - try: - return self._values['license_state']['evaluationStartDateTime'] - except KeyError: - return None - - @property - def licensed_version(self): - try: - return self._values['license_state']['licensedVersion'] - except KeyError: - return None - - @property - def licensed_date_time(self): - try: - return self._values['license_state']['licensedDateTime'] - except KeyError: - return None - - @property - def vendor(self): - try: - return self._values['license_state']['vendor'] - except KeyError: - return None - - -class PurchasedPoolLicensesFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(PurchasedPoolLicensesFactManager, self).__init__(**kwargs) - self.want = PurchasedPoolLicensesParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(purchased_pool_licenses=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['name']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = PurchasedPoolLicensesParameters(params=resource) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/cm/device/licensing/pool/purchased-pool/licenses".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - try: - return response['items'] - except KeyError: - return [] - - -class RegkeyPoolsParameters(BaseParameters): - api_map = { - - } - - returnables = [ - 'name', - 'id', - 'offerings', - 'total_offerings', - ] - - -class RegkeyPoolsOfferingParameters(BaseParameters): - api_map = { - 'regKey': 'registration_key', - 'licenseState': 'license_state', - 'status': 'state', - } - - returnables = [ - 'name', - 'dossier', - 'state', - - # license_state facts - 'licensed_date_time', - 'licensed_version', - 'evaluation_start_date_time', - 'evaluation_end_date_time', - 'license_end_date_time', - 'license_start_date_time', - 'registration_key', - ] - - @property - def registration_key(self): - try: - return self._values['license_state']['registrationKey'] - except KeyError: - return None - - @property - def license_start_date_time(self): - try: - return self._values['license_state']['licenseStartDateTime'] - except KeyError: - return None - - @property - def license_end_date_time(self): - try: - return self._values['license_state']['licenseEndDateTime'] - except KeyError: - return None - - @property - def evaluation_end_date_time(self): - try: - return self._values['license_state']['evaluationEndDateTime'] - except KeyError: - return None - - @property - def evaluation_start_date_time(self): - try: - return self._values['license_state']['evaluationStartDateTime'] - except KeyError: - return None - - @property - def licensed_version(self): - try: - return self._values['license_state']['licensedVersion'] - except KeyError: - return None - - @property - def licensed_date_time(self): - try: - return self._values['license_state']['licensedDateTime'] - except KeyError: - return None - - @property - def vendor(self): - try: - return self._values['license_state']['vendor'] - except KeyError: - return None - - -class RegkeyPoolsFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(RegkeyPoolsFactManager, self).__init__(**kwargs) - self.want = RegkeyPoolsParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(regkey_pools=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['name']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - params = RegkeyPoolsParameters(params=resource) - offerings = self.read_offerings_from_device(resource['id']) - params.update({'total_offerings': len(offerings)}) - for offering in offerings: - params2 = RegkeyPoolsOfferingParameters(params=offering) - params.update({'offerings': params2.to_return()}) - results.append(params) - return results - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/cm/device/licensing/pool/regkey/licenses".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - try: - return response['items'] - except KeyError: - return [] - - def read_offerings_from_device(self, license): - uri = "https://{0}:{1}/mgmt/cm/device/licensing/pool/regkey/licenses/{2}/offerings".format( - self.client.provider['server'], - self.client.provider['server_port'], - license, - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - try: - return response['items'] - except KeyError: - return [] - - -class SystemInfoParameters(BaseParameters): - api_map = { - 'isSystemSetup': 'is_system_setup', - 'isAdminPasswordChanged': 'is_admin_password_changed', - 'isRootPasswordChanged': 'is_root_password_changed' - } - - returnables = [ - 'base_mac_address', - 'chassis_serial', - 'hardware_information', - 'host_board_part_revision', - 'host_board_serial', - 'is_admin_password_changed', - 'is_root_password_changed', - 'is_system_setup', - 'marketing_name', - 'package_edition', - 'package_version', - 'platform', - 'product_build', - 'product_build_date', - 'product_built', - 'product_changelist', - 'product_code', - 'product_information', - 'product_jobid', - 'product_version', - 'switch_board_part_revision', - 'switch_board_serial', - 'time', - 'uptime', - ] - - @property - def is_admin_password_changed(self): - return flatten_boolean(self._values['is_admin_password_changed']) - - @property - def is_root_password_changed(self): - return flatten_boolean(self._values['is_root_password_changed']) - - @property - def is_system_setup(self): - if self._values['is_system_setup'] is None: - return 'no' - return flatten_boolean(self._values['is_system_setup']) - - @property - def chassis_serial(self): - if self._values['system-info'] is None: - return None - - # Yes, this is still called "bigip" even though this is querying the BIG-IQ - # product. This is likely due to BIG-IQ inheriting TMOS. - if 'bigipChassisSerialNum' not in self._values['system-info'][0]: - return None - return self._values['system-info'][0]['bigipChassisSerialNum'] - - @property - def switch_board_serial(self): - if self._values['system-info'] is None: - return None - if 'switchBoardSerialNum' not in self._values['system-info'][0]: - return None - if self._values['system-info'][0]['switchBoardSerialNum'].strip() == '': - return None - return self._values['system-info'][0]['switchBoardSerialNum'] - - @property - def switch_board_part_revision(self): - if self._values['system-info'] is None: - return None - if 'switchBoardPartRevNum' not in self._values['system-info'][0]: - return None - if self._values['system-info'][0]['switchBoardPartRevNum'].strip() == '': - return None - return self._values['system-info'][0]['switchBoardPartRevNum'] - - @property - def platform(self): - if self._values['system-info'] is None: - return None - return self._values['system-info'][0]['platform'] - - @property - def host_board_serial(self): - if self._values['system-info'] is None: - return None - if 'hostBoardSerialNum' not in self._values['system-info'][0]: - return None - if self._values['system-info'][0]['hostBoardSerialNum'].strip() == '': - return None - return self._values['system-info'][0]['hostBoardSerialNum'] - - @property - def host_board_part_revision(self): - if self._values['system-info'] is None: - return None - if 'hostBoardPartRevNum' not in self._values['system-info'][0]: - return None - if self._values['system-info'][0]['hostBoardPartRevNum'].strip() == '': - return None - return self._values['system-info'][0]['hostBoardPartRevNum'] - - @property - def package_edition(self): - return self._values['Edition'] - - @property - def package_version(self): - return 'Build {0} - {1}'.format(self._values['Build'], self._values['Date']) - - @property - def product_build(self): - return self._values['Build'] - - @property - def product_build_date(self): - return self._values['Date'] - - @property - def product_built(self): - if 'version_info' not in self._values: - return None - if 'Built' in self._values['version_info']: - return int(self._values['version_info']['Built']) - - @property - def product_changelist(self): - if 'version_info' not in self._values: - return None - if 'Changelist' in self._values['version_info']: - return int(self._values['version_info']['Changelist']) - - @property - def product_jobid(self): - if 'version_info' not in self._values: - return None - if 'JobID' in self._values['version_info']: - return int(self._values['version_info']['JobID']) - - @property - def product_code(self): - return self._values['Product'] - - @property - def product_version(self): - return self._values['Version'] - - @property - def hardware_information(self): - if self._values['hardware-version'] is None: - return None - self._transform_name_attribute(self._values['hardware-version']) - result = [v for k, v in iteritems(self._values['hardware-version'])] - return result - - def _transform_name_attribute(self, entry): - if isinstance(entry, dict): - for k, v in iteritems(entry): - if k == 'tmName': - entry['name'] = entry.pop('tmName') - self._transform_name_attribute(v) - elif isinstance(entry, list): - for k in entry: - if k == 'tmName': - entry['name'] = entry.pop('tmName') - self._transform_name_attribute(k) - else: - return - - @property - def time(self): - if self._values['fullDate'] is None: - return None - date = datetime.datetime.strptime(self._values['fullDate'], "%Y-%m-%dT%H:%M:%SZ") - result = dict( - day=date.day, - hour=date.hour, - minute=date.minute, - month=date.month, - second=date.second, - year=date.year - ) - return result - - @property - def marketing_name(self): - if self._values['platform'] is None: - return None - return self._values['platform'][0]['marketingName'] - - @property - def base_mac_address(self): - if self._values['platform'] is None: - return None - return self._values['platform'][0]['baseMac'] - - -class SystemInfoFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(SystemInfoFactManager, self).__init__(**kwargs) - self.want = SystemInfoParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(system_info=facts) - return result - - def _exec_module(self): - facts = self.read_facts() - results = facts.to_return() - return results - - def read_facts(self): - collection = self.read_collection_from_device() - params = SystemInfoParameters(params=collection) - return params - - def read_collection_from_device(self): - result = dict() - tmp = self.read_hardware_info_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_system_setup_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_clock_info_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_version_info_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_uptime_info_from_device() - if tmp: - result.update(tmp) - - tmp = self.read_version_file_info_from_device() - if tmp: - result.update(tmp) - - return result - - def read_system_setup_from_device(self): - uri = "https://{0}:{1}/mgmt/shared/system/setup".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - - return response - - def read_version_file_info_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/util/bash".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - args = dict( - command='run', - utilCmdArgs='-c "cat /VERSION"' - ) - resp = self.client.api.post(uri, json=args) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - try: - pattern = r'^(?P(Product|Build|Sequence|BaseBuild|Edition|Date|Built|Changelist|JobID))\:(?P.*)' - result = response['commandResult'].strip() - except KeyError: - return None - - if 'No such file or directory' in result: - return None - - lines = response['commandResult'].split("\n") - result = dict() - for line in lines: - if not line: - continue - matches = re.match(pattern, line) - if matches: - result[matches.group('key')] = matches.group('value').strip() - - if result: - return dict( - version_info=result - ) - - def read_uptime_info_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/util/bash".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - args = dict( - command='run', - utilCmdArgs='-c "cat /proc/uptime"' - ) - resp = self.client.api.post(uri, json=args) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - try: - parts = response['commandResult'].strip().split(' ') - return dict( - uptime=math.floor(float(parts[0])) - ) - except KeyError: - pass - - def read_hardware_info_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/sys/hardware".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - return result - - def read_clock_info_from_device(self): - """Parses clock info from the REST API - - The clock stat returned from the REST API (at the time of 13.1.0.7) - is similar to the following. - - { - "kind": "tm:sys:clock:clockstats", - "selfLink": "https://localhost/mgmt/tm/sys/clock?ver=13.1.0.4", - "entries": { - "https://localhost/mgmt/tm/sys/clock/0": { - "nestedStats": { - "entries": { - "fullDate": { - "description": "2018-06-05T13:38:33Z" - } - } - } - } - } - } - - Parsing this data using the ``parseStats`` method, yields a list of - the clock stats in a format resembling that below. - - [{'fullDate': '2018-06-05T13:41:05Z'}] - - Therefore, this method cherry-picks the first entry from this list - and returns it. There can be no other items in this list. - - Returns: - A dict mapping keys to the corresponding clock stats. For - example: - - {'fullDate': '2018-06-05T13:41:05Z'} - - There should never not be a clock stat, unless by chance it - is removed from the API in the future, or changed to a different - API endpoint. - - Raises: - F5ModuleError: A non-successful HTTP code was returned or a JSON - response was not found. - """ - uri = "https://{0}:{1}/mgmt/tm/sys/clock".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - if result is None: - return None - return result[0] - - def read_version_info_from_device(self): - """Parses version info from the REST API - - The version stat returned from the REST API (at the time of 13.1.0.7) - is similar to the following. - - { - "kind": "tm:sys:version:versionstats", - "selfLink": "https://localhost/mgmt/tm/sys/version?ver=13.1.0.4", - "entries": { - "https://localhost/mgmt/tm/sys/version/0": { - "nestedStats": { - "entries": { - "Build": { - "description": "0.0.6" - }, - "Date": { - "description": "Tue Mar 13 20:10:42 PDT 2018" - }, - "Edition": { - "description": "Point Release 4" - }, - "Product": { - "description": "BIG-IP" - }, - "Title": { - "description": "Main Package" - }, - "Version": { - "description": "13.1.0.4" - } - } - } - } - } - } - - Parsing this data using the ``parseStats`` method, yields a list of - the clock stats in a format resembling that below. - - [{'Build': '0.0.6', 'Date': 'Tue Mar 13 20:10:42 PDT 2018', - 'Edition': 'Point Release 4', 'Product': 'BIG-IP', 'Title': 'Main Package', - 'Version': '13.1.0.4'}] - - Therefore, this method cherry-picks the first entry from this list - and returns it. There can be no other items in this list. - - Returns: - A dict mapping keys to the corresponding clock stats. For - example: - - {'Build': '0.0.6', 'Date': 'Tue Mar 13 20:10:42 PDT 2018', - 'Edition': 'Point Release 4', 'Product': 'BIG-IP', 'Title': 'Main Package', - 'Version': '13.1.0.4'} - - There should never not be a version stat, unless by chance it - is removed from the API in the future, or changed to a different - API endpoint. - - Raises: - F5ModuleError: A non-successful HTTP code was returned or a JSON - response was not found. - """ - uri = "https://{0}:{1}/mgmt/tm/sys/version".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - if result is None: - return None - return result[0] - - -class VlansParameters(BaseParameters): - api_map = { - 'autoLasthop': 'auto_lasthop', - 'cmpHash': 'cmp_hash_algorithm', - 'failsafeAction': 'failsafe_action', - 'failsafe': 'failsafe_enabled', - 'failsafeTimeout': 'failsafe_timeout', - 'ifIndex': 'if_index', - 'learning': 'learning_mode', - 'interfacesReference': 'interfaces', - 'sourceChecking': 'source_check_enabled', - 'fullPath': 'full_path' - } - - returnables = [ - 'full_path', - 'name', - 'auto_lasthop', - 'cmp_hash_algorithm', - 'description', - 'failsafe_action', - 'failsafe_enabled', - 'failsafe_timeout', - 'if_index', - 'learning_mode', - 'interfaces', - 'mtu', - 'sflow_poll_interval', - 'sflow_poll_interval_global', - 'sflow_sampling_rate', - 'sflow_sampling_rate_global', - 'source_check_enabled', - 'true_mac_address', - 'tag', - ] - - @property - def interfaces(self): - if self._values['interfaces'] is None: - return None - if 'items' not in self._values['interfaces']: - return None - result = [] - for item in self._values['interfaces']['items']: - tmp = dict( - name=item['name'], - full_path=item['fullPath'] - ) - if 'tagged' in item: - tmp['tagged'] = 'yes' - else: - tmp['tagged'] = 'no' - result.append(tmp) - return result - - @property - def sflow_poll_interval(self): - return int(self._values['sflow']['pollInterval']) - - @property - def sflow_poll_interval_global(self): - return flatten_boolean(self._values['sflow']['pollIntervalGlobal']) - - @property - def sflow_sampling_rate(self): - return int(self._values['sflow']['samplingRate']) - - @property - def sflow_sampling_rate_global(self): - return flatten_boolean(self._values['sflow']['samplingRateGlobal']) - - @property - def source_check_state(self): - return flatten_boolean(self._values['source_check_state']) - - @property - def true_mac_address(self): - if self._values['stats']['macTrue'] in [None, 'none']: - return None - return self._values['stats']['macTrue'] - - @property - def tag(self): - return self._values['stats']['id'] - - @property - def failsafe_enabled(self): - return flatten_boolean(self._values['failsafe_enabled']) - - -class VlansFactManager(BaseManager): - def __init__(self, *args, **kwargs): - self.client = kwargs.get('client', None) - self.module = kwargs.get('module', None) - super(VlansFactManager, self).__init__(**kwargs) - self.want = VlansParameters(params=self.module.params) - - def exec_module(self): - facts = self._exec_module() - result = dict(vlans=facts) - return result - - def _exec_module(self): - results = [] - facts = self.read_facts() - for item in facts: - attrs = item.to_return() - results.append(attrs) - results = sorted(results, key=lambda k: k['full_path']) - return results - - def read_facts(self): - results = [] - collection = self.read_collection_from_device() - for resource in collection: - resource.update(self.read_stats(resource['fullPath'])) - params = VlansParameters(params=resource) - results.append(params) - return results - - def read_stats(self, resource): - uri = "https://{0}:{1}/mgmt/tm/net/vlan/{2}/stats".format( - self.client.provider['server'], - self.client.provider['server_port'], - transform_name(name=resource) - - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - result = parseStats(response) - return result - - def read_collection_from_device(self): - uri = "https://{0}:{1}/mgmt/tm/net/vlan/?expandSubcollections=true".format( - self.client.provider['server'], - self.client.provider['server_port'], - ) - resp = self.client.api.get(uri) - try: - response = resp.json() - except ValueError as ex: - raise F5ModuleError(str(ex)) - - if 'code' in response and response['code'] == 400: - if 'message' in response: - raise F5ModuleError(response['message']) - else: - raise F5ModuleError(resp.content) - if 'items' not in response: - return [] - result = response['items'] - return result - - -class ModuleManager(object): - def __init__(self, *args, **kwargs): - self.module = kwargs.get('module', None) - self.client = kwargs.get('client', None) - self.kwargs = kwargs - self.want = Parameters(params=self.module.params) - self.managers = { - 'applications': dict( - manager=ApplicationsFactManager, - client=F5RestClient, - ), - 'managed-devices': dict( - manager=ManagedDevicesFactManager, - client=F5RestClient, - ), - 'purchased-pool-licenses': dict( - manager=PurchasedPoolLicensesFactManager, - client=F5RestClient, - ), - 'regkey-pools': dict( - manager=RegkeyPoolsFactManager, - client=F5RestClient, - ), - 'system-info': dict( - manager=SystemInfoFactManager, - client=F5RestClient, - ), - 'vlans': dict( - manager=VlansFactManager, - client=F5RestClient, - ), - } - - def exec_module(self): - self.handle_all_keyword() - res = self.check_valid_gather_subset(self.want.gather_subset) - if res: - invalid = ','.join(res) - raise F5ModuleError( - "The specified 'gather_subset' options are invalid: {0}".format(invalid) - ) - result = self.filter_excluded_facts() - - managers = [] - for name in result: - manager = self.get_manager(name) - if manager: - managers.append(manager) - - if not managers: - result = dict( - changed=False - ) - return result - - result = self.execute_managers(managers) - if result: - result['changed'] = True - else: - result['changed'] = False - return result - - def filter_excluded_facts(self): - # Remove the excluded entries from the list of possible facts - exclude = [x[1:] for x in self.want.gather_subset if x[0] == '!'] - include = [x for x in self.want.gather_subset if x[0] != '!'] - result = [x for x in include if x not in exclude] - return result - - def handle_all_keyword(self): - if 'all' not in self.want.gather_subset: - return - managers = list(self.managers.keys()) + self.want.gather_subset - managers.remove('all') - self.want.update({'gather_subset': managers}) - - def check_valid_gather_subset(self, includes): - """Check that the specified subset is valid - - The ``gather_subset`` parameter is specified as a "raw" field which means that - any Python type could technically be provided - - :param includes: - :return: - """ - keys = self.managers.keys() - result = [] - for x in includes: - if x not in keys: - if x[0] == '!': - if x[1:] not in keys: - result.append(x) - else: - result.append(x) - return result - - def execute_managers(self, managers): - results = dict() - for manager in managers: - result = manager.exec_module() - results.update(result) - return results - - def get_manager(self, which): - result = {} - info = self.managers.get(which, None) - if not info: - return result - kwargs = dict() - kwargs.update(self.kwargs) - - manager = info.get('manager', None) - client = info.get('client', None) - kwargs['client'] = client(**self.module.params) - result = manager(**kwargs) - return result - - -class ArgumentSpec(object): - def __init__(self): - self.supports_check_mode = False - argument_spec = dict( - gather_subset=dict( - type='list', - required=True, - choices=[ - # Meta choices - 'all', - - # Non-meta choices - 'applications', - 'managed-devices', - 'purchased-pool-licenses', - 'regkey-pools', - 'system-info', - 'vlans', - - # Negations of meta choices - '!all', - - # Negations of non-meta-choices - '!applications', - '!managed-devices', - '!purchased-pool-licenses', - '!regkey-pools', - '!system-info', - '!vlans', - ] - ), - ) - self.argument_spec = {} - self.argument_spec.update(f5_argument_spec) - self.argument_spec.update(argument_spec) - - -def main(): - spec = ArgumentSpec() - - module = AnsibleModule( - argument_spec=spec.argument_spec, - supports_check_mode=spec.supports_check_mode - ) - if module._name == 'bigiq_device_facts': - module.deprecate("The 'bigiq_device_facts' module has been renamed to 'bigiq_device_info'", version='2.13') - - try: - mm = ModuleManager(module=module) - results = mm.exec_module() - module.exit_json(**results) - except F5ModuleError as ex: - module.fail_json(msg=str(ex)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/fortianalyzer/faz_device.py b/plugins/modules/network/fortianalyzer/faz_device.py deleted file mode 100644 index 08a3615751..0000000000 --- a/plugins/modules/network/fortianalyzer/faz_device.py +++ /dev/null @@ -1,438 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: faz_device -author: Luke Weighall (@lweighall) -short_description: Add or remove device -description: - - Add or remove a device or list of devices to FortiAnalyzer Device Manager. ADOM Capable. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: true - default: root - type: str - - mode: - description: - - Add or delete devices. Or promote unregistered devices that are in the FortiAnalyzer "waiting pool" - required: false - default: add - choices: ["add", "delete", "promote"] - type: str - - device_username: - description: - - The username of the device being added to FortiAnalyzer. - required: false - type: str - - device_password: - description: - - The password of the device being added to FortiAnalyzer. - required: false - type: str - - device_ip: - description: - - The IP of the device being added to FortiAnalyzer. - required: false - type: str - - device_unique_name: - description: - - The desired "friendly" name of the device being added to FortiAnalyzer. - required: false - type: str - - device_serial: - description: - - The serial number of the device being added to FortiAnalyzer. - required: false - type: str - - os_type: - description: - - The os type of the device being added (default 0). - required: true - choices: ["unknown", "fos", "fsw", "foc", "fml", "faz", "fwb", "fch", "fct", "log", "fmg", "fsa", "fdd", "fac"] - type: str - - mgmt_mode: - description: - - Management Mode of the device you are adding. - choices: ["unreg", "fmg", "faz", "fmgfaz"] - required: true - type: str - - os_minor_vers: - description: - - Minor OS rev of the device. - required: true - type: str - - os_ver: - description: - - Major OS rev of the device - required: true - choices: ["unknown", "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0"] - type: str - - platform_str: - description: - - Required for determine the platform for VM platforms. ie FortiGate-VM64 - required: false - type: str - - faz_quota: - description: - - Specifies the quota for the device in FAZ - required: False - type: str -''' - -EXAMPLES = ''' -- name: DISCOVER AND ADD DEVICE A PHYSICAL FORTIGATE - faz_device: - adom: "root" - device_username: "admin" - device_password: "admin" - device_ip: "10.10.24.201" - device_unique_name: "FGT1" - device_serial: "FGVM000000117994" - state: "present" - mgmt_mode: "faz" - os_type: "fos" - os_ver: "5.0" - minor_rev: 6 - - -- name: DISCOVER AND ADD DEVICE A VIRTUAL FORTIGATE - faz_device: - adom: "root" - device_username: "admin" - device_password: "admin" - device_ip: "10.10.24.202" - device_unique_name: "FGT2" - mgmt_mode: "faz" - os_type: "fos" - os_ver: "5.0" - minor_rev: 6 - state: "present" - platform_str: "FortiGate-VM64" - -- name: DELETE DEVICE FGT01 - faz_device: - adom: "root" - device_unique_name: "ansible-fgt01" - mode: "delete" - -- name: DELETE DEVICE FGT02 - faz_device: - adom: "root" - device_unique_name: "ansible-fgt02" - mode: "delete" - -- name: PROMOTE FGT01 IN FAZ BY IP - faz_device: - adom: "root" - device_password: "fortinet" - device_ip: "10.7.220.151" - device_username: "ansible" - mgmt_mode: "faz" - mode: "promote" - - -- name: PROMOTE FGT02 IN FAZ - faz_device: - adom: "root" - device_password: "fortinet" - device_unique_name: "ansible-fgt02" - device_username: "ansible" - mgmt_mode: "faz" - mode: "promote" - -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.fortianalyzer import FortiAnalyzerHandler -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZBaseException -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZCommon -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAZMethods -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import DEFAULT_RESULT_OBJ -from ansible_collections.community.general.plugins.module_utils.network.fortianalyzer.common import FAIL_SOCKET_MSG - - -def faz_add_device(faz, paramgram): - """ - This method is used to add devices to the faz or delete them - """ - - datagram = { - "adom": paramgram["adom"], - "device": {"adm_usr": paramgram["device_username"], "adm_pass": paramgram["device_password"], - "ip": paramgram["ip"], "name": paramgram["device_unique_name"], - "mgmt_mode": paramgram["mgmt_mode"], "os_type": paramgram["os_type"], - "mr": paramgram["os_minor_vers"]} - } - - if paramgram["platform_str"] is not None: - datagram["device"]["platform_str"] = paramgram["platform_str"] - - if paramgram["sn"] is not None: - datagram["device"]["sn"] = paramgram["sn"] - - if paramgram["device_action"] is not None: - datagram["device"]["device_action"] = paramgram["device_action"] - - if paramgram["faz.quota"] is not None: - datagram["device"]["faz.quota"] = paramgram["faz.quota"] - - url = '/dvm/cmd/add/device/' - response = faz.process_request(url, datagram, FAZMethods.EXEC) - return response - - -def faz_delete_device(faz, paramgram): - """ - This method deletes a device from the FAZ - """ - datagram = { - "adom": paramgram["adom"], - "device": paramgram["device_unique_name"], - } - - url = '/dvm/cmd/del/device/' - response = faz.process_request(url, datagram, FAZMethods.EXEC) - return response - - -def faz_get_unknown_devices(faz): - """ - This method gets devices with an unknown management type field - """ - - faz_filter = ["mgmt_mode", "==", "0"] - - datagram = { - "filter": faz_filter - } - - url = "/dvmdb/device" - response = faz.process_request(url, datagram, FAZMethods.GET) - - return response - - -def faz_approve_unregistered_device_by_ip(faz, paramgram): - """ - This method approves unregistered devices by ip. - """ - # TRY TO FIND DETAILS ON THIS UNREGISTERED DEVICE - unknown_devices = faz_get_unknown_devices(faz) - target_device = None - if unknown_devices[0] == 0: - for device in unknown_devices[1]: - if device["ip"] == paramgram["ip"]: - target_device = device - else: - return "No devices are waiting to be registered!" - - # now that we have the target device details...fill out the datagram and make the call to promote it - if target_device is not None: - target_device_paramgram = { - "adom": paramgram["adom"], - "ip": target_device["ip"], - "device_username": paramgram["device_username"], - "device_password": paramgram["device_password"], - "device_unique_name": paramgram["device_unique_name"], - "sn": target_device["sn"], - "os_type": target_device["os_type"], - "mgmt_mode": paramgram["mgmt_mode"], - "os_minor_vers": target_device["mr"], - "os_ver": target_device["os_ver"], - "platform_str": target_device["platform_str"], - "faz.quota": target_device["faz.quota"], - "device_action": paramgram["device_action"] - } - - add_device = faz_add_device(faz, target_device_paramgram) - return add_device - - return str("Couldn't find the desired device with ip: " + str(paramgram["device_ip"])) - - -def faz_approve_unregistered_device_by_name(faz, paramgram): - # TRY TO FIND DETAILS ON THIS UNREGISTERED DEVICE - unknown_devices = faz_get_unknown_devices(faz) - target_device = None - if unknown_devices[0] == 0: - for device in unknown_devices[1]: - if device["name"] == paramgram["device_unique_name"]: - target_device = device - else: - return "No devices are waiting to be registered!" - - # now that we have the target device details...fill out the datagram and make the call to promote it - if target_device is not None: - target_device_paramgram = { - "adom": paramgram["adom"], - "ip": target_device["ip"], - "device_username": paramgram["device_username"], - "device_password": paramgram["device_password"], - "device_unique_name": paramgram["device_unique_name"], - "sn": target_device["sn"], - "os_type": target_device["os_type"], - "mgmt_mode": paramgram["mgmt_mode"], - "os_minor_vers": target_device["mr"], - "os_ver": target_device["os_ver"], - "platform_str": target_device["platform_str"], - "faz.quota": target_device["faz.quota"], - "device_action": paramgram["device_action"] - } - - add_device = faz_add_device(faz, target_device_paramgram) - return add_device - - return str("Couldn't find the desired device with name: " + str(paramgram["device_unique_name"])) - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - mode=dict(choices=["add", "delete", "promote"], type="str", default="add"), - - device_ip=dict(required=False, type="str"), - device_username=dict(required=False, type="str"), - device_password=dict(required=False, type="str", no_log=True), - device_unique_name=dict(required=False, type="str"), - device_serial=dict(required=False, type="str"), - - os_type=dict(required=False, type="str", choices=["unknown", "fos", "fsw", "foc", "fml", - "faz", "fwb", "fch", "fct", "log", "fmg", - "fsa", "fdd", "fac"]), - mgmt_mode=dict(required=False, type="str", choices=["unreg", "fmg", "faz", "fmgfaz"]), - os_minor_vers=dict(required=False, type="str"), - os_ver=dict(required=False, type="str", choices=["unknown", "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0"]), - platform_str=dict(required=False, type="str"), - faz_quota=dict(required=False, type="str") - ) - - required_if = [ - ['mode', 'delete', ['device_unique_name']], - ['mode', 'add', ['device_serial', 'device_username', - 'device_password', 'device_unique_name', 'device_ip', 'mgmt_mode', 'platform_str']] - - ] - - module = AnsibleModule(argument_spec, supports_check_mode=True, required_if=required_if, ) - - # START SESSION LOGIC - paramgram = { - "adom": module.params["adom"], - "mode": module.params["mode"], - "ip": module.params["device_ip"], - "device_username": module.params["device_username"], - "device_password": module.params["device_password"], - "device_unique_name": module.params["device_unique_name"], - "sn": module.params["device_serial"], - "os_type": module.params["os_type"], - "mgmt_mode": module.params["mgmt_mode"], - "os_minor_vers": module.params["os_minor_vers"], - "os_ver": module.params["os_ver"], - "platform_str": module.params["platform_str"], - "faz.quota": module.params["faz_quota"], - "device_action": None - } - # INSERT THE PARAMGRAM INTO THE MODULE SO WHEN WE PASS IT TO MOD_UTILS.FortiManagerHandler IT HAS THAT INFO - - if paramgram["mode"] == "add": - paramgram["device_action"] = "add_model" - elif paramgram["mode"] == "promote": - paramgram["device_action"] = "promote_unreg" - module.paramgram = paramgram - - # TRY TO INIT THE CONNECTION SOCKET PATH AND FortiManagerHandler OBJECT AND TOOLS - faz = None - if module._socket_path: - connection = Connection(module._socket_path) - faz = FortiAnalyzerHandler(connection, module) - faz.tools = FAZCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - # BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION - results = DEFAULT_RESULT_OBJ - - try: - if paramgram["mode"] == "add": - results = faz_add_device(faz, paramgram) - except BaseException as err: - raise FAZBaseException(msg="An error occurred trying to add the device. Error: " + str(err)) - - try: - if paramgram["mode"] == "promote": - if paramgram["ip"] is not None: - results = faz_approve_unregistered_device_by_ip(faz, paramgram) - elif paramgram["device_unique_name"] is not None: - results = faz_approve_unregistered_device_by_name(faz, paramgram) - except BaseException as err: - raise FAZBaseException(msg="An error occurred trying to promote the device. Error: " + str(err)) - - try: - if paramgram["mode"] == "delete": - results = faz_delete_device(faz, paramgram) - except BaseException as err: - raise FAZBaseException(msg="An error occurred trying to delete the device. Error: " + str(err)) - - # PROCESS RESULTS - try: - faz.govern_response(module=module, results=results, - ansible_facts=faz.construct_ansible_facts(results, module.params, paramgram)) - except BaseException as err: - raise FAZBaseException(msg="An error occurred with govern_response(). Error: " + str(err)) - - # This should only be hit if faz.govern_response is missed or failed somehow. In fact. It should never be hit. - # But it's here JIC. - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_device.py b/plugins/modules/network/fortimanager/fmgr_device.py deleted file mode 100644 index c5262d01c5..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_device.py +++ /dev/null @@ -1,302 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_device -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Add or remove device from FortiManager. -description: - - Add or remove a device or list of devices from FortiManager Device Manager using JSON RPC API. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: true - default: root - - mode: - description: - - The desired mode of the specified object. - required: false - default: add - choices: ["add", "delete"] - - blind_add: - description: - - When adding a device, module will check if it exists, and skip if it does. - - If enabled, this option will stop the module from checking if it already exists, and blindly add the device. - required: false - default: "disable" - choices: ["enable", "disable"] - - device_username: - description: - - The username of the device being added to FortiManager. - required: false - - device_password: - description: - - The password of the device being added to FortiManager. - required: false - - device_ip: - description: - - The IP of the device being added to FortiManager. Supports both IPv4 and IPv6. - required: false - - device_unique_name: - description: - - The desired "friendly" name of the device being added to FortiManager. - required: false - - device_serial: - description: - - The serial number of the device being added to FortiManager. - required: false -''' - -EXAMPLES = ''' -- name: DISCOVER AND ADD DEVICE FGT1 - fmgr_device: - adom: "root" - device_username: "admin" - device_password: "admin" - device_ip: "10.10.24.201" - device_unique_name: "FGT1" - device_serial: "FGVM000000117994" - mode: "add" - blind_add: "enable" - -- name: DISCOVER AND ADD DEVICE FGT2 - fmgr_device: - adom: "root" - device_username: "admin" - device_password: "admin" - device_ip: "10.10.24.202" - device_unique_name: "FGT2" - device_serial: "FGVM000000117992" - mode: "delete" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG - - -def discover_device(fmgr, paramgram): - """ - This method is used to discover devices before adding them to FMGR - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - - datagram = { - "odd_request_form": "True", - "device": {"adm_usr": paramgram["device_username"], - "adm_pass": paramgram["device_password"], - "ip": paramgram["device_ip"]} - } - - url = '/dvm/cmd/discover/device/' - - response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) - return response - - -def add_device(fmgr, paramgram): - """ - This method is used to add devices to the FMGR - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - - datagram = { - "adom": paramgram["adom"], - "flags": ["create_task", "nonblocking"], - "odd_request_form": "True", - "device": {"adm_usr": paramgram["device_username"], "adm_pass": paramgram["device_password"], - "ip": paramgram["device_ip"], "name": paramgram["device_unique_name"], - "sn": paramgram["device_serial"], "mgmt_mode": "fmgfaz", "flags": 24} - } - - url = '/dvm/cmd/add/device/' - response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) - return response - - -def delete_device(fmgr, paramgram): - """ - This method deletes a device from the FMGR - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - datagram = { - "adom": paramgram["adom"], - "flags": ["create_task", "nonblocking"], - "device": paramgram["device_unique_name"], - } - - url = '/dvm/cmd/del/device/' - response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) - return response - - -def get_device(fmgr, paramgram): - """ - This method attempts to find the firewall on FortiManager to see if it already exists. - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - datagram = { - "adom": paramgram["adom"], - "filter": ["name", "==", paramgram["device_unique_name"]], - } - - url = '/dvmdb/adom/{adom}/device/{name}'.format(adom=paramgram["adom"], - name=paramgram["device_unique_name"]) - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - return response - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - mode=dict(choices=["add", "delete"], type="str", default="add"), - blind_add=dict(choices=["enable", "disable"], type="str", default="disable"), - device_ip=dict(required=False, type="str"), - device_username=dict(required=False, type="str"), - device_password=dict(required=False, type="str", no_log=True), - device_unique_name=dict(required=True, type="str"), - device_serial=dict(required=False, type="str") - ) - - # BUILD MODULE OBJECT SO WE CAN BUILD THE PARAMGRAM - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - - # BUILD THE PARAMGRAM - paramgram = { - "device_ip": module.params["device_ip"], - "device_username": module.params["device_username"], - "device_password": module.params["device_password"], - "device_unique_name": module.params["device_unique_name"], - "device_serial": module.params["device_serial"], - "adom": module.params["adom"], - "mode": module.params["mode"] - } - - # INSERT THE PARAMGRAM INTO THE MODULE SO WHEN WE PASS IT TO MOD_UTILS.FortiManagerHandler IT HAS THAT INFO - module.paramgram = paramgram - - # TRY TO INIT THE CONNECTION SOCKET PATH AND FortiManagerHandler OBJECT AND TOOLS - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - # BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION - results = DEFAULT_RESULT_OBJ - try: - if paramgram["mode"] == "add": - # CHECK IF DEVICE EXISTS - if module.params["blind_add"] == "disable": - exists_results = get_device(fmgr, paramgram) - fmgr.govern_response(module=module, results=exists_results, good_codes=(0, -3), changed=False, - ansible_facts=fmgr.construct_ansible_facts(exists_results, - module.params, paramgram)) - - discover_results = discover_device(fmgr, paramgram) - fmgr.govern_response(module=module, results=discover_results, stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(discover_results, - module.params, paramgram)) - - if discover_results[0] == 0: - results = add_device(fmgr, paramgram) - fmgr.govern_response(module=module, results=discover_results, stop_on_success=True, - changed_if_success=True, - ansible_facts=fmgr.construct_ansible_facts(discover_results, - module.params, paramgram)) - - if paramgram["mode"] == "delete": - results = delete_device(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_device_config.py b/plugins/modules/network/fortimanager/fmgr_device_config.py deleted file mode 100644 index 150fc85a80..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_device_config.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_device_config -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Edit device configurations -description: - - Edit device configurations from FortiManager Device Manager using JSON RPC API. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - device_unique_name: - description: - - The unique device's name that you are editing. A.K.A. Friendly name of the device in FortiManager. - required: True - - device_hostname: - description: - - The device's new hostname. - required: false - - install_config: - description: - - Tells FMGR to attempt to install the config after making it. - required: false - default: disable - - interface: - description: - - The interface/port number you are editing. - required: false - - interface_ip: - description: - - The IP and subnet of the interface/port you are editing. - required: false - - interface_allow_access: - description: - - Specify what protocols are allowed on the interface, comma-separated list (see examples). - required: false -''' - -EXAMPLES = ''' -- name: CHANGE HOSTNAME - fmgr_device_config: - device_hostname: "ChangedbyAnsible" - device_unique_name: "FGT1" - -- name: EDIT INTERFACE INFORMATION - fmgr_device_config: - adom: "root" - device_unique_name: "FGT2" - interface: "port3" - interface_ip: "10.1.1.1/24" - interface_allow_access: "ping, telnet, https" - -- name: INSTALL CONFIG - fmgr_device_config: - adom: "root" - device_unique_name: "FGT1" - install_config: "enable" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods - - -def update_device_hostname(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - datagram = { - "hostname": paramgram["device_hostname"] - } - - url = "pm/config/device/{device_name}/global/system/global".format(device_name=paramgram["device_unique_name"]) - response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE) - return response - - -def update_device_interface(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - access_list = list() - allow_access_list = paramgram["interface_allow_access"].replace(' ', '') - access_list = allow_access_list.split(',') - - datagram = { - "allowaccess": access_list, - "ip": paramgram["interface_ip"] - } - - url = "/pm/config/device/{device_name}/global/system/interface" \ - "/{interface}".format(device_name=paramgram["device_unique_name"], interface=paramgram["interface"]) - response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE) - return response - - -def exec_config(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - datagram = { - "scope": { - "name": paramgram["device_unique_name"] - }, - "adom": paramgram["adom"], - "flags": "none" - } - - url = "/securityconsole/install/device" - response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) - return response - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - device_unique_name=dict(required=True, type="str"), - device_hostname=dict(required=False, type="str"), - interface=dict(required=False, type="str"), - interface_ip=dict(required=False, type="str"), - interface_allow_access=dict(required=False, type="str"), - install_config=dict(required=False, type="str", default="disable"), - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - paramgram = { - "device_unique_name": module.params["device_unique_name"], - "device_hostname": module.params["device_hostname"], - "interface": module.params["interface"], - "interface_ip": module.params["interface_ip"], - "interface_allow_access": module.params["interface_allow_access"], - "install_config": module.params["install_config"], - "adom": module.params["adom"] - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - # BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION - results = DEFAULT_RESULT_OBJ - try: - if paramgram["device_hostname"] is not None: - results = update_device_hostname(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - if paramgram["interface_ip"] is not None or paramgram["interface_allow_access"] is not None: - results = update_device_interface(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - if paramgram["install_config"] == "enable": - results = exec_config(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_device_group.py b/plugins/modules/network/fortimanager/fmgr_device_group.py deleted file mode 100644 index 9112f5ff3f..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_device_group.py +++ /dev/null @@ -1,329 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_device_group -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Alter FortiManager device groups. -description: - - Add or edit device groups and assign devices to device groups FortiManager Device Manager using JSON RPC API. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - vdom: - description: - - The VDOM of the Fortigate you want to add, must match the device in FMGR. Usually root. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - grp_name: - description: - - The name of the device group. - required: false - - grp_desc: - description: - - The description of the device group. - required: false - - grp_members: - description: - - A comma separated list of device names or device groups to be added as members to the device group. - - If Group Members are defined, and mode="delete", only group members will be removed. - - If you want to delete a group itself, you must omit this parameter from the task in playbook. - required: false - -''' - - -EXAMPLES = ''' -- name: CREATE DEVICE GROUP - fmgr_device_group: - grp_name: "TestGroup" - grp_desc: "CreatedbyAnsible" - adom: "ansible" - mode: "add" - -- name: CREATE DEVICE GROUP 2 - fmgr_device_group: - grp_name: "AnsibleGroup" - grp_desc: "CreatedbyAnsible" - adom: "ansible" - mode: "add" - -- name: ADD DEVICES TO DEVICE GROUP - fmgr_device_group: - mode: "add" - grp_name: "TestGroup" - grp_members: "FGT1,FGT2" - adom: "ansible" - vdom: "root" - -- name: REMOVE DEVICES TO DEVICE GROUP - fmgr_device_group: - mode: "delete" - grp_name: "TestGroup" - grp_members: "FGT1,FGT2" - adom: "ansible" - -- name: DELETE DEVICE GROUP - fmgr_device_group: - grp_name: "AnsibleGroup" - grp_desc: "CreatedbyAnsible" - mode: "delete" - adom: "ansible" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG - - -def get_groups(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - datagram = { - "method": "get" - } - - url = '/dvmdb/adom/{adom}/group'.format(adom=paramgram["adom"]) - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - return response - - -def add_device_group(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - mode = paramgram["mode"] - - datagram = { - "name": paramgram["grp_name"], - "desc": paramgram["grp_desc"], - "os_type": "fos" - } - - url = '/dvmdb/adom/{adom}/group'.format(adom=paramgram["adom"]) - - # IF MODE = SET -- USE THE 'SET' API CALL MODE - if mode == "set": - response = fmgr.process_request(url, datagram, FMGRMethods.SET) - # IF MODE = UPDATE -- USER THE 'UPDATE' API CALL MODE - elif mode == "update": - response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE) - # IF MODE = ADD -- USE THE 'ADD' API CALL MODE - elif mode == "add": - response = fmgr.process_request(url, datagram, FMGRMethods.ADD) - - return response - - -def delete_device_group(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - - datagram = { - "adom": paramgram["adom"], - "name": paramgram["grp_name"] - } - - url = '/dvmdb/adom/{adom}/group/{grp_name}'.format(adom=paramgram["adom"], grp_name=paramgram["grp_name"]) - response = fmgr.process_request(url, datagram, FMGRMethods.DELETE) - return response - - -def add_group_member(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - device_member_list = paramgram["grp_members"].replace(' ', '') - device_member_list = device_member_list.split(',') - - for dev_name in device_member_list: - datagram = {'name': dev_name, 'vdom': paramgram["vdom"]} - - url = '/dvmdb/adom/{adom}/group/{grp_name}/object member'.format(adom=paramgram["adom"], - grp_name=paramgram["grp_name"]) - response = fmgr.process_request(url, datagram, FMGRMethods.ADD) - - return response - - -def delete_group_member(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - device_member_list = paramgram["grp_members"].replace(' ', '') - device_member_list = device_member_list.split(',') - - for dev_name in device_member_list: - datagram = {'name': dev_name, 'vdom': paramgram["vdom"]} - - url = '/dvmdb/adom/{adom}/group/{grp_name}/object member'.format(adom=paramgram["adom"], - grp_name=paramgram["grp_name"]) - response = fmgr.process_request(url, datagram, FMGRMethods.DELETE) - - return response - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - vdom=dict(required=False, type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - grp_desc=dict(required=False, type="str"), - grp_name=dict(required=True, type="str"), - grp_members=dict(required=False, type="str"), - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - paramgram = { - "mode": module.params["mode"], - "grp_name": module.params["grp_name"], - "grp_desc": module.params["grp_desc"], - "grp_members": module.params["grp_members"], - "adom": module.params["adom"], - "vdom": module.params["vdom"] - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - # BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION - results = DEFAULT_RESULT_OBJ - try: - # PROCESS THE GROUP ADDS FIRST - if paramgram["grp_name"] is not None and paramgram["mode"] in ["add", "set", "update"]: - # add device group - results = add_device_group(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - # PROCESS THE GROUP MEMBER ADDS - if paramgram["grp_members"] is not None and paramgram["mode"] in ["add", "set", "update"]: - # assign devices to device group - results = add_group_member(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - # PROCESS THE GROUP MEMBER DELETES - if paramgram["grp_members"] is not None and paramgram["mode"] == "delete": - # remove devices grom a group - results = delete_group_member(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - # PROCESS THE GROUP DELETES, ONLY IF GRP_MEMBERS IS NOT NULL TOO - if paramgram["grp_name"] is not None and paramgram["mode"] == "delete" and paramgram["grp_members"] is None: - # delete device group - results = delete_device_group(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_device_provision_template.py b/plugins/modules/network/fortimanager/fmgr_device_provision_template.py deleted file mode 100644 index 5c3da9487c..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_device_provision_template.py +++ /dev/null @@ -1,1552 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_device_provision_template -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manages Device Provisioning Templates in FortiManager. -description: - - Allows the editing and assignment of device provisioning templates in FortiManager. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: true - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values. - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - device_unique_name: - description: - - The unique device's name that you are editing. - required: True - - provisioning_template: - description: - - The provisioning template you want to apply (default = default). - required: True - - provision_targets: - description: - - The friendly names of devices in FortiManager to assign the provisioning template to. CSV separated list. - required: True - - snmp_status: - description: - - Enables or disables SNMP globally. - required: False - choices: ["enable", "disable"] - - snmp_v2c_query_port: - description: - - Sets the snmp v2c community query port. - required: False - - snmp_v2c_trap_port: - description: - - Sets the snmp v2c community trap port. - required: False - - snmp_v2c_status: - description: - - Enables or disables the v2c community specified. - required: False - choices: ["enable", "disable"] - - snmp_v2c_trap_status: - description: - - Enables or disables the v2c community specified for traps. - required: False - choices: ["enable", "disable"] - - snmp_v2c_query_status: - description: - - Enables or disables the v2c community specified for queries. - required: False - choices: ["enable", "disable"] - - snmp_v2c_name: - description: - - Specifies the v2c community name. - required: False - - snmp_v2c_id: - description: - - Primary key for the snmp community. this must be unique! - required: False - - snmp_v2c_trap_src_ipv4: - description: - - Source ip the traps should come from IPv4. - required: False - - snmp_v2c_trap_hosts_ipv4: - description: > - - IPv4 addresses of the hosts that should get SNMP v2c traps, comma separated, must include mask - ("10.7.220.59 255.255.255.255, 10.7.220.60 255.255.255.255"). - required: False - - snmp_v2c_query_hosts_ipv4: - description: > - - IPv4 addresses or subnets that are allowed to query SNMP v2c, comma separated - ("10.7.220.59 255.255.255.0, 10.7.220.0 255.255.255.0"). - required: False - - snmpv3_auth_proto: - description: - - SNMPv3 auth protocol. - required: False - choices: ["md5", "sha"] - - snmpv3_auth_pwd: - description: - - SNMPv3 auth pwd __ currently not encrypted! ensure this file is locked down permissions wise! - required: False - - snmpv3_name: - description: - - SNMPv3 user name. - required: False - - snmpv3_notify_hosts: - description: - - List of ipv4 hosts to send snmpv3 traps to. Comma separated IPv4 list. - required: False - - snmpv3_priv_proto: - description: - - SNMPv3 priv protocol. - required: False - choices: ["aes", "des", "aes256", "aes256cisco"] - - snmpv3_priv_pwd: - description: - - SNMPv3 priv pwd currently not encrypted! ensure this file is locked down permissions wise! - required: False - - snmpv3_queries: - description: - - Allow snmpv3_queries. - required: False - choices: ["enable", "disable"] - - snmpv3_query_port: - description: - - SNMPv3 query port. - required: False - - snmpv3_security_level: - description: - - SNMPv3 security level. - required: False - choices: ["no-auth-no-priv", "auth-no-priv", "auth-priv"] - - snmpv3_source_ip: - description: - - SNMPv3 source ipv4 address for traps. - required: False - - snmpv3_status: - description: - - SNMPv3 user is enabled or disabled. - required: False - choices: ["enable", "disable"] - - snmpv3_trap_rport: - description: - - SNMPv3 trap remote port. - required: False - - snmpv3_trap_status: - description: - - SNMPv3 traps is enabled or disabled. - required: False - choices: ["enable", "disable"] - - syslog_port: - description: - - Syslog port that will be set. - required: False - - syslog_server: - description: - - Server the syslogs will be sent to. - required: False - - syslog_status: - description: - - Enables or disables syslogs. - required: False - choices: ["enable", "disable"] - - syslog_mode: - description: - - Remote syslog logging over UDP/Reliable TCP. - - choice | udp | Enable syslogging over UDP. - - choice | legacy-reliable | Enable legacy reliable syslogging by RFC3195 (Reliable Delivery for Syslog). - - choice | reliable | Enable reliable syslogging by RFC6587 (Transmission of Syslog Messages over TCP). - required: false - choices: ["udp", "legacy-reliable", "reliable"] - default: "udp" - - syslog_filter: - description: - - Sets the logging level for syslog. - required: False - choices: ["emergency", "alert", "critical", "error", "warning", "notification", "information", "debug"] - - syslog_facility: - description: - - Remote syslog facility. - - choice | kernel | Kernel messages. - - choice | user | Random user-level messages. - - choice | mail | Mail system. - - choice | daemon | System daemons. - - choice | auth | Security/authorization messages. - - choice | syslog | Messages generated internally by syslog. - - choice | lpr | Line printer subsystem. - - choice | news | Network news subsystem. - - choice | uucp | Network news subsystem. - - choice | cron | Clock daemon. - - choice | authpriv | Security/authorization messages (private). - - choice | ftp | FTP daemon. - - choice | ntp | NTP daemon. - - choice | audit | Log audit. - - choice | alert | Log alert. - - choice | clock | Clock daemon. - - choice | local0 | Reserved for local use. - - choice | local1 | Reserved for local use. - - choice | local2 | Reserved for local use. - - choice | local3 | Reserved for local use. - - choice | local4 | Reserved for local use. - - choice | local5 | Reserved for local use. - - choice | local6 | Reserved for local use. - - choice | local7 | Reserved for local use. - required: false - choices: ["kernel", "user", "mail", "daemon", "auth", "syslog", - "lpr", "news", "uucp", "cron", "authpriv", "ftp", "ntp", "audit", - "alert", "clock", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7"] - default: "syslog" - - syslog_enc_algorithm: - description: - - Enable/disable reliable syslogging with TLS encryption. - - choice | high | SSL communication with high encryption algorithms. - - choice | low | SSL communication with low encryption algorithms. - - choice | disable | Disable SSL communication. - - choice | high-medium | SSL communication with high and medium encryption algorithms. - required: false - choices: ["high", "low", "disable", "high-medium"] - default: "disable" - - syslog_certificate: - description: - - Certificate used to communicate with Syslog server if encryption on. - required: false - - ntp_status: - description: - - Enables or disables ntp. - required: False - choices: ["enable", "disable"] - - ntp_sync_interval: - description: - - Sets the interval in minutes for ntp sync. - required: False - - ntp_type: - description: - - Enables fortiguard servers or custom servers are the ntp source. - required: False - choices: ["fortiguard", "custom"] - - ntp_server: - description: - - Only used with custom ntp_type -- specifies IP of server to sync to -- comma separated ip addresses for multiples. - required: False - - ntp_auth: - description: - - Enables or disables ntp authentication. - required: False - choices: ["enable", "disable"] - - ntp_auth_pwd: - description: - - Sets the ntp auth password. - required: False - - ntp_v3: - description: - - Enables or disables ntpv3 (default is ntpv4). - required: False - choices: ["enable", "disable"] - - admin_https_redirect: - description: - - Enables or disables https redirect from http. - required: False - choices: ["enable", "disable"] - - admin_https_port: - description: - - SSL admin gui port number. - required: False - - admin_http_port: - description: - - Non-SSL admin gui port number. - required: False - - admin_timeout: - description: - - Admin timeout in minutes. - required: False - - admin_language: - description: - - Sets the admin gui language. - required: False - choices: ["english", "simch", "japanese", "korean", "spanish", "trach", "french", "portuguese"] - - admin_switch_controller: - description: - - Enables or disables the switch controller. - required: False - choices: ["enable", "disable"] - - admin_gui_theme: - description: - - Changes the admin gui theme. - required: False - choices: ["green", "red", "blue", "melongene", "mariner"] - - admin_enable_fortiguard: - description: - - Enables FortiGuard security updates to their default settings. - required: False - choices: ["none", "direct", "this-fmg"] - - admin_fortianalyzer_target: - description: - - Configures faz target. - required: False - - admin_fortiguard_target: - description: - - Configures fortiguard target. - - admin_enable_fortiguard must be set to "direct". - required: False - - smtp_username: - description: - - SMTP auth username. - required: False - - smtp_password: - description: - - SMTP password. - required: False - - smtp_port: - description: - - SMTP port number. - required: False - - smtp_replyto: - description: - - SMTP reply to address. - required: False - - smtp_conn_sec: - description: - - defines the ssl level for smtp. - required: False - choices: ["none", "starttls", "smtps"] - - smtp_server: - description: - - SMTP server ipv4 address. - required: False - - smtp_source_ipv4: - description: - - SMTP source ip address. - required: False - - smtp_validate_cert: - description: - - Enables or disables valid certificate checking for smtp. - required: False - choices: ["enable", "disable"] - - dns_suffix: - description: - - Sets the local dns domain suffix. - required: False - - dns_primary_ipv4: - description: - - primary ipv4 dns forwarder. - required: False - - dns_secondary_ipv4: - description: - - secondary ipv4 dns forwarder. - required: False - - delete_provisioning_template: - description: - - If specified, all other options are ignored. The specified provisioning template will be deleted. - required: False - -''' - - -EXAMPLES = ''' -- name: SET SNMP SYSTEM INFO - fmgr_device_provision_template: - provisioning_template: "default" - snmp_status: "enable" - mode: "set" - -- name: SET SNMP SYSTEM INFO ANSIBLE ADOM - fmgr_device_provision_template: - provisioning_template: "default" - snmp_status: "enable" - mode: "set" - adom: "ansible" - -- name: SET SNMP SYSTEM INFO different template (SNMPv2) - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - snmp_status: "enable" - mode: "set" - adom: "ansible" - snmp_v2c_query_port: "162" - snmp_v2c_trap_port: "161" - snmp_v2c_status: "enable" - snmp_v2c_trap_status: "enable" - snmp_v2c_query_status: "enable" - snmp_v2c_name: "ansibleV2c" - snmp_v2c_id: "1" - snmp_v2c_trap_src_ipv4: "10.7.220.41" - snmp_v2c_trap_hosts_ipv4: "10.7.220.59 255.255.255.255, 10.7.220.60 255.255.255.255" - snmp_v2c_query_hosts_ipv4: "10.7.220.59 255.255.255.255, 10.7.220.0 255.255.255.0" - -- name: SET SNMP SYSTEM INFO different template (SNMPv3) - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - snmp_status: "enable" - mode: "set" - adom: "ansible" - snmpv3_auth_proto: "sha" - snmpv3_auth_pwd: "fortinet" - snmpv3_name: "ansibleSNMPv3" - snmpv3_notify_hosts: "10.7.220.59,10.7.220.60" - snmpv3_priv_proto: "aes256" - snmpv3_priv_pwd: "fortinet" - snmpv3_queries: "enable" - snmpv3_query_port: "161" - snmpv3_security_level: "auth_priv" - snmpv3_source_ip: "0.0.0.0" - snmpv3_status: "enable" - snmpv3_trap_rport: "162" - snmpv3_trap_status: "enable" - -- name: SET SYSLOG INFO - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - mode: "set" - adom: "ansible" - syslog_server: "10.7.220.59" - syslog_port: "514" - syslog_mode: "disable" - syslog_status: "enable" - syslog_filter: "information" - -- name: SET NTP TO FORTIGUARD - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - mode: "set" - adom: "ansible" - ntp_status: "enable" - ntp_sync_interval: "60" - type: "fortiguard" - -- name: SET NTP TO CUSTOM SERVER - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - mode: "set" - adom: "ansible" - ntp_status: "enable" - ntp_sync_interval: "60" - ntp_type: "custom" - ntp_server: "10.7.220.32,10.7.220.1" - ntp_auth: "enable" - ntp_auth_pwd: "fortinet" - ntp_v3: "disable" - -- name: SET ADMIN GLOBAL SETTINGS - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - mode: "set" - adom: "ansible" - admin_https_redirect: "enable" - admin_https_port: "4433" - admin_http_port: "8080" - admin_timeout: "30" - admin_language: "english" - admin_switch_controller: "enable" - admin_gui_theme: "blue" - admin_enable_fortiguard: "direct" - admin_fortiguard_target: "10.7.220.128" - admin_fortianalyzer_target: "10.7.220.61" - -- name: SET CUSTOM SMTP SERVER - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - mode: "set" - adom: "ansible" - smtp_username: "ansible" - smtp_password: "fortinet" - smtp_port: "25" - smtp_replyto: "ansible@do-not-reply.com" - smtp_conn_sec: "starttls" - smtp_server: "10.7.220.32" - smtp_source_ipv4: "0.0.0.0" - smtp_validate_cert: "disable" - -- name: SET DNS SERVERS - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - mode: "set" - adom: "ansible" - dns_suffix: "ansible.local" - dns_primary_ipv4: "8.8.8.8" - dns_secondary_ipv4: "4.4.4.4" - -- name: SET PROVISIONING TEMPLATE DEVICE TARGETS IN FORTIMANAGER - fmgr_device_provision_template: - provisioning_template: "ansibleTest" - mode: "set" - adom: "ansible" - provision_targets: "FGT1, FGT2" - -- name: DELETE ENTIRE PROVISIONING TEMPLATE - fmgr_device_provision_template: - delete_provisioning_template: "ansibleTest" - mode: "delete" - adom: "ansible" - -''' -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG - - -def get_devprof(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - response = DEFAULT_RESULT_OBJ - datagram = {} - - url = "/pm/devprof/adom/{adom}/{name}".format(adom=paramgram["adom"], name=paramgram["provisioning_template"]) - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - - return response - - -def set_devprof(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - response = DEFAULT_RESULT_OBJ - if paramgram["mode"] in ['set', 'add', 'update']: - datagram = { - "name": paramgram["provisioning_template"], - "type": "devprof", - "description": "CreatedByAnsible", - } - url = "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"]) - - elif paramgram["mode"] == "delete": - datagram = {} - - url = "/pm/devprof/adom/{adom}/{name}".format(adom=paramgram["adom"], - name=paramgram["delete_provisioning_template"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def get_devprof_scope(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - response = DEFAULT_RESULT_OBJ - datagram = { - "name": paramgram["provisioning_template"], - "type": "devprof", - "description": "CreatedByAnsible", - } - - url = "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"]) - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - - return response - - -def set_devprof_scope(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - response = DEFAULT_RESULT_OBJ - if paramgram["mode"] in ['set', 'add', 'update']: - datagram = { - "name": paramgram["provisioning_template"], - "type": "devprof", - "description": "CreatedByAnsible", - } - - targets = [] - for target in paramgram["provision_targets"].split(","): - # split the host on the space to get the mask out - new_target = {"name": target.strip()} - targets.append(new_target) - - datagram["scope member"] = targets - - url = "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"]) - - elif paramgram["mode"] == "delete": - datagram = { - "name": paramgram["provisioning_template"], - "type": "devprof", - "description": "CreatedByAnsible", - "scope member": paramgram["targets_to_add"] - } - - url = "/pm/devprof/adom/{adom}".format(adom=paramgram["adom"]) - - response = fmgr.process_request(url, datagram, FMGRMethods.SET) - return response - - -def set_devprof_snmp(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - datagram = { - "status": paramgram["snmp_status"] - } - url = "/pm/config/adom/{adom}/devprof/" \ - "{provisioning_template}/system/snmp/sysinfo".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - - response = fmgr.process_request(url, datagram, FMGRMethods.SET) - return response - - -def set_devprof_snmp_v2c(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - if paramgram["mode"] in ['set', 'add', 'update']: - datagram = { - "query-v2c-port": paramgram["snmp_v2c_query_port"], - "trap-v2c-rport": paramgram["snmp_v2c_trap_port"], - "status": paramgram["snmp_v2c_status"], - "trap-v2c-status": paramgram["snmp_v2c_trap_status"], - "query-v2c-status": paramgram["snmp_v2c_query_status"], - "name": paramgram["snmp_v2c_name"], - "id": paramgram["snmp_v2c_id"], - "meta fields": dict(), - "hosts": list(), - "events": 411578417151, - "query-v1-status": 0, - "query-v1-port": 161, - "trap-v1-status": 0, - "trap-v1-lport": 162, - "trap-v1-rport": 162, - "trap-v2c-lport": 162, - } - - # BUILD THE HOST STRINGS - id_counter = 1 - if paramgram["snmp_v2c_trap_hosts_ipv4"] or paramgram["snmp_v2c_query_hosts_ipv4"]: - hosts = [] - if paramgram["snmp_v2c_query_hosts_ipv4"]: - for ipv4_host in paramgram["snmp_v2c_query_hosts_ipv4"].strip().split(","): - # split the host on the space to get the mask out - new_ipv4_host = {"ha-direct": "enable", - "host-type": "query", - "id": id_counter, - "ip": ipv4_host.strip().split(), - "meta fields": {}, - "source-ip": "0.0.0.0"} - hosts.append(new_ipv4_host) - id_counter += 1 - - if paramgram["snmp_v2c_trap_hosts_ipv4"]: - for ipv4_host in paramgram["snmp_v2c_trap_hosts_ipv4"].strip().split(","): - # split the host on the space to get the mask out - new_ipv4_host = {"ha-direct": "enable", - "host-type": "trap", - "id": id_counter, - "ip": ipv4_host.strip().split(), - "meta fields": {}, - "source-ip": paramgram["snmp_v2c_trap_src_ipv4"]} - hosts.append(new_ipv4_host) - id_counter += 1 - datagram["hosts"] = hosts - - url = "/pm/config/adom/{adom}/devprof/" \ - "{provisioning_template}/system/snmp/community".format(adom=adom, - provisioning_template=paramgram[ - "provisioning_template"]) - elif paramgram["mode"] == "delete": - datagram = { - "confirm": 1 - } - - url = "/pm/config/adom/{adom}/" \ - "devprof/{provisioning_template}/" \ - "system/snmp/community/{snmp_v2c_id}".format(adom=adom, - provisioning_template=paramgram["provisioning_template"], - snmp_v2c_id=paramgram["snmp_v2c_id"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_snmp_v3(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - if paramgram["mode"] in ['set', 'add', 'update']: - datagram = {} - datagram["auth-pwd"] = paramgram["snmpv3_auth_pwd"] - datagram["priv-pwd"] = paramgram["snmpv3_priv_pwd"] - datagram["trap-rport"] = paramgram["snmpv3_trap_rport"] - datagram["query-port"] = paramgram["snmpv3_query_port"] - datagram["name"] = paramgram["snmpv3_name"] - datagram["notify-hosts"] = paramgram["snmpv3_notify_hosts"].strip().split(",") - datagram["events"] = 1647387997183 - datagram["trap-lport"] = 162 - - datagram["source-ip"] = paramgram["snmpv3_source_ip"] - datagram["ha-direct"] = 0 - - url = "/pm/config/adom/{adom}/" \ - "devprof/{provisioning_template}/" \ - "system/snmp/user".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - elif paramgram["mode"] == "delete": - datagram = { - "confirm": 1 - } - - url = "/pm/config/adom/{adom}/devprof/" \ - "{provisioning_template}/system/snmp" \ - "/user/{snmpv3_name}".format(adom=adom, - provisioning_template=paramgram["provisioning_template"], - snmpv3_name=paramgram["snmpv3_name"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_syslog(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - - datagram = { - "status": paramgram["syslog_status"], - "port": paramgram["syslog_port"], - "server": paramgram["syslog_server"], - "mode": paramgram["syslog_mode"], - "facility": paramgram["syslog_facility"] - } - - if paramgram["mode"] in ['set', 'add', 'update']: - if paramgram["syslog_enc_algorithm"] in ["high", "low", "high-medium"] \ - and paramgram["syslog_certificate"] is not None: - datagram["certificate"] = paramgram["certificate"] - datagram["enc-algorithm"] = paramgram["syslog_enc_algorithm"] - - url = "/pm/config/adom/{adom}/" \ - "devprof/{provisioning_template}/" \ - "log/syslogd/setting".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - elif paramgram["mode"] == "delete": - url = "/pm/config/adom/{adom}/" \ - "devprof/{provisioning_template}/" \ - "log/syslogd/setting".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_syslog_filter(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - datagram = { - "severity": paramgram["syslog_filter"] - } - response = DEFAULT_RESULT_OBJ - - url = "/pm/config/adom/{adom}" \ - "/devprof/{provisioning_template}" \ - "/log/syslogd/filter".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_ntp(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - - # IF SET TO FORTIGUARD, BUILD A STRING SPECIFIC TO THAT - if paramgram["ntp_type"] == "fortiguard": - datagram = {} - if paramgram["ntp_status"] == "enable": - datagram["ntpsync"] = 1 - if paramgram["ntp_status"] == "disable": - datagram["ntpsync"] = 0 - if paramgram["ntp_sync_interval"] is None: - datagram["syncinterval"] = 1 - else: - datagram["syncinterval"] = paramgram["ntp_sync_interval"] - - datagram["type"] = 0 - - # IF THE NTP TYPE IS CUSTOM BUILD THE SERVER LIST - if paramgram["ntp_type"] == "custom": - id_counter = 0 - key_counter = 0 - ntpservers = [] - datagram = {} - if paramgram["ntp_status"] == "enable": - datagram["ntpsync"] = 1 - if paramgram["ntp_status"] == "disable": - datagram["ntpsync"] = 0 - try: - datagram["syncinterval"] = paramgram["ntp_sync_interval"] - except BaseException: - datagram["syncinterval"] = 1 - datagram["type"] = 1 - - for server in paramgram["ntp_server"].strip().split(","): - id_counter += 1 - server_fields = dict() - - key_counter += 1 - if paramgram["ntp_auth"] == "enable": - server_fields["authentication"] = 1 - server_fields["key"] = paramgram["ntp_auth_pwd"] - server_fields["key-id"] = key_counter - else: - server_fields["authentication"] = 0 - server_fields["key"] = "" - server_fields["key-id"] = key_counter - - if paramgram["ntp_v3"] == "enable": - server_fields["ntp_v3"] = 1 - else: - server_fields["ntp_v3"] = 0 - - # split the host on the space to get the mask out - new_ntp_server = {"authentication": server_fields["authentication"], - "id": id_counter, "key": server_fields["key"], - "key-id": id_counter, "ntpv3": server_fields["ntp_v3"], - "server": server} - ntpservers.append(new_ntp_server) - datagram["ntpserver"] = ntpservers - - url = "/pm/config/adom/{adom}" \ - "/devprof/{provisioning_template}" \ - "/system/ntp".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_admin(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - datagram = { - "admin-https-redirect": paramgram["admin_https_redirect"], - "admin-port": paramgram["admin_http_port"], - "admin-sport": paramgram["admin_https_port"], - "admintimeout": paramgram["admin_timeout"], - "language": paramgram["admin_language"], - "gui-theme": paramgram["admin_gui_theme"], - "switch-controller": paramgram["admin_switch_controller"], - } - url = "/pm/config/adom/{adom}" \ - "/devprof/{provisioning_template}" \ - "/system/global".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_smtp(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - datagram = { - "port": paramgram["smtp_port"], - "reply-to": paramgram["smtp_replyto"], - "server": paramgram["smtp_server"], - "source-ip": paramgram["smtp_source_ipv4"] - } - - if paramgram["smtp_username"]: - datagram["authenticate"] = 1 - datagram["username"] = paramgram["smtp_username"] - datagram["password"] = paramgram["smtp_password"] - - if paramgram["smtp_conn_sec"] == "none": - datagram["security"] = 0 - if paramgram["smtp_conn_sec"] == "starttls": - datagram["security"] = 1 - if paramgram["smtp_conn_sec"] == "smtps": - datagram["security"] = 2 - - if paramgram["smtp_validate_cert"] == "enable": - datagram["validate-server"] = 1 - else: - datagram["validate-server"] = 0 - - url = "/pm/config/adom/{adom}" \ - "/devprof/{provisioning_template}" \ - "/system/email-server".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_dns(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - datagram = { - "domain": paramgram["dns_suffix"], - "primary": paramgram["dns_primary_ipv4"], - "secondary": paramgram["dns_secondary_ipv4"], - } - url = "/pm/config/adom/{adom}" \ - "/devprof/{provisioning_template}" \ - "/system/dns".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_toggle_fg(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - response = DEFAULT_RESULT_OBJ - datagram = {} - if paramgram["admin_enable_fortiguard"] in ["direct", "this-fmg"]: - datagram["include-default-servers"] = "enable" - elif paramgram["admin_enable_fortiguard"] == "none": - datagram["include-default-servers"] = "disable" - - datagram["server-list"] = list() - - url = "/pm/config/adom/{adom}" \ - "/devprof/{provisioning_template}" \ - "/system/central-management".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - response = fmgr.process_request(url, datagram, FMGRMethods.SET) - - return response - - -def set_devprof_fg(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - datagram = { - "target": paramgram["admin_enable_fortiguard"], - "target-ip": None - } - - if paramgram["mode"] in ['set', 'add', 'update']: - if paramgram["admin_fortiguard_target"] is not None and datagram["target"] == "direct": - datagram["target-ip"] = paramgram["admin_fortiguard_target"] - - url = "/pm/config/adom/{adom}" \ - "/devprof/{provisioning_template}" \ - "/device/profile/fortiguard".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def set_devprof_faz(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - paramgram["mode"] = paramgram["mode"] - adom = paramgram["adom"] - response = DEFAULT_RESULT_OBJ - datagram = { - "target-ip": paramgram["admin_fortianalyzer_target"], - "target": "others", - } - url = "/pm/config/adom/{adom}" \ - "/devprof/{provisioning_template}" \ - "/device/profile/fortianalyzer".format(adom=adom, - provisioning_template=paramgram["provisioning_template"]) - if paramgram["mode"] == "delete": - datagram["hastarget"] = "False" - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - provisioning_template=dict(required=False, type="str"), - provision_targets=dict(required=False, type="str"), - - device_unique_name=dict(required=False, type="str"), - snmp_status=dict(required=False, type="str", choices=["enable", "disable"]), - snmp_v2c_query_port=dict(required=False, type="int"), - snmp_v2c_trap_port=dict(required=False, type="int"), - snmp_v2c_status=dict(required=False, type="str", choices=["enable", "disable"]), - snmp_v2c_trap_status=dict(required=False, type="str", choices=["enable", "disable"]), - snmp_v2c_query_status=dict(required=False, type="str", choices=["enable", "disable"]), - snmp_v2c_name=dict(required=False, type="str", no_log=True), - snmp_v2c_id=dict(required=False, type="int"), - snmp_v2c_trap_src_ipv4=dict(required=False, type="str"), - snmp_v2c_trap_hosts_ipv4=dict(required=False, type="str"), - snmp_v2c_query_hosts_ipv4=dict(required=False, type="str"), - - snmpv3_auth_proto=dict(required=False, type="str", choices=["md5", "sha"]), - snmpv3_auth_pwd=dict(required=False, type="str", no_log=True), - snmpv3_name=dict(required=False, type="str"), - snmpv3_notify_hosts=dict(required=False, type="str"), - snmpv3_priv_proto=dict(required=False, type="str", choices=["aes", "des", "aes256", "aes256cisco"]), - snmpv3_priv_pwd=dict(required=False, type="str", no_log=True), - snmpv3_queries=dict(required=False, type="str", choices=["enable", "disable"]), - snmpv3_query_port=dict(required=False, type="int"), - snmpv3_security_level=dict(required=False, type="str", - choices=["no-auth-no-priv", "auth-no-priv", "auth-priv"]), - snmpv3_source_ip=dict(required=False, type="str"), - snmpv3_status=dict(required=False, type="str", choices=["enable", "disable"]), - snmpv3_trap_rport=dict(required=False, type="int"), - snmpv3_trap_status=dict(required=False, type="str", choices=["enable", "disable"]), - - syslog_port=dict(required=False, type="int"), - syslog_server=dict(required=False, type="str"), - syslog_mode=dict(required=False, type="str", choices=["udp", "legacy-reliable", "reliable"], default="udp"), - syslog_status=dict(required=False, type="str", choices=["enable", "disable"]), - syslog_filter=dict(required=False, type="str", choices=["emergency", "alert", "critical", "error", - "warning", "notification", "information", "debug"]), - syslog_enc_algorithm=dict(required=False, type="str", choices=["high", "low", "disable", "high-medium"], - default="disable"), - syslog_facility=dict(required=False, type="str", choices=["kernel", "user", "mail", "daemon", "auth", - "syslog", "lpr", "news", "uucp", "cron", "authpriv", - "ftp", "ntp", "audit", "alert", "clock", "local0", - "local1", "local2", "local3", "local4", "local5", - "local6", "local7"], default="syslog"), - syslog_certificate=dict(required=False, type="str"), - - ntp_status=dict(required=False, type="str", choices=["enable", "disable"]), - ntp_sync_interval=dict(required=False, type="int"), - ntp_type=dict(required=False, type="str", choices=["fortiguard", "custom"]), - ntp_server=dict(required=False, type="str"), - ntp_auth=dict(required=False, type="str", choices=["enable", "disable"]), - ntp_auth_pwd=dict(required=False, type="str", no_log=True), - ntp_v3=dict(required=False, type="str", choices=["enable", "disable"]), - - admin_https_redirect=dict(required=False, type="str", choices=["enable", "disable"]), - admin_https_port=dict(required=False, type="int"), - admin_http_port=dict(required=False, type="int"), - admin_timeout=dict(required=False, type="int"), - admin_language=dict(required=False, type="str", - choices=["english", "simch", "japanese", "korean", - "spanish", "trach", "french", "portuguese"]), - admin_switch_controller=dict(required=False, type="str", choices=["enable", "disable"]), - admin_gui_theme=dict(required=False, type="str", choices=["green", "red", "blue", "melongene", "mariner"]), - admin_enable_fortiguard=dict(required=False, type="str", choices=["none", "direct", "this-fmg"]), - admin_fortianalyzer_target=dict(required=False, type="str"), - admin_fortiguard_target=dict(required=False, type="str"), - - smtp_username=dict(required=False, type="str"), - smtp_password=dict(required=False, type="str", no_log=True), - smtp_port=dict(required=False, type="int"), - smtp_replyto=dict(required=False, type="str"), - smtp_conn_sec=dict(required=False, type="str", choices=["none", "starttls", "smtps"]), - smtp_server=dict(required=False, type="str"), - smtp_source_ipv4=dict(required=False, type="str"), - smtp_validate_cert=dict(required=False, type="str", choices=["enable", "disable"]), - - dns_suffix=dict(required=False, type="str"), - dns_primary_ipv4=dict(required=False, type="str"), - dns_secondary_ipv4=dict(required=False, type="str"), - delete_provisioning_template=dict(required=False, type="str") - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - paramgram = { - "adom": module.params["adom"], - "mode": module.params["mode"], - "provision_targets": module.params["provision_targets"], - "provisioning_template": module.params["provisioning_template"], - - "snmp_status": module.params["snmp_status"], - "snmp_v2c_query_port": module.params["snmp_v2c_query_port"], - "snmp_v2c_trap_port": module.params["snmp_v2c_trap_port"], - "snmp_v2c_status": module.params["snmp_v2c_status"], - "snmp_v2c_trap_status": module.params["snmp_v2c_trap_status"], - "snmp_v2c_query_status": module.params["snmp_v2c_query_status"], - "snmp_v2c_name": module.params["snmp_v2c_name"], - "snmp_v2c_id": module.params["snmp_v2c_id"], - "snmp_v2c_trap_src_ipv4": module.params["snmp_v2c_trap_src_ipv4"], - "snmp_v2c_trap_hosts_ipv4": module.params["snmp_v2c_trap_hosts_ipv4"], - "snmp_v2c_query_hosts_ipv4": module.params["snmp_v2c_query_hosts_ipv4"], - - "snmpv3_auth_proto": module.params["snmpv3_auth_proto"], - "snmpv3_auth_pwd": module.params["snmpv3_auth_pwd"], - "snmpv3_name": module.params["snmpv3_name"], - "snmpv3_notify_hosts": module.params["snmpv3_notify_hosts"], - "snmpv3_priv_proto": module.params["snmpv3_priv_proto"], - "snmpv3_priv_pwd": module.params["snmpv3_priv_pwd"], - "snmpv3_queries": module.params["snmpv3_queries"], - "snmpv3_query_port": module.params["snmpv3_query_port"], - "snmpv3_security_level": module.params["snmpv3_security_level"], - "snmpv3_source_ip": module.params["snmpv3_source_ip"], - "snmpv3_status": module.params["snmpv3_status"], - "snmpv3_trap_rport": module.params["snmpv3_trap_rport"], - "snmpv3_trap_status": module.params["snmpv3_trap_status"], - - "syslog_port": module.params["syslog_port"], - "syslog_server": module.params["syslog_server"], - "syslog_mode": module.params["syslog_mode"], - "syslog_status": module.params["syslog_status"], - "syslog_filter": module.params["syslog_filter"], - "syslog_facility": module.params["syslog_facility"], - "syslog_enc_algorithm": module.params["syslog_enc_algorithm"], - "syslog_certificate": module.params["syslog_certificate"], - - "ntp_status": module.params["ntp_status"], - "ntp_sync_interval": module.params["ntp_sync_interval"], - "ntp_type": module.params["ntp_type"], - "ntp_server": module.params["ntp_server"], - "ntp_auth": module.params["ntp_auth"], - "ntp_auth_pwd": module.params["ntp_auth_pwd"], - "ntp_v3": module.params["ntp_v3"], - - "admin_https_redirect": module.params["admin_https_redirect"], - "admin_https_port": module.params["admin_https_port"], - "admin_http_port": module.params["admin_http_port"], - "admin_timeout": module.params["admin_timeout"], - "admin_language": module.params["admin_language"], - "admin_switch_controller": module.params["admin_switch_controller"], - "admin_gui_theme": module.params["admin_gui_theme"], - "admin_enable_fortiguard": module.params["admin_enable_fortiguard"], - "admin_fortianalyzer_target": module.params["admin_fortianalyzer_target"], - "admin_fortiguard_target": module.params["admin_fortiguard_target"], - - "smtp_username": module.params["smtp_username"], - "smtp_password": module.params["smtp_password"], - "smtp_port": module.params["smtp_port"], - "smtp_replyto": module.params["smtp_replyto"], - "smtp_conn_sec": module.params["smtp_conn_sec"], - "smtp_server": module.params["smtp_server"], - "smtp_source_ipv4": module.params["smtp_source_ipv4"], - "smtp_validate_cert": module.params["smtp_validate_cert"], - - "dns_suffix": module.params["dns_suffix"], - "dns_primary_ipv4": module.params["dns_primary_ipv4"], - "dns_secondary_ipv4": module.params["dns_secondary_ipv4"], - "delete_provisioning_template": module.params["delete_provisioning_template"] - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - results = DEFAULT_RESULT_OBJ - try: - # CHECK IF WE ARE DELETING AN ENTIRE TEMPLATE. IF THAT'S THE CASE DO IT FIRST AND IGNORE THE REST. - if paramgram["delete_provisioning_template"] is not None: - results = set_devprof(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -10, -1], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram), - stop_on_success=True) - except Exception as err: - raise FMGBaseException(err) - - try: - # CHECK TO SEE IF THE DEVPROF TEMPLATE EXISTS - devprof = get_devprof(fmgr, paramgram) - if devprof[0] != 0: - results = set_devprof(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -2], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # PROCESS THE SNMP SETTINGS IF THE SNMP_STATUS VARIABLE IS SET - if paramgram["snmp_status"] is not None: - results = set_devprof_snmp(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - # PROCESS THE SNMP V2C COMMUNITY SETTINGS IF THEY ARE ALL HERE - if all(v is not None for v in (paramgram["snmp_v2c_query_port"], paramgram["snmp_v2c_trap_port"], - paramgram["snmp_v2c_status"], paramgram["snmp_v2c_trap_status"], - paramgram["snmp_v2c_query_status"], paramgram["snmp_v2c_name"], - paramgram["snmp_v2c_id"])): - results = set_devprof_snmp_v2c(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -10033], stop_on_success=True, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - # PROCESS THE SNMPV3 USER IF THERE - if all(v is not None for v in ( - [paramgram["snmpv3_auth_proto"], paramgram["snmpv3_auth_pwd"], paramgram["snmpv3_name"], - paramgram["snmpv3_notify_hosts"], paramgram["snmpv3_priv_proto"], - paramgram["snmpv3_priv_pwd"], - paramgram["snmpv3_queries"], - paramgram["snmpv3_query_port"], paramgram["snmpv3_security_level"], - paramgram["snmpv3_source_ip"], - paramgram["snmpv3_status"], paramgram["snmpv3_trap_rport"], paramgram["snmpv3_trap_status"]])): - - results = set_devprof_snmp_v3(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -10033, -10000, -3], - stop_on_success=True, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # PROCESS THE SYSLOG SETTINGS IF THE ALL THE NEEDED SYSLOG VARIABLES ARE PRESENT - if all(v is not None for v in [paramgram["syslog_port"], paramgram["syslog_mode"], - paramgram["syslog_server"], paramgram["syslog_status"]]): - # enable syslog in the devprof template - results = set_devprof_syslog(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -10033, -10000, -3], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # IF THE SYSLOG FILTER IS PRESENT THEN RUN THAT - if paramgram["syslog_filter"] is not None: - results = set_devprof_syslog_filter(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # PROCESS NTP OPTIONS - if paramgram["ntp_status"]: - # VALIDATE INPUT - if paramgram["ntp_type"] == "custom" and paramgram["ntp_server"] is None: - module.exit_json(msg="You requested custom NTP type but did not provide ntp_server parameter.") - if paramgram["ntp_auth"] == "enable" and paramgram["ntp_auth_pwd"] is None: - module.exit_json( - msg="You requested NTP Authentication but did not provide ntp_auth_pwd parameter.") - - results = set_devprof_ntp(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - try: - # PROCESS THE ADMIN OPTIONS - if any(v is not None for v in ( - paramgram["admin_https_redirect"], paramgram["admin_https_port"], paramgram["admin_http_port"], - paramgram["admin_timeout"], - paramgram["admin_language"], paramgram["admin_switch_controller"], - paramgram["admin_gui_theme"])): - - results = set_devprof_admin(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # PROCESS FORTIGUARD OPTIONS - if paramgram["admin_enable_fortiguard"] is not None: - - results = set_devprof_toggle_fg(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - results = set_devprof_fg(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # PROCESS THE SMTP OPTIONS - if all(v is not None for v in ( - paramgram["smtp_username"], paramgram["smtp_password"], paramgram["smtp_port"], - paramgram["smtp_replyto"], - paramgram["smtp_conn_sec"], paramgram["smtp_server"], - paramgram["smtp_source_ipv4"], paramgram["smtp_validate_cert"])): - - results = set_devprof_smtp(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # PROCESS THE DNS OPTIONS - if any(v is not None for v in - (paramgram["dns_suffix"], paramgram["dns_primary_ipv4"], paramgram["dns_secondary_ipv4"])): - results = set_devprof_dns(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # PROCESS THE admin_fortianalyzer_target OPTIONS - if paramgram["admin_fortianalyzer_target"] is not None: - - results = set_devprof_faz(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # PROCESS THE PROVISIONING TEMPLATE TARGET PARAMETER - if paramgram["provision_targets"] is not None: - if paramgram["mode"] != "delete": - results = set_devprof_scope(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0], stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - if paramgram["mode"] == "delete": - # WE NEED TO FIGURE OUT WHAT'S THERE FIRST, BEFORE WE CAN RUN THIS - targets_to_add = list() - try: - current_scope = get_devprof_scope(fmgr, paramgram) - targets_to_remove = paramgram["provision_targets"].strip().split(",") - targets = current_scope[1][1]["scope member"] - for target in targets: - if target["name"] not in targets_to_remove: - target_append = {"name": target["name"]} - targets_to_add.append(target_append) - except BaseException: - pass - paramgram["targets_to_add"] = targets_to_add - results = set_devprof_scope(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -10033, -10000, -3], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_fwobj_address.py b/plugins/modules/network/fortimanager/fmgr_fwobj_address.py deleted file mode 100644 index 58b23cf847..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_fwobj_address.py +++ /dev/null @@ -1,667 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_fwobj_address -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Allows the management of firewall objects in FortiManager -description: - - Allows for the management of IPv4, IPv6, and multicast address objects within FortiManager. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - allow_routing: - description: - - Enable/disable use of this address in the static route configuration. - choices: ['enable', 'disable'] - default: 'disable' - - associated_interface: - description: - - Associated interface name. - - cache_ttl: - description: - - Minimal TTL of individual IP addresses in FQDN cache. Only applies when type = wildcard-fqdn. - - color: - description: - - Color of the object in FortiManager GUI. - - Takes integers 1-32 - default: 22 - - comment: - description: - - Comment for the object in FortiManager. - - country: - description: - - Country name. Required if type = geographic. - - end_ip: - description: - - End IP. Only used when ipv4 = iprange. - - group_members: - description: - - Address group member. If this is defined w/out group_name, the operation will fail. - - group_name: - description: - - Address group name. If this is defined in playbook task, all other options are ignored. - - ipv4: - description: - - Type of IPv4 Object. - - Must not be specified with either multicast or IPv6 parameters. - choices: ['ipmask', 'iprange', 'fqdn', 'wildcard', 'geography', 'wildcard-fqdn', 'group'] - - ipv4addr: - description: - - IP and network mask. If only defining one IP use this parameter. (i.e. 10.7.220.30/255.255.255.255) - - Can also define subnets (i.e. 10.7.220.0/255.255.255.0) - - Also accepts CIDR (i.e. 10.7.220.0/24) - - If Netmask is omitted after IP address, /32 is assumed. - - When multicast is set to Broadcast Subnet the ipv4addr parameter is used to specify the subnet. - - ipv6: - description: - - Puts module into IPv6 mode. - - Must not be specified with either ipv4 or multicast parameters. - choices: ['ip', 'iprange', 'group'] - - ipv6addr: - description: - - IPv6 address in full. (i.e. 2001:0db8:85a3:0000:0000:8a2e:0370:7334) - - fqdn: - description: - - Fully qualified domain name. - - mode: - description: - - Sets one of three modes for managing the object. - choices: ['add', 'set', 'delete'] - default: add - - multicast: - description: - - Manages Multicast Address Objects. - - Sets either a Multicast IP Range or a Broadcast Subnet. - - Must not be specified with either ipv4 or ipv6 parameters. - - When set to Broadcast Subnet the ipv4addr parameter is used to specify the subnet. - - Can create IPv4 Multicast Objects (multicastrange and broadcastmask options -- uses start/end-ip and ipv4addr). - choices: ['multicastrange', 'broadcastmask', 'ip6'] - - name: - description: - - Friendly Name Address object name in FortiManager. - - obj_id: - description: - - Object ID for NSX. - - start_ip: - description: - - Start IP. Only used when ipv4 = iprange. - - visibility: - description: - - Enable/disable address visibility. - choices: ['enable', 'disable'] - default: 'enable' - - wildcard: - description: - - IP address and wildcard netmask. Required if ipv4 = wildcard. - - wildcard_fqdn: - description: - - Wildcard FQDN. Required if ipv4 = wildcard-fqdn. -''' - -EXAMPLES = ''' -- name: ADD IPv4 IP ADDRESS OBJECT - fmgr_fwobj_address: - ipv4: "ipmask" - ipv4addr: "10.7.220.30/32" - name: "ansible_v4Obj" - comment: "Created by Ansible" - color: "6" - -- name: ADD IPv4 IP ADDRESS OBJECT MORE OPTIONS - fmgr_fwobj_address: - ipv4: "ipmask" - ipv4addr: "10.7.220.34/32" - name: "ansible_v4Obj_MORE" - comment: "Created by Ansible" - color: "6" - allow_routing: "enable" - cache_ttl: "180" - associated_interface: "port1" - obj_id: "123" - -- name: ADD IPv4 IP ADDRESS SUBNET OBJECT - fmgr_fwobj_address: - ipv4: "ipmask" - ipv4addr: "10.7.220.0/255.255.255.128" - name: "ansible_subnet" - comment: "Created by Ansible" - mode: "set" - -- name: ADD IPv4 IP ADDRESS RANGE OBJECT - fmgr_fwobj_address: - ipv4: "iprange" - start_ip: "10.7.220.1" - end_ip: "10.7.220.125" - name: "ansible_range" - comment: "Created by Ansible" - -- name: ADD IPv4 IP ADDRESS WILDCARD OBJECT - fmgr_fwobj_address: - ipv4: "wildcard" - wildcard: "10.7.220.30/255.255.255.255" - name: "ansible_wildcard" - comment: "Created by Ansible" - -- name: ADD IPv4 IP ADDRESS WILDCARD FQDN OBJECT - fmgr_fwobj_address: - ipv4: "wildcard-fqdn" - wildcard_fqdn: "*.myds.com" - name: "Synology myds DDNS service" - comment: "Created by Ansible" - -- name: ADD IPv4 IP ADDRESS FQDN OBJECT - fmgr_fwobj_address: - ipv4: "fqdn" - fqdn: "ansible.com" - name: "ansible_fqdn" - comment: "Created by Ansible" - -- name: ADD IPv4 IP ADDRESS GEO OBJECT - fmgr_fwobj_address: - ipv4: "geography" - country: "usa" - name: "ansible_geo" - comment: "Created by Ansible" - -- name: ADD IPv6 ADDRESS - fmgr_fwobj_address: - ipv6: "ip" - ipv6addr: "2001:0db8:85a3:0000:0000:8a2e:0370:7334" - name: "ansible_v6Obj" - comment: "Created by Ansible" - -- name: ADD IPv6 ADDRESS RANGE - fmgr_fwobj_address: - ipv6: "iprange" - start_ip: "2001:0db8:85a3:0000:0000:8a2e:0370:7334" - end_ip: "2001:0db8:85a3:0000:0000:8a2e:0370:7446" - name: "ansible_v6range" - comment: "Created by Ansible" - -- name: ADD IPv4 IP ADDRESS GROUP - fmgr_fwobj_address: - ipv4: "group" - group_name: "ansibleIPv4Group" - group_members: "ansible_fqdn, ansible_wildcard, ansible_range" - -- name: ADD IPv6 IP ADDRESS GROUP - fmgr_fwobj_address: - ipv6: "group" - group_name: "ansibleIPv6Group" - group_members: "ansible_v6Obj, ansible_v6range" - -- name: ADD MULTICAST RANGE - fmgr_fwobj_address: - multicast: "multicastrange" - start_ip: "224.0.0.251" - end_ip: "224.0.0.251" - name: "ansible_multicastrange" - comment: "Created by Ansible" - -- name: ADD BROADCAST SUBNET - fmgr_fwobj_address: - multicast: "broadcastmask" - ipv4addr: "10.7.220.0/24" - name: "ansible_broadcastSubnet" - comment: "Created by Ansible" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - - -import re -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG - - -def fmgr_fwobj_ipv4(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # EVAL THE MODE PARAMETER FOR SET OR ADD - if paramgram["mode"] in ['set', 'add']: - # CREATE THE DATAGRAM DICTIONARY - # ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE - # SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED - datagram = { - "comment": paramgram["comment"], - "associated-interface": paramgram["associated-interface"], - "cache-ttl": paramgram["cache-ttl"], - "name": paramgram["name"], - "allow-routing": paramgram["allow-routing"], - "color": paramgram["color"], - "meta fields": {}, - "dynamic_mapping": [], - "visibility": paramgram["allow-routing"], - "type": paramgram["ipv4"], - } - - # SET THE CORRECT URL BASED ON THE TYPE (WE'RE DOING GROUPS IN THIS METHOD, TOO) - if datagram["type"] == "group": - url = '/pm/config/adom/{adom}/obj/firewall/addrgrp'.format(adom=paramgram["adom"]) - else: - url = '/pm/config/adom/{adom}/obj/firewall/address'.format(adom=paramgram["adom"]) - - ######################### - # IF type = 'ipmask' - ######################### - if datagram["type"] == "ipmask": - # CREATE THE SUBNET LIST OBJECT - subnet = [] - # EVAL THE IPV4ADDR INPUT AND SPLIT THE IP ADDRESS FROM THE MASK AND APPEND THEM TO THE SUBNET LIST - for subnets in paramgram["ipv4addr"].split("/"): - subnet.append(subnets) - - # CHECK THAT THE SECOND ENTRY IN THE SUBNET LIST (WHAT WAS TO THE RIGHT OF THE / CHARACTER) - # IS IN SUBNET MASK FORMAT AND NOT CIDR FORMAT. - # IF IT IS IN CIDR FORMAT, WE NEED TO CONVERT IT TO SUBNET BIT MASK FORMAT FOR THE JSON API - if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]): - # IF THE SUBNET PARAMETER INPUT DIDN'T LOOK LIKE xxx.xxx.xxx.xxx TO REGEX... - # ... RUN IT THROUGH THE CIDR_TO_NETMASK() FUNCTION - mask = fmgr._tools.cidr_to_netmask(subnet[1]) - # AND THEN UPDATE THE SUBNET LIST OBJECT - subnet[1] = mask - - # INCLUDE THE SUBNET LIST OBJECT IN THE DATAGRAM DICTIONARY TO BE SUBMITTED - datagram["subnet"] = subnet - - ######################### - # IF type = 'iprange' - ######################### - if datagram["type"] == "iprange": - datagram["start-ip"] = paramgram["start-ip"] - datagram["end-ip"] = paramgram["end-ip"] - datagram["subnet"] = ["0.0.0.0", "0.0.0.0"] - - ######################### - # IF type = 'geography' - ######################### - if datagram["type"] == "geography": - datagram["country"] = paramgram["country"] - - ######################### - # IF type = 'wildcard' - ######################### - if datagram["type"] == "wildcard": - - subnet = [] - for subnets in paramgram["wildcard"].split("/"): - subnet.append(subnets) - - if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]): - mask = fmgr._tools.cidr_to_netmask(subnet[1]) - subnet[1] = mask - - datagram["wildcard"] = subnet - - ######################### - # IF type = 'wildcard-fqdn' - ######################### - if datagram["type"] == "wildcard-fqdn": - datagram["wildcard-fqdn"] = paramgram["wildcard-fqdn"] - - ######################### - # IF type = 'fqdn' - ######################### - if datagram["type"] == "fqdn": - datagram["fqdn"] = paramgram["fqdn"] - - ######################### - # IF type = 'group' - ######################### - if datagram["type"] == "group": - datagram = { - "comment": paramgram["comment"], - "name": paramgram["group_name"], - "color": paramgram["color"], - "meta fields": {}, - "dynamic_mapping": [], - "visibility": paramgram["visibility"] - } - - members = [] - group_members = paramgram["group_members"].replace(" ", "") - try: - for member in group_members.split(","): - members.append(member) - except Exception: - pass - - datagram["member"] = members - - # EVAL THE MODE PARAMETER FOR DELETE - if paramgram["mode"] == "delete": - # IF A GROUP, SET THE CORRECT NAME AND URL FOR THE GROUP ENDPOINT - if paramgram["ipv4"] == "group": - datagram = {} - url = '/pm/config/adom/{adom}/obj/firewall/addrgrp/{name}'.format(adom=paramgram["adom"], - name=paramgram["group_name"]) - # OTHERWISE WE'RE JUST GOING TO USE THE ADDRESS ENDPOINT - else: - datagram = {} - url = '/pm/config/adom/{adom}/obj/firewall/address/{name}'.format(adom=paramgram["adom"], - name=paramgram["name"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def fmgr_fwobj_ipv6(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # EVAL THE MODE PARAMETER FOR SET OR ADD - if paramgram["mode"] in ['set', 'add']: - # CREATE THE DATAGRAM DICTIONARY - # ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE - # SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED - datagram = { - "comment": paramgram["comment"], - "name": paramgram["name"], - "color": paramgram["color"], - "dynamic_mapping": [], - "visibility": paramgram["visibility"], - "type": paramgram["ipv6"] - } - - # SET THE CORRECT URL BASED ON THE TYPE (WE'RE DOING GROUPS IN THIS METHOD, TOO) - if datagram["type"] == "group": - url = '/pm/config/adom/{adom}/obj/firewall/addrgrp6'.format(adom=paramgram["adom"]) - else: - url = '/pm/config/adom/{adom}/obj/firewall/address6'.format(adom=paramgram["adom"]) - - ######################### - # IF type = 'ip' - ######################### - if datagram["type"] == "ip": - datagram["type"] = "ipprefix" - datagram["ip6"] = paramgram["ipv6addr"] - - ######################### - # IF type = 'iprange' - ######################### - if datagram["type"] == "iprange": - datagram["start-ip"] = paramgram["start-ip"] - datagram["end-ip"] = paramgram["end-ip"] - - ######################### - # IF type = 'group' - ######################### - if datagram["type"] == "group": - datagram = None - datagram = { - "comment": paramgram["comment"], - "name": paramgram["group_name"], - "color": paramgram["color"], - "visibility": paramgram["visibility"] - } - - members = [] - group_members = paramgram["group_members"].replace(" ", "") - try: - for member in group_members.split(","): - members.append(member) - except Exception: - pass - - datagram["member"] = members - - # EVAL THE MODE PARAMETER FOR DELETE - if paramgram["mode"] == "delete": - # IF A GROUP, SET THE CORRECT NAME AND URL FOR THE GROUP ENDPOINT - if paramgram["ipv6"] == "group": - datagram = {} - url = '/pm/config/adom/{adom}/obj/firewall/addrgrp6/{name}'.format(adom=paramgram["adom"], - name=paramgram["group_name"]) - # OTHERWISE WE'RE JUST GOING TO USE THE ADDRESS ENDPOINT - else: - datagram = {} - url = '/pm/config/adom/{adom}/obj/firewall/address6/{name}'.format(adom=paramgram["adom"], - name=paramgram["name"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def fmgr_fwobj_multicast(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # EVAL THE MODE PARAMETER FOR SET OR ADD - if paramgram["mode"] in ['set', 'add']: - # CREATE THE DATAGRAM DICTIONARY - # ENSURE THE DATAGRAM KEYS MATCH THE JSON API GUIDE ATTRIBUTES, NOT WHAT IS IN ANSIBLE - # SOME PARAMETERS SHOWN IN THIS DICTIONARY WE DON'T EVEN ASK THE USER FOR IN PLAYBOOKS BUT ARE REQUIRED - datagram = { - "associated-interface": paramgram["associated-interface"], - "comment": paramgram["comment"], - "name": paramgram["name"], - "color": paramgram["color"], - "type": paramgram["multicast"], - "visibility": paramgram["visibility"], - } - - # SET THE CORRECT URL - url = '/pm/config/adom/{adom}/obj/firewall/multicast-address'.format(adom=paramgram["adom"]) - - ######################### - # IF type = 'multicastrange' - ######################### - if paramgram["multicast"] == "multicastrange": - datagram["start-ip"] = paramgram["start-ip"] - datagram["end-ip"] = paramgram["end-ip"] - datagram["subnet"] = ["0.0.0.0", "0.0.0.0"] - - ######################### - # IF type = 'broadcastmask' - ######################### - if paramgram["multicast"] == "broadcastmask": - # EVAL THE IPV4ADDR INPUT AND SPLIT THE IP ADDRESS FROM THE MASK AND APPEND THEM TO THE SUBNET LIST - subnet = [] - for subnets in paramgram["ipv4addr"].split("/"): - subnet.append(subnets) - # CHECK THAT THE SECOND ENTRY IN THE SUBNET LIST (WHAT WAS TO THE RIGHT OF THE / CHARACTER) - # IS IN SUBNET MASK FORMAT AND NOT CIDR FORMAT. - # IF IT IS IN CIDR FORMAT, WE NEED TO CONVERT IT TO SUBNET BIT MASK FORMAT FOR THE JSON API - if not re.match(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', subnet[1]): - # IF THE SUBNET PARAMETER INPUT DIDN'T LOOK LIKE 255.255.255.255 TO REGEX... - # ... RUN IT THROUGH THE fmgr_cidr_to_netmask() FUNCTION - mask = fmgr._tools.cidr_to_netmask(subnet[1]) - # AND THEN UPDATE THE SUBNET LIST OBJECT - subnet[1] = mask - - # INCLUDE THE SUBNET LIST OBJECT IN THE DATAGRAM DICTIONARY TO BE SUBMITTED - datagram["subnet"] = subnet - - # EVAL THE MODE PARAMETER FOR DELETE - if paramgram["mode"] == "delete": - datagram = { - "name": paramgram["name"] - } - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/firewall/multicast-address/{name}'.format(adom=paramgram["adom"], - name=paramgram["name"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - mode=dict(choices=["add", "set", "delete"], type="str", default="add"), - - allow_routing=dict(required=False, type="str", choices=['enable', 'disable'], default="disable"), - associated_interface=dict(required=False, type="str"), - cache_ttl=dict(required=False, type="str"), - color=dict(required=False, type="str", default=22), - comment=dict(required=False, type="str"), - country=dict(required=False, type="str"), - fqdn=dict(required=False, type="str"), - name=dict(required=False, type="str"), - start_ip=dict(required=False, type="str"), - end_ip=dict(required=False, type="str"), - ipv4=dict(required=False, type="str", choices=['ipmask', 'iprange', 'fqdn', 'wildcard', - 'geography', 'wildcard-fqdn', 'group']), - visibility=dict(required=False, type="str", choices=['enable', 'disable'], default="enable"), - wildcard=dict(required=False, type="str"), - wildcard_fqdn=dict(required=False, type="str"), - ipv6=dict(required=False, type="str", choices=['ip', 'iprange', 'group']), - group_members=dict(required=False, type="str"), - group_name=dict(required=False, type="str"), - ipv4addr=dict(required=False, type="str"), - ipv6addr=dict(required=False, type="str"), - multicast=dict(required=False, type="str", choices=['multicastrange', 'broadcastmask', 'ip6']), - obj_id=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - mutually_exclusive=[ - ['ipv4', 'ipv6'], - ['ipv4', 'multicast'], - ['ipv6', 'multicast'] - ]) - paramgram = { - "adom": module.params["adom"], - "allow-routing": module.params["allow_routing"], - "associated-interface": module.params["associated_interface"], - "cache-ttl": module.params["cache_ttl"], - "color": module.params["color"], - "comment": module.params["comment"], - "country": module.params["country"], - "end-ip": module.params["end_ip"], - "fqdn": module.params["fqdn"], - "name": module.params["name"], - "start-ip": module.params["start_ip"], - "visibility": module.params["visibility"], - "wildcard": module.params["wildcard"], - "wildcard-fqdn": module.params["wildcard_fqdn"], - "ipv6": module.params["ipv6"], - "ipv4": module.params["ipv4"], - "group_members": module.params["group_members"], - "group_name": module.params["group_name"], - "ipv4addr": module.params["ipv4addr"], - "ipv6addr": module.params["ipv6addr"], - "multicast": module.params["multicast"], - "mode": module.params["mode"], - "obj-id": module.params["obj_id"], - } - - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr._tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - results = DEFAULT_RESULT_OBJ - try: - if paramgram["ipv4"]: - results = fmgr_fwobj_ipv4(fmgr, paramgram) - - elif paramgram["ipv6"]: - results = fmgr_fwobj_ipv6(fmgr, paramgram) - - elif paramgram["multicast"]: - results = fmgr_fwobj_multicast(fmgr, paramgram) - - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - if results is not None: - return module.exit_json(**results[1]) - else: - return module.exit_json(msg="Couldn't find a proper ipv4 or ipv6 or multicast parameter " - "to run in the logic tree. Exiting...") - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_fwobj_ippool.py b/plugins/modules/network/fortimanager/fmgr_fwobj_ippool.py deleted file mode 100644 index d0c3e11faf..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_fwobj_ippool.py +++ /dev/null @@ -1,446 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_fwobj_ippool -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Allows the editing of IP Pool Objects within FortiManager. -description: - - Allows users to add/edit/delete IP Pool Objects. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - type: - description: - - IP pool type (overload, one-to-one, fixed port range, or port block allocation). - - choice | overload | IP addresses in the IP pool can be shared by clients. - - choice | one-to-one | One to one mapping. - - choice | fixed-port-range | Fixed port range. - - choice | port-block-allocation | Port block allocation. - required: false - choices: ["overload", "one-to-one", "fixed-port-range", "port-block-allocation"] - - startip: - description: - - First IPv4 address (inclusive) in the range for the address pool (format xxx.xxx.xxx.xxx, Default| 0.0.0.0). - required: false - - source_startip: - description: - - First IPv4 address (inclusive) in the range of the source addresses to be translated (format xxx.xxx.xxx.xxx, - Default| 0.0.0.0). - required: false - - source_endip: - description: - - Final IPv4 address (inclusive) in the range of the source addresses to be translated (format xxx.xxx.xxx.xxx, - Default| 0.0.0.0). - required: false - - permit_any_host: - description: - - Enable/disable full cone NAT. - - choice | disable | Disable full cone NAT. - - choice | enable | Enable full cone NAT. - required: false - choices: ["disable", "enable"] - - pba_timeout: - description: - - Port block allocation timeout (seconds). - required: false - - num_blocks_per_user: - description: - - Number of addresses blocks that can be used by a user (1 to 128, default = 8). - required: false - - name: - description: - - IP pool name. - required: false - - endip: - description: - - Final IPv4 address (inclusive) in the range for the address pool (format xxx.xxx.xxx.xxx, Default| 0.0.0.0). - required: false - - comments: - description: - - Comment. - required: false - - block_size: - description: - - Number of addresses in a block (64 to 4096, default = 128). - required: false - - associated_interface: - description: - - Associated interface name. - required: false - - arp_reply: - description: - - Enable/disable replying to ARP requests when an IP Pool is added to a policy (default = enable). - - choice | disable | Disable ARP reply. - - choice | enable | Enable ARP reply. - required: false - choices: ["disable", "enable"] - - arp_intf: - description: - - Select an interface from available options that will reply to ARP requests. (If blank, any is selected). - required: false - - dynamic_mapping: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameter.ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - dynamic_mapping_arp_intf: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_arp_reply: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - choices: ["disable", "enable"] - - dynamic_mapping_associated_interface: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_block_size: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_comments: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_endip: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_num_blocks_per_user: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_pba_timeout: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_permit_any_host: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - choices: ["disable", "enable"] - - dynamic_mapping_source_endip: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_source_startip: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_startip: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_type: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - choices: ["overload", "one-to-one", "fixed-port-range", "port-block-allocation"] - - -''' - -EXAMPLES = ''' -- name: ADD FMGR_FIREWALL_IPPOOL Overload - fmgr_fwobj_ippool: - mode: "add" - adom: "ansible" - name: "Ansible_pool4_overload" - comments: "Created by ansible" - type: "overload" - - # OPTIONS FOR ALL MODES - startip: "10.10.10.10" - endip: "10.10.10.100" - arp_reply: "enable" - -- name: ADD FMGR_FIREWALL_IPPOOL one-to-one - fmgr_fwobj_ippool: - mode: "add" - adom: "ansible" - name: "Ansible_pool4_121" - comments: "Created by ansible" - type: "one-to-one" - - # OPTIONS FOR ALL MODES - startip: "10.10.20.10" - endip: "10.10.20.100" - arp_reply: "enable" - -- name: ADD FMGR_FIREWALL_IPPOOL FIXED PORT RANGE - fmgr_fwobj_ippool: - mode: "add" - adom: "ansible" - name: "Ansible_pool4_fixed_port" - comments: "Created by ansible" - type: "fixed-port-range" - - # OPTIONS FOR ALL MODES - startip: "10.10.40.10" - endip: "10.10.40.100" - arp_reply: "enable" - # FIXED PORT RANGE OPTIONS - source_startip: "192.168.20.1" - source_endip: "192.168.20.20" - -- name: ADD FMGR_FIREWALL_IPPOOL PORT BLOCK ALLOCATION - fmgr_fwobj_ippool: - mode: "add" - adom: "ansible" - name: "Ansible_pool4_port_block_allocation" - comments: "Created by ansible" - type: "port-block-allocation" - - # OPTIONS FOR ALL MODES - startip: "10.10.30.10" - endip: "10.10.30.100" - arp_reply: "enable" - # PORT BLOCK ALLOCATION OPTIONS - block_size: "128" - num_blocks_per_user: "1" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -############### -# START METHODS -############### - - -def fmgr_fwobj_ippool_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/firewall/ippool'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/firewall/ippool/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - type=dict(required=False, type="str", choices=["overload", - "one-to-one", - "fixed-port-range", - "port-block-allocation"]), - startip=dict(required=False, type="str"), - source_startip=dict(required=False, type="str"), - source_endip=dict(required=False, type="str"), - permit_any_host=dict(required=False, type="str", choices=["disable", "enable"]), - pba_timeout=dict(required=False, type="int"), - num_blocks_per_user=dict(required=False, type="int"), - name=dict(required=False, type="str"), - endip=dict(required=False, type="str"), - comments=dict(required=False, type="str"), - block_size=dict(required=False, type="int"), - associated_interface=dict(required=False, type="str"), - arp_reply=dict(required=False, type="str", choices=["disable", "enable"]), - arp_intf=dict(required=False, type="str"), - dynamic_mapping=dict(required=False, type="list"), - dynamic_mapping_arp_intf=dict(required=False, type="str"), - dynamic_mapping_arp_reply=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_associated_interface=dict(required=False, type="str"), - dynamic_mapping_block_size=dict(required=False, type="int"), - dynamic_mapping_comments=dict(required=False, type="str"), - dynamic_mapping_endip=dict(required=False, type="str"), - dynamic_mapping_num_blocks_per_user=dict(required=False, type="int"), - dynamic_mapping_pba_timeout=dict(required=False, type="int"), - dynamic_mapping_permit_any_host=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_source_endip=dict(required=False, type="str"), - dynamic_mapping_source_startip=dict(required=False, type="str"), - dynamic_mapping_startip=dict(required=False, type="str"), - dynamic_mapping_type=dict(required=False, type="str", choices=["overload", - "one-to-one", - "fixed-port-range", - "port-block-allocation"]), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "type": module.params["type"], - "startip": module.params["startip"], - "source-startip": module.params["source_startip"], - "source-endip": module.params["source_endip"], - "permit-any-host": module.params["permit_any_host"], - "pba-timeout": module.params["pba_timeout"], - "num-blocks-per-user": module.params["num_blocks_per_user"], - "name": module.params["name"], - "endip": module.params["endip"], - "comments": module.params["comments"], - "block-size": module.params["block_size"], - "associated-interface": module.params["associated_interface"], - "arp-reply": module.params["arp_reply"], - "arp-intf": module.params["arp_intf"], - "dynamic_mapping": { - "arp-intf": module.params["dynamic_mapping_arp_intf"], - "arp-reply": module.params["dynamic_mapping_arp_reply"], - "associated-interface": module.params["dynamic_mapping_associated_interface"], - "block-size": module.params["dynamic_mapping_block_size"], - "comments": module.params["dynamic_mapping_comments"], - "endip": module.params["dynamic_mapping_endip"], - "num-blocks-per-user": module.params["dynamic_mapping_num_blocks_per_user"], - "pba-timeout": module.params["dynamic_mapping_pba_timeout"], - "permit-any-host": module.params["dynamic_mapping_permit_any_host"], - "source-endip": module.params["dynamic_mapping_source_endip"], - "source-startip": module.params["dynamic_mapping_source_startip"], - "startip": module.params["dynamic_mapping_startip"], - "type": module.params["dynamic_mapping_type"], - } - } - - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['dynamic_mapping'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - # UPDATE THE CHANGED PARAMGRAM - module.paramgram = paramgram - - results = DEFAULT_RESULT_OBJ - try: - results = fmgr_fwobj_ippool_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_fwobj_ippool6.py b/plugins/modules/network/fortimanager/fmgr_fwobj_ippool6.py deleted file mode 100644 index 45d1f471a2..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_fwobj_ippool6.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_fwobj_ippool6 -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Allows the editing of IP Pool Objects within FortiManager. -description: - - Allows users to add/edit/delete IPv6 Pool Objects. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - startip: - description: - - First IPv6 address (inclusive) in the range for the address pool. - required: false - - name: - description: - - IPv6 IP pool name. - required: false - - endip: - description: - - Final IPv6 address (inclusive) in the range for the address pool. - required: false - - comments: - description: - - Comment. - required: false - - dynamic_mapping: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - dynamic_mapping_comments: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_endip: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - dynamic_mapping_startip: - description: - - Dynamic Mapping clone of original suffixed parameter. - required: false - - -''' - -EXAMPLES = ''' -- name: ADD FMGR_FIREWALL_IPPOOL6 - fmgr_firewall_ippool6: - mode: "add" - adom: "ansible" - startip: - name: "IPv6 IPPool" - endip: - comments: "Created by Ansible" - -- name: DELETE FMGR_FIREWALL_IPPOOL6 - fmgr_firewall_ippool6: - mode: "delete" - adom: "ansible" - name: "IPv6 IPPool" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -def fmgr_fwobj_ippool6_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/firewall/ippool6'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/firewall/ippool6/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - startip=dict(required=False, type="str"), - name=dict(required=False, type="str"), - endip=dict(required=False, type="str"), - comments=dict(required=False, type="str"), - dynamic_mapping=dict(required=False, type="list"), - dynamic_mapping_comments=dict(required=False, type="str"), - dynamic_mapping_endip=dict(required=False, type="str"), - dynamic_mapping_startip=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "startip": module.params["startip"], - "name": module.params["name"], - "endip": module.params["endip"], - "comments": module.params["comments"], - "dynamic_mapping": { - "comments": module.params["dynamic_mapping_comments"], - "endip": module.params["dynamic_mapping_endip"], - "startip": module.params["dynamic_mapping_startip"], - } - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['dynamic_mapping'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - - try: - results = fmgr_fwobj_ippool6_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_fwobj_service.py b/plugins/modules/network/fortimanager/fmgr_fwobj_service.py deleted file mode 100644 index 7ee1e62f8c..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_fwobj_service.py +++ /dev/null @@ -1,623 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_fwobj_service -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manages FortiManager Firewall Service Objects. -description: - - Manages FortiManager Firewall Service Objects. - -options: - adom: - description: - -The ADOM the configuration should belong to. - required: false - default: root - - app_category: - description: - - Application category ID. - required: false - - app_service_type: - description: - - Application service type. - required: false - - application: - description: - - Application ID. - required: false - - category: - description: - - Service category. - required: false - - check_reset_range: - description: - - Enable disable RST check. - required: false - - color: - description: - - GUI icon color. - required: false - default: 22 - - comment: - description: - - Comment. - required: false - - custom_type: - description: - - Tells module what kind of custom service to be added. - choices: ['tcp_udp_sctp', 'icmp', 'icmp6', 'ip', 'http', 'ftp', 'connect', 'socks_tcp', 'socks_udp', 'all'] - default: all - required: false - - explicit_proxy: - description: - - Enable/disable explicit web proxy service. - choices: ['enable', 'disable'] - default: 'disable' - required: false - - fqdn: - description: - - Fully qualified domain name. - required: false - default: "" - - group_name: - description: - - Name of the Service Group. - required: false - - group_member: - description: - - Comma-Seperated list of members' names. - required: false - - icmp_code: - description: - - ICMP code. - required: false - - icmp_type: - description: - - ICMP type. - required: false - - iprange: - description: - - Start IP-End IP. - required: false - default: "0.0.0.0" - - name: - description: - - Custom service name. - required: false - - mode: - description: - - Sets one of three modes for managing the object. - choices: ['add', 'set', 'delete'] - default: add - required: false - - object_type: - description: - - Tells module if we are adding a custom service, category, or group. - choices: ['custom', 'group', 'category'] - required: false - - protocol: - description: - - Protocol type. - required: false - - protocol_number: - description: - - IP protocol number. - required: false - - sctp_portrange: - description: - - Multiple SCTP port ranges. Comma separated list of destination ports to add (i.e. '443,80'). - - Syntax is - - If no sourcePort is defined, it assumes all of them. - - Ranges can be defined with a hyphen - - - Examples -- '443' (destPort 443 only) '443:1000-2000' (destPort 443 from source ports 1000-2000). - - String multiple together in same quotes, comma separated. ('443:1000-2000, 80:1000-2000'). - required: false - - session_ttl: - description: - - Session TTL (300 - 604800, 0 = default). - required: false - default: 0 - - tcp_halfclose_timer: - description: - - TCP half close timeout (1 - 86400 sec, 0 = default). - required: false - default: 0 - - tcp_halfopen_timer: - description: - - TCP half close timeout (1 - 86400 sec, 0 = default). - required: false - default: 0 - - tcp_portrange: - description: - - Comma separated list of destination ports to add (i.e. '443,80'). - - Syntax is - - If no sourcePort is defined, it assumes all of them. - - Ranges can be defined with a hyphen - - - Examples -- '443' (destPort 443 only) '443:1000-2000' (destPort 443 from source ports 1000-2000). - - String multiple together in same quotes, comma separated. ('443:1000-2000, 80:1000-2000'). - required: false - - tcp_timewait_timer: - description: - - TCP half close timeout (1 - 300 sec, 0 = default). - required: false - default: 0 - - udp_idle_timer: - description: - - TCP half close timeout (0 - 86400 sec, 0 = default). - required: false - default: 0 - - udp_portrange: - description: - - Comma separated list of destination ports to add (i.e. '443,80'). - - Syntax is - - If no sourcePort is defined, it assumes all of them. - - Ranges can be defined with a hyphen - - - Examples -- '443' (destPort 443 only) '443:1000-2000' (destPort 443 from source ports 1000-2000). - - String multiple together in same quotes, comma separated. ('443:1000-2000, 80:1000-2000'). - required: false - - visibility: - description: - - Enable/disable service visibility. - required: false - choices: ["enable", "disable"] - default: "enable" - -''' - -EXAMPLES = ''' -- name: ADD A CUSTOM SERVICE FOR TCP/UDP/SCP - fmgr_fwobj_service: - adom: "ansible" - name: "ansible_custom_service" - object_type: "custom" - custom_type: "tcp_udp_sctp" - tcp_portrange: "443" - udp_portrange: "51" - sctp_portrange: "100" - -- name: ADD A CUSTOM SERVICE FOR TCP/UDP/SCP WITH SOURCE RANGES AND MULTIPLES - fmgr_fwobj_service: - adom: "ansible" - name: "ansible_custom_serviceWithSource" - object_type: "custom" - custom_type: "tcp_udp_sctp" - tcp_portrange: "443:2000-1000,80-82:10000-20000" - udp_portrange: "51:100-200,162:200-400" - sctp_portrange: "100:2000-2500" - -- name: ADD A CUSTOM SERVICE FOR ICMP - fmgr_fwobj_service: - adom: "ansible" - name: "ansible_custom_icmp" - object_type: "custom" - custom_type: "icmp" - icmp_type: "8" - icmp_code: "3" - -- name: ADD A CUSTOM SERVICE FOR ICMP6 - fmgr_fwobj_service: - adom: "ansible" - name: "ansible_custom_icmp6" - object_type: "custom" - custom_type: "icmp6" - icmp_type: "5" - icmp_code: "1" - -- name: ADD A CUSTOM SERVICE FOR IP - GRE - fmgr_fwobj_service: - adom: "ansible" - name: "ansible_custom_icmp6" - object_type: "custom" - custom_type: "ip" - protocol_number: "47" - -- name: ADD A CUSTOM PROXY FOR ALL WITH SOURCE RANGES AND MULTIPLES - fmgr_fwobj_service: - adom: "ansible" - name: "ansible_custom_proxy_all" - object_type: "custom" - custom_type: "all" - explicit_proxy: "enable" - tcp_portrange: "443:2000-1000,80-82:10000-20000" - iprange: "www.ansible.com" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -def fmgr_fwobj_service_custom(fmgr, paramgram): - """ - description: - - the tcp and udp-portrange parameters are in a list when there are multiple. they are not in a list when they - singular or by themselves (only 1 was listed) - - the syntax for this is (destPort:sourcePort). Ranges are (xxxx-xxxx) i.e. 443:443, or 443:1000-2000. - - if you leave out the second field after the colon (source port) it assumes any source port (which is usual) - - multiples would look like ['443:1000-2000','80'] - - a single would look simple like "443:1000-2000" without the list around it ( a string!) - - the protocol parameter is the protocol NUMBER, not the string of it. - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - response = DEFAULT_RESULT_OBJ - if paramgram["mode"] in ['set', 'add']: - # SET THE URL FOR ADD / SET - url = '/pm/config/adom/{adom}/obj/firewall/service/custom'.format(adom=paramgram["adom"]) - # BUILD THE DEFAULT DATAGRAM - datagram = { - # ADVANCED OPTIONS - "app-category": paramgram["app-category"], - "app-service-type": paramgram["app-service-type"], - "application": paramgram["application"], - "category": paramgram["category"], - "check-reset-range": paramgram["check-reset-range"], - "color": paramgram["color"], - "session-ttl": paramgram["session-ttl"], - "tcp-halfclose-timer": paramgram["tcp-halfclose-timer"], - "tcp-halfopen-timer": paramgram["tcp-halfopen-timer"], - "tcp-timewait-timer": paramgram["tcp-timewait-timer"], - "udp-idle-timer": paramgram["udp-idle-timer"], - "visibility": paramgram["visibility"], - "comment": paramgram["comment"], - "proxy": paramgram["explicit-proxy"], - "name": paramgram["name"] - } - - if datagram["proxy"] == "disable": - ####################################### - # object-type = "TCP/UDP/SCTP" - ####################################### - if paramgram["custom_type"] == "tcp_udp_sctp": - datagram["protocol"] = "TCP/UDP/SCTP" - # PROCESS PORT RANGES TO PUT INTO THE PROPER SYNTAX - if paramgram["tcp-portrange"] is not None: - tcp_list = [] - for tcp in paramgram["tcp-portrange"].split(","): - tcp = tcp.strip() - tcp_list.append(tcp) - datagram["tcp-portrange"] = tcp_list - - if paramgram["udp-portrange"] is not None: - udp_list = [] - for udp in paramgram["udp-portrange"].split(","): - udp = udp.strip() - udp_list.append(udp) - datagram["udp-portrange"] = udp_list - - if paramgram["sctp-portrange"] is not None: - sctp_list = [] - for sctp in paramgram["sctp-portrange"].split(","): - sctp = sctp.strip() - sctp_list.append(sctp) - datagram["sctp-portrange"] = sctp_list - - ####################################### - # object-type = "ICMP" - ####################################### - if paramgram["custom_type"] == "icmp": - datagram["icmpcode"] = paramgram["icmp_code"] - datagram["icmptype"] = paramgram["icmp_type"] - datagram["protocol"] = "ICMP" - - ####################################### - # object-type = "ICMP6" - ####################################### - if paramgram["custom_type"] == "icmp6": - datagram["icmpcode"] = paramgram["icmp_code"] - datagram["icmptype"] = paramgram["icmp_type"] - datagram["protocol"] = "ICMP6" - - ####################################### - # object-type = "IP" - ####################################### - if paramgram["custom_type"] == "ip": - datagram["protocol"] = "IP" - datagram["protocol-number"] = paramgram["protocol-number"] - - ####################################### - # object-type in any of the explicit proxy options - ####################################### - if datagram["proxy"] == "enable": - datagram["protocol"] = paramgram["custom_type"].upper() - datagram["iprange"] = paramgram["iprange"] - - # PROCESS PROXY TCP PORT RANGES TO PUT INTO THE PROPER SYNTAX - if paramgram["tcp-portrange"] is not None: - tcp_list = [] - for tcp in paramgram["tcp-portrange"].split(","): - tcp = tcp.strip() - tcp_list.append(tcp) - datagram["tcp-portrange"] = tcp_list - - if paramgram["mode"] == "delete": - datagram = { - "name": paramgram["name"] - } - # SET DELETE URL - url = '/pm/config/adom/{adom}/obj/firewall/service/custom' \ - '/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) - - datagram = scrub_dict(datagram) - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def fmgr_fwobj_service_group(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - response = DEFAULT_RESULT_OBJ - if paramgram["mode"] in ['set', 'add']: - url = '/pm/config/adom/{adom}/obj/firewall/service/group'.format(adom=paramgram["adom"]) - datagram = { - "name": paramgram["group-name"], - "comment": paramgram["comment"], - "proxy": paramgram["explicit-proxy"], - "color": paramgram["color"] - } - - members = paramgram["group-member"] - member = [] - for obj in members.split(","): - member.append(obj.strip()) - datagram["member"] = member - - if paramgram["mode"] == "delete": - datagram = { - "name": paramgram["name"] - } - # SET DELETE URL - url = '/pm/config/adom/{adom}/obj/firewall/service/group' \ - '/{name}'.format(adom=paramgram["adom"], name=paramgram["group-name"]) - - datagram = scrub_dict(datagram) - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def fmgr_fwobj_service_category(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - response = DEFAULT_RESULT_OBJ - if paramgram["mode"] in ['set', 'add']: - url = '/pm/config/adom/{adom}/obj/firewall/service/category'.format(adom=paramgram["adom"]) - # GET RID OF ANY WHITESPACE - category = paramgram["category"] - category = category.strip() - - datagram = { - "name": paramgram["category"], - "comment": "Created by Ansible" - } - - # IF MODE = DELETE - if paramgram["mode"] == "delete": - datagram = { - "name": paramgram["name"] - } - # SET DELETE URL - url = '/pm/config/adom/{adom}/obj/firewall/service/category' \ - '/{name}'.format(adom=paramgram["adom"], name=paramgram["category"]) - - datagram = scrub_dict(datagram) - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - mode=dict(required=False, type="str", choices=['add', 'set', 'delete'], default="add"), - app_category=dict(required=False, type="str"), - app_service_type=dict(required=False, type="str"), - application=dict(required=False, type="str"), - category=dict(required=False, type="str"), - check_reset_range=dict(required=False, type="str"), - color=dict(required=False, type="int", default=22), - comment=dict(required=False, type="str"), - custom_type=dict(required=False, type="str", choices=['tcp_udp_sctp', 'icmp', 'icmp6', 'ip', 'http', 'ftp', - 'connect', 'socks_tcp', 'socks_udp', 'all'], - default="all"), - explicit_proxy=dict(required=False, type="str", choices=['enable', 'disable'], default="disable"), - fqdn=dict(required=False, type="str", default=""), - group_name=dict(required=False, type="str"), - group_member=dict(required=False, type="str"), - icmp_code=dict(required=False, type="int"), - icmp_type=dict(required=False, type="int"), - iprange=dict(required=False, type="str", default="0.0.0.0"), - name=dict(required=False, type="str"), - protocol=dict(required=False, type="str"), - protocol_number=dict(required=False, type="int"), - sctp_portrange=dict(required=False, type="str"), - session_ttl=dict(required=False, type="int", default=0), - object_type=dict(required=False, type="str", choices=['custom', 'group', 'category']), - tcp_halfclose_timer=dict(required=False, type="int", default=0), - tcp_halfopen_timer=dict(required=False, type="int", default=0), - tcp_portrange=dict(required=False, type="str"), - tcp_timewait_timer=dict(required=False, type="int", default=0), - udp_idle_timer=dict(required=False, type="int", default=0), - udp_portrange=dict(required=False, type="str"), - visibility=dict(required=False, type="str", default="enable", choices=["enable", "disable"]), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE DATAGRAM - paramgram = { - "adom": module.params["adom"], - "app-category": module.params["app_category"], - "app-service-type": module.params["app_service_type"], - "application": module.params["application"], - "category": module.params["category"], - "check-reset-range": module.params["check_reset_range"], - "color": module.params["color"], - "comment": module.params["comment"], - "custom_type": module.params["custom_type"], - "explicit-proxy": module.params["explicit_proxy"], - "fqdn": module.params["fqdn"], - "group-name": module.params["group_name"], - "group-member": module.params["group_member"], - "icmp_code": module.params["icmp_code"], - "icmp_type": module.params["icmp_type"], - "iprange": module.params["iprange"], - "name": module.params["name"], - "mode": module.params["mode"], - "protocol": module.params["protocol"], - "protocol-number": module.params["protocol_number"], - "sctp-portrange": module.params["sctp_portrange"], - "object_type": module.params["object_type"], - "session-ttl": module.params["session_ttl"], - "tcp-halfclose-timer": module.params["tcp_halfclose_timer"], - "tcp-halfopen-timer": module.params["tcp_halfopen_timer"], - "tcp-portrange": module.params["tcp_portrange"], - "tcp-timewait-timer": module.params["tcp_timewait_timer"], - "udp-idle-timer": module.params["udp_idle_timer"], - "udp-portrange": module.params["udp_portrange"], - "visibility": module.params["visibility"], - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - results = DEFAULT_RESULT_OBJ - - try: - # CHECK FOR CATEGORIES TO ADD - # THIS IS ONLY WHEN OBJECT_TYPE ISN'T SPECIFICALLY ADDING A CATEGORY! - # WE NEED TO ADD THE CATEGORY BEFORE ADDING THE OBJECT - # IF ANY category ARE DEFINED AND MODE IS ADD OR SET LETS ADD THOSE - # THIS IS A "BLIND ADD" AND THE EXIT CODE FOR OBJECT ALREADY EXISTS IS TREATED AS A PASS - if paramgram["category"] is not None and paramgram["mode"] in ['add', 'set'] \ - and paramgram["object_type"] != "category": - category_add = fmgr_fwobj_service_category(fmgr, paramgram) - fmgr.govern_response(module=module, results=category_add, - ansible_facts=fmgr.construct_ansible_facts(category_add, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # IF OBJECT_TYPE IS CATEGORY... - if paramgram["object_type"] == 'category': - results = fmgr_fwobj_service_category(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -2, -3], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # IF OBJECT_TYPE IS CUSTOM... - if paramgram["object_type"] == 'custom': - results = fmgr_fwobj_service_custom(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -2, -3], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # IF OBJECT_TYPE IS GROUP... - if paramgram["object_type"] == 'group': - results = fmgr_fwobj_service_group(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, good_codes=[0, -2, -3], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_fwobj_vip.py b/plugins/modules/network/fortimanager/fmgr_fwobj_vip.py deleted file mode 100644 index a03b4e0b0f..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_fwobj_vip.py +++ /dev/null @@ -1,2428 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_fwobj_vip -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manages Virtual IPs objects in FortiManager -description: - - Manages Virtual IP objects in FortiManager for IPv4 - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - websphere_server: - description: - - Enable to add an HTTP header to indicate SSL offloading for a WebSphere server. - - choice | disable | Do not add HTTP header indicating SSL offload for WebSphere server. - - choice | enable | Add HTTP header indicating SSL offload for WebSphere server. - required: false - choices: ["disable", "enable"] - - weblogic_server: - description: - - Enable to add an HTTP header to indicate SSL offloading for a WebLogic server. - - choice | disable | Do not add HTTP header indicating SSL offload for WebLogic server. - - choice | enable | Add HTTP header indicating SSL offload for WebLogic server. - required: false - choices: ["disable", "enable"] - - type: - description: - - Configure a static NAT, load balance, server load balance, DNS translation, or FQDN VIP. - - choice | static-nat | Static NAT. - - choice | load-balance | Load balance. - - choice | server-load-balance | Server load balance. - - choice | dns-translation | DNS translation. - - choice | fqdn | FQDN Translation - required: false - choices: ["static-nat", "load-balance", "server-load-balance", "dns-translation", "fqdn"] - - ssl_server_session_state_type: - description: - - How to expire SSL sessions for the segment of the SSL connection between the server and the FortiGate. - - choice | disable | Do not keep session states. - - choice | time | Expire session states after this many minutes. - - choice | count | Expire session states when this maximum is reached. - - choice | both | Expire session states based on time or count, whichever occurs first. - required: false - choices: ["disable", "time", "count", "both"] - - ssl_server_session_state_timeout: - description: - - Number of minutes to keep FortiGate to Server SSL session state. - required: false - - ssl_server_session_state_max: - description: - - Maximum number of FortiGate to Server SSL session states to keep. - required: false - - ssl_server_min_version: - description: - - Lowest SSL/TLS version acceptable from a server. Use the client setting by default. - - choice | ssl-3.0 | SSL 3.0. - - choice | tls-1.0 | TLS 1.0. - - choice | tls-1.1 | TLS 1.1. - - choice | tls-1.2 | TLS 1.2. - - choice | client | Use same value as client configuration. - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"] - - ssl_server_max_version: - description: - - Highest SSL/TLS version acceptable from a server. Use the client setting by default. - - choice | ssl-3.0 | SSL 3.0. - - choice | tls-1.0 | TLS 1.0. - - choice | tls-1.1 | TLS 1.1. - - choice | tls-1.2 | TLS 1.2. - - choice | client | Use same value as client configuration. - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"] - - ssl_server_algorithm: - description: - - Permitted encryption algorithms for the server side of SSL full mode sessions according to encryption strength - - choice | high | High encryption. Allow only AES and ChaCha. - - choice | low | Low encryption. Allow AES, ChaCha, 3DES, RC4, and DES. - - choice | medium | Medium encryption. Allow AES, ChaCha, 3DES, and RC4. - - choice | custom | Custom encryption. Use ssl-server-cipher-suites to select the cipher suites that are allowed. - - choice | client | Use the same encryption algorithms for both client and server sessions. - required: false - choices: ["high", "low", "medium", "custom", "client"] - - ssl_send_empty_frags: - description: - - Enable/disable sending empty fragments to avoid CBC IV attacks (SSL 3.0 & TLS 1.0 only). - - choice | disable | Do not send empty fragments. - - choice | enable | Send empty fragments. - required: false - choices: ["disable", "enable"] - - ssl_pfs: - description: - - Select the cipher suites that can be used for SSL perfect forward secrecy (PFS). - - choice | require | Allow only Diffie-Hellman cipher-suites, so PFS is applied. - - choice | deny | Allow only non-Diffie-Hellman cipher-suites, so PFS is not applied. - - choice | allow | Allow use of any cipher suite so PFS may or may not be used depending on the cipher suite - required: false - choices: ["require", "deny", "allow"] - - ssl_mode: - description: - - Apply SSL offloading mode - - choice | half | Client to FortiGate SSL. - - choice | full | Client to FortiGate and FortiGate to Server SSL. - required: false - choices: ["half", "full"] - - ssl_min_version: - description: - - Lowest SSL/TLS version acceptable from a client. - - choice | ssl-3.0 | SSL 3.0. - - choice | tls-1.0 | TLS 1.0. - - choice | tls-1.1 | TLS 1.1. - - choice | tls-1.2 | TLS 1.2. - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - ssl_max_version: - description: - - Highest SSL/TLS version acceptable from a client. - - choice | ssl-3.0 | SSL 3.0. - - choice | tls-1.0 | TLS 1.0. - - choice | tls-1.1 | TLS 1.1. - - choice | tls-1.2 | TLS 1.2. - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - ssl_http_match_host: - description: - - Enable/disable HTTP host matching for location conversion. - - choice | disable | Do not match HTTP host. - - choice | enable | Match HTTP host in response header. - required: false - choices: ["disable", "enable"] - - ssl_http_location_conversion: - description: - - Enable to replace HTTP with HTTPS in the reply's Location HTTP header field. - - choice | disable | Disable HTTP location conversion. - - choice | enable | Enable HTTP location conversion. - required: false - choices: ["disable", "enable"] - - ssl_hsts_include_subdomains: - description: - - Indicate that HSTS header applies to all subdomains. - - choice | disable | HSTS header does not apply to subdomains. - - choice | enable | HSTS header applies to subdomains. - required: false - choices: ["disable", "enable"] - - ssl_hsts_age: - description: - - Number of seconds the client should honour the HSTS setting. - required: false - - ssl_hsts: - description: - - Enable/disable including HSTS header in response. - - choice | disable | Do not add a HSTS header to each a HTTP response. - - choice | enable | Add a HSTS header to each HTTP response. - required: false - choices: ["disable", "enable"] - - ssl_hpkp_report_uri: - description: - - URL to report HPKP violations to. - required: false - - ssl_hpkp_primary: - description: - - Certificate to generate primary HPKP pin from. - required: false - - ssl_hpkp_include_subdomains: - description: - - Indicate that HPKP header applies to all subdomains. - - choice | disable | HPKP header does not apply to subdomains. - - choice | enable | HPKP header applies to subdomains. - required: false - choices: ["disable", "enable"] - - ssl_hpkp_backup: - description: - - Certificate to generate backup HPKP pin from. - required: false - - ssl_hpkp_age: - description: - - Number of seconds the client should honour the HPKP setting. - required: false - - ssl_hpkp: - description: - - Enable/disable including HPKP header in response. - - choice | disable | Do not add a HPKP header to each HTTP response. - - choice | enable | Add a HPKP header to each a HTTP response. - - choice | report-only | Add a HPKP Report-Only header to each HTTP response. - required: false - choices: ["disable", "enable", "report-only"] - - ssl_dh_bits: - description: - - Number of bits to use in the Diffie-Hellman exchange for RSA encryption of SSL sessions. - - choice | 768 | 768-bit Diffie-Hellman prime. - - choice | 1024 | 1024-bit Diffie-Hellman prime. - - choice | 1536 | 1536-bit Diffie-Hellman prime. - - choice | 2048 | 2048-bit Diffie-Hellman prime. - - choice | 3072 | 3072-bit Diffie-Hellman prime. - - choice | 4096 | 4096-bit Diffie-Hellman prime. - required: false - choices: ["768", "1024", "1536", "2048", "3072", "4096"] - - ssl_client_session_state_type: - description: - - How to expire SSL sessions for the segment of the SSL connection between the client and the FortiGate. - - choice | disable | Do not keep session states. - - choice | time | Expire session states after this many minutes. - - choice | count | Expire session states when this maximum is reached. - - choice | both | Expire session states based on time or count, whichever occurs first. - required: false - choices: ["disable", "time", "count", "both"] - - ssl_client_session_state_timeout: - description: - - Number of minutes to keep client to FortiGate SSL session state. - required: false - - ssl_client_session_state_max: - description: - - Maximum number of client to FortiGate SSL session states to keep. - required: false - - ssl_client_renegotiation: - description: - - Allow, deny, or require secure renegotiation of client sessions to comply with RFC 5746. - - choice | deny | Abort any client initiated SSL re-negotiation attempt. - - choice | allow | Allow a SSL client to renegotiate. - - choice | secure | Abort any client initiated SSL re-negotiation attempt that does not use RFC 5746. - required: false - choices: ["deny", "allow", "secure"] - - ssl_client_fallback: - description: - - Enable/disable support for preventing Downgrade Attacks on client connections (RFC 7507). - - choice | disable | Disable. - - choice | enable | Enable. - required: false - choices: ["disable", "enable"] - - ssl_certificate: - description: - - The name of the SSL certificate to use for SSL acceleration. - required: false - - ssl_algorithm: - description: - - Permitted encryption algorithms for SSL sessions according to encryption strength. - - choice | high | High encryption. Allow only AES and ChaCha. - - choice | medium | Medium encryption. Allow AES, ChaCha, 3DES, and RC4. - - choice | low | Low encryption. Allow AES, ChaCha, 3DES, RC4, and DES. - - choice | custom | Custom encryption. Use config ssl-cipher-suites to select the cipher suites that are allowed. - required: false - choices: ["high", "medium", "low", "custom"] - - srcintf_filter: - description: - - Interfaces to which the VIP applies. Separate the names with spaces. - required: false - - src_filter: - description: - - Source address filter. Each address must be either an IP/subnet (x.x.x.x/n) or a range (x.x.x.x-y.y.y.y). - - Separate addresses with spaces. - required: false - - service: - description: - - Service name. - required: false - - server_type: - description: - - Protocol to be load balanced by the virtual server (also called the server load balance virtual IP). - - choice | http | HTTP - - choice | https | HTTPS - - choice | ssl | SSL - - choice | tcp | TCP - - choice | udp | UDP - - choice | ip | IP - - choice | imaps | IMAPS - - choice | pop3s | POP3S - - choice | smtps | SMTPS - required: false - choices: ["http", "https", "ssl", "tcp", "udp", "ip", "imaps", "pop3s", "smtps"] - - protocol: - description: - - Protocol to use when forwarding packets. - - choice | tcp | TCP. - - choice | udp | UDP. - - choice | sctp | SCTP. - - choice | icmp | ICMP. - required: false - choices: ["tcp", "udp", "sctp", "icmp"] - - portmapping_type: - description: - - Port mapping type. - - choice | 1-to-1 | One to one. - - choice | m-to-n | Many to many. - required: false - choices: ["1-to-1", "m-to-n"] - - portforward: - description: - - Enable/disable port forwarding. - - choice | disable | Disable port forward. - - choice | enable | Enable port forward. - required: false - choices: ["disable", "enable"] - - persistence: - description: - - Configure how to make sure that clients connect to the same server every time they make a request that is part - - of the same session. - - choice | none | None. - - choice | http-cookie | HTTP cookie. - - choice | ssl-session-id | SSL session ID. - required: false - choices: ["none", "http-cookie", "ssl-session-id"] - - outlook_web_access: - description: - - Enable to add the Front-End-Https header for Microsoft Outlook Web Access. - - choice | disable | Disable Outlook Web Access support. - - choice | enable | Enable Outlook Web Access support. - required: false - choices: ["disable", "enable"] - - nat_source_vip: - description: - - Enable to prevent unintended servers from using a virtual IP. - - Disable to use the actual IP address of the server as the source address. - - choice | disable | Do not force to NAT as VIP. - - choice | enable | Force to NAT as VIP. - required: false - choices: ["disable", "enable"] - - name: - description: - - Virtual IP name. - required: false - - monitor: - description: - - Name of the health check monitor to use when polling to determine a virtual server's connectivity status. - required: false - - max_embryonic_connections: - description: - - Maximum number of incomplete connections. - required: false - - mappedport: - description: - - Port number range on the destination network to which the external port number range is mapped. - required: false - - mappedip: - description: - - IP address or address range on the destination network to which the external IP address is mapped. - required: false - - mapped_addr: - description: - - Mapped FQDN address name. - required: false - - ldb_method: - description: - - Method used to distribute sessions to real servers. - - choice | static | Distribute to server based on source IP. - - choice | round-robin | Distribute to server based round robin order. - - choice | weighted | Distribute to server based on weight. - - choice | least-session | Distribute to server with lowest session count. - - choice | least-rtt | Distribute to server with lowest Round-Trip-Time. - - choice | first-alive | Distribute to the first server that is alive. - - choice | http-host | Distribute to server based on host field in HTTP header. - required: false - choices: ["static", "round-robin", "weighted", "least-session", "least-rtt", "first-alive", "http-host"] - - https_cookie_secure: - description: - - Enable/disable verification that inserted HTTPS cookies are secure. - - choice | disable | Do not mark cookie as secure, allow sharing between an HTTP and HTTPS connection. - - choice | enable | Mark inserted cookie as secure, cookie can only be used for HTTPS a connection. - required: false - choices: ["disable", "enable"] - - http_multiplex: - description: - - Enable/disable HTTP multiplexing. - - choice | disable | Disable HTTP session multiplexing. - - choice | enable | Enable HTTP session multiplexing. - required: false - choices: ["disable", "enable"] - - http_ip_header_name: - description: - - For HTTP multiplexing, enter a custom HTTPS header name. The orig client IP address is added to this header. - - If empty, X-Forwarded-For is used. - required: false - - http_ip_header: - description: - - For HTTP multiplexing, enable to add the original client IP address in the XForwarded-For HTTP header. - - choice | disable | Disable adding HTTP header. - - choice | enable | Enable adding HTTP header. - required: false - choices: ["disable", "enable"] - - http_cookie_share: - description: - - Control sharing of cookies across virtual servers. same-ip means a cookie from one virtual server can be used - - by another. Disable stops cookie sharing. - - choice | disable | Only allow HTTP cookie to match this virtual server. - - choice | same-ip | Allow HTTP cookie to match any virtual server with same IP. - required: false - choices: ["disable", "same-ip"] - - http_cookie_path: - description: - - Limit HTTP cookie persistence to the specified path. - required: false - - http_cookie_generation: - description: - - Generation of HTTP cookie to be accepted. Changing invalidates all existing cookies. - required: false - - http_cookie_domain_from_host: - description: - - Enable/disable use of HTTP cookie domain from host field in HTTP. - - choice | disable | Disable use of HTTP cookie domain from host field in HTTP (use http-cooke-domain setting). - - choice | enable | Enable use of HTTP cookie domain from host field in HTTP. - required: false - choices: ["disable", "enable"] - - http_cookie_domain: - description: - - Domain that HTTP cookie persistence should apply to. - required: false - - http_cookie_age: - description: - - Time in minutes that client web browsers should keep a cookie. Default is 60 seconds. 0 = no time limit. - required: false - - gratuitous_arp_interval: - description: - - Enable to have the VIP send gratuitous ARPs. 0=disabled. Set from 5 up to 8640000 seconds to enable. - required: false - - extport: - description: - - Incoming port number range that you want to map to a port number range on the destination network. - required: false - - extip: - description: - - IP address or address range on the external interface that you want to map to an address or address range on t - - he destination network. - required: false - - extintf: - description: - - Interface connected to the source network that receives the packets that will be forwarded to the destination - - network. - required: false - - extaddr: - description: - - External FQDN address name. - required: false - - dns_mapping_ttl: - description: - - DNS mapping TTL (Set to zero to use TTL in DNS response, default = 0). - required: false - - comment: - description: - - Comment. - required: false - - color: - description: - - Color of icon on the GUI. - required: false - - arp_reply: - description: - - Enable to respond to ARP requests for this virtual IP address. Enabled by default. - - choice | disable | Disable ARP reply. - - choice | enable | Enable ARP reply. - required: false - choices: ["disable", "enable"] - - dynamic_mapping: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - dynamic_mapping_arp_reply: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_color: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_comment: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_dns_mapping_ttl: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_extaddr: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_extintf: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_extip: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_extport: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_gratuitous_arp_interval: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_http_cookie_age: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_http_cookie_domain: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_http_cookie_domain_from_host: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_http_cookie_generation: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_http_cookie_path: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_http_cookie_share: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | same-ip | - required: false - choices: ["disable", "same-ip"] - - dynamic_mapping_http_ip_header: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_http_ip_header_name: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_http_multiplex: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_https_cookie_secure: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_ldb_method: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | static | - - choice | round-robin | - - choice | weighted | - - choice | least-session | - - choice | least-rtt | - - choice | first-alive | - - choice | http-host | - required: false - choices: ["static", "round-robin", "weighted", "least-session", "least-rtt", "first-alive", "http-host"] - - dynamic_mapping_mapped_addr: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_mappedip: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_mappedport: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_max_embryonic_connections: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_monitor: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_nat_source_vip: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_outlook_web_access: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_persistence: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | none | - - choice | http-cookie | - - choice | ssl-session-id | - required: false - choices: ["none", "http-cookie", "ssl-session-id"] - - dynamic_mapping_portforward: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_portmapping_type: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | 1-to-1 | - - choice | m-to-n | - required: false - choices: ["1-to-1", "m-to-n"] - - dynamic_mapping_protocol: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | tcp | - - choice | udp | - - choice | sctp | - - choice | icmp | - required: false - choices: ["tcp", "udp", "sctp", "icmp"] - - dynamic_mapping_server_type: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | http | - - choice | https | - - choice | ssl | - - choice | tcp | - - choice | udp | - - choice | ip | - - choice | imaps | - - choice | pop3s | - - choice | smtps | - required: false - choices: ["http", "https", "ssl", "tcp", "udp", "ip", "imaps", "pop3s", "smtps"] - - dynamic_mapping_service: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_src_filter: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_srcintf_filter: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_algorithm: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | high | - - choice | medium | - - choice | low | - - choice | custom | - required: false - choices: ["high", "medium", "low", "custom"] - - dynamic_mapping_ssl_certificate: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_client_fallback: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_ssl_client_renegotiation: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | deny | - - choice | allow | - - choice | secure | - required: false - choices: ["deny", "allow", "secure"] - - dynamic_mapping_ssl_client_session_state_max: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_client_session_state_timeout: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_client_session_state_type: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | time | - - choice | count | - - choice | both | - required: false - choices: ["disable", "time", "count", "both"] - - dynamic_mapping_ssl_dh_bits: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | 768 | - - choice | 1024 | - - choice | 1536 | - - choice | 2048 | - - choice | 3072 | - - choice | 4096 | - required: false - choices: ["768", "1024", "1536", "2048", "3072", "4096"] - - dynamic_mapping_ssl_hpkp: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - - choice | report-only | - required: false - choices: ["disable", "enable", "report-only"] - - dynamic_mapping_ssl_hpkp_age: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_hpkp_backup: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_hpkp_include_subdomains: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_ssl_hpkp_primary: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_hpkp_report_uri: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_hsts: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_ssl_hsts_age: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_hsts_include_subdomains: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_ssl_http_location_conversion: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_ssl_http_match_host: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_ssl_max_version: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | ssl-3.0 | - - choice | tls-1.0 | - - choice | tls-1.1 | - - choice | tls-1.2 | - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - dynamic_mapping_ssl_min_version: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | ssl-3.0 | - - choice | tls-1.0 | - - choice | tls-1.1 | - - choice | tls-1.2 | - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - dynamic_mapping_ssl_mode: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | half | - - choice | full | - required: false - choices: ["half", "full"] - - dynamic_mapping_ssl_pfs: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | require | - - choice | deny | - - choice | allow | - required: false - choices: ["require", "deny", "allow"] - - dynamic_mapping_ssl_send_empty_frags: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_ssl_server_algorithm: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | high | - - choice | low | - - choice | medium | - - choice | custom | - - choice | client | - required: false - choices: ["high", "low", "medium", "custom", "client"] - - dynamic_mapping_ssl_server_max_version: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | ssl-3.0 | - - choice | tls-1.0 | - - choice | tls-1.1 | - - choice | tls-1.2 | - - choice | client | - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"] - - dynamic_mapping_ssl_server_min_version: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | ssl-3.0 | - - choice | tls-1.0 | - - choice | tls-1.1 | - - choice | tls-1.2 | - - choice | client | - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"] - - dynamic_mapping_ssl_server_session_state_max: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_server_session_state_timeout: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_server_session_state_type: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | time | - - choice | count | - - choice | both | - required: false - choices: ["disable", "time", "count", "both"] - - dynamic_mapping_type: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | static-nat | - - choice | load-balance | - - choice | server-load-balance | - - choice | dns-translation | - - choice | fqdn | - required: false - choices: ["static-nat", "load-balance", "server-load-balance", "dns-translation", "fqdn"] - - dynamic_mapping_weblogic_server: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_websphere_server: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - required: false - choices: ["disable", "enable"] - - dynamic_mapping_realservers_client_ip: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_realservers_healthcheck: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | disable | - - choice | enable | - - choice | vip | - required: false - choices: ["disable", "enable", "vip"] - - dynamic_mapping_realservers_holddown_interval: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_realservers_http_host: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_realservers_ip: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_realservers_max_connections: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_realservers_monitor: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_realservers_port: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_realservers_seq: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_realservers_status: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | active | - - choice | standby | - - choice | disable | - required: false - choices: ["active", "standby", "disable"] - - dynamic_mapping_realservers_weight: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - required: false - - dynamic_mapping_ssl_cipher_suites_cipher: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - choice | TLS-RSA-WITH-RC4-128-MD5 | - - choice | TLS-RSA-WITH-RC4-128-SHA | - - choice | TLS-RSA-WITH-DES-CBC-SHA | - - choice | TLS-RSA-WITH-3DES-EDE-CBC-SHA | - - choice | TLS-RSA-WITH-AES-128-CBC-SHA | - - choice | TLS-RSA-WITH-AES-256-CBC-SHA | - - choice | TLS-RSA-WITH-AES-128-CBC-SHA256 | - - choice | TLS-RSA-WITH-AES-256-CBC-SHA256 | - - choice | TLS-RSA-WITH-CAMELLIA-128-CBC-SHA | - - choice | TLS-RSA-WITH-CAMELLIA-256-CBC-SHA | - - choice | TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256 | - - choice | TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256 | - - choice | TLS-RSA-WITH-SEED-CBC-SHA | - - choice | TLS-RSA-WITH-ARIA-128-CBC-SHA256 | - - choice | TLS-RSA-WITH-ARIA-256-CBC-SHA384 | - - choice | TLS-DHE-RSA-WITH-DES-CBC-SHA | - - choice | TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA | - - choice | TLS-DHE-RSA-WITH-AES-128-CBC-SHA | - - choice | TLS-DHE-RSA-WITH-AES-256-CBC-SHA | - - choice | TLS-DHE-RSA-WITH-AES-128-CBC-SHA256 | - - choice | TLS-DHE-RSA-WITH-AES-256-CBC-SHA256 | - - choice | TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA | - - choice | TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA | - - choice | TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256 | - - choice | TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256 | - - choice | TLS-DHE-RSA-WITH-SEED-CBC-SHA | - - choice | TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256 | - - choice | TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384 | - - choice | TLS-ECDHE-RSA-WITH-RC4-128-SHA | - - choice | TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA | - - choice | TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA | - - choice | TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA | - - choice | TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256 | - - choice | TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256 | - - choice | TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256 | - - choice | TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 | - - choice | TLS-DHE-RSA-WITH-AES-256-GCM-SHA384 | - - choice | TLS-DHE-DSS-WITH-AES-128-CBC-SHA | - - choice | TLS-DHE-DSS-WITH-AES-256-CBC-SHA | - - choice | TLS-DHE-DSS-WITH-AES-128-CBC-SHA256 | - - choice | TLS-DHE-DSS-WITH-AES-128-GCM-SHA256 | - - choice | TLS-DHE-DSS-WITH-AES-256-CBC-SHA256 | - - choice | TLS-DHE-DSS-WITH-AES-256-GCM-SHA384 | - - choice | TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256 | - - choice | TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256 | - - choice | TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384 | - - choice | TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384 | - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA | - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256 | - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 | - - choice | TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384 | - - choice | TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 | - - choice | TLS-RSA-WITH-AES-128-GCM-SHA256 | - - choice | TLS-RSA-WITH-AES-256-GCM-SHA384 | - - choice | TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA | - - choice | TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA | - - choice | TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256 | - - choice | TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256 | - - choice | TLS-DHE-DSS-WITH-SEED-CBC-SHA | - - choice | TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256 | - - choice | TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384 | - - choice | TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256 | - - choice | TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384 | - - choice | TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256 | - - choice | TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384 | - - choice | TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA | - - choice | TLS-DHE-DSS-WITH-DES-CBC-SHA | - required: false - choices: ["TLS-RSA-WITH-RC4-128-MD5", - "TLS-RSA-WITH-RC4-128-SHA", - "TLS-RSA-WITH-DES-CBC-SHA", - "TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA", - "TLS-RSA-WITH-AES-256-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA256", - "TLS-RSA-WITH-AES-256-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-RSA-WITH-SEED-CBC-SHA", - "TLS-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", - "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-SEED-CBC-SHA", - "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-RC4-128-SHA", - "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", - "TLS-RSA-WITH-AES-128-GCM-SHA256", - "TLS-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-SEED-CBC-SHA", - "TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-DSS-WITH-DES-CBC-SHA"] - - dynamic_mapping_ssl_cipher_suites_versions: - description: - - Dynamic Mapping Version of Suffixed Option Name. Sub-Table. Same Descriptions as Parent. - - FLAG Based Options. Specify multiple in list form. - - flag | ssl-3.0 | - - flag | tls-1.0 | - - flag | tls-1.1 | - - flag | tls-1.2 | - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - realservers: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - realservers_client_ip: - description: - - Only clients in this IP range can connect to this real server. - required: false - - realservers_healthcheck: - description: - - Enable to check the responsiveness of the real server before forwarding traffic. - - choice | disable | Disable per server health check. - - choice | enable | Enable per server health check. - - choice | vip | Use health check defined in VIP. - required: false - choices: ["disable", "enable", "vip"] - - realservers_holddown_interval: - description: - - Time in seconds that the health check monitor monitors an unresponsive server that should be active. - required: false - - realservers_http_host: - description: - - HTTP server domain name in HTTP header. - required: false - - realservers_ip: - description: - - IP address of the real server. - required: false - - realservers_max_connections: - description: - - Max number of active connections that can be directed to the real server. When reached, sessions are sent to - - their real servers. - required: false - - realservers_monitor: - description: - - Name of the health check monitor to use when polling to determine a virtual server's connectivity status. - required: false - - realservers_port: - description: - - Port for communicating with the real server. Required if port forwarding is enabled. - required: false - - realservers_seq: - description: - - Real Server Sequence Number - required: false - - realservers_status: - description: - - Set the status of the real server to active so that it can accept traffic. - - Or on standby or disabled so no traffic is sent. - - choice | active | Server status active. - - choice | standby | Server status standby. - - choice | disable | Server status disable. - required: false - choices: ["active", "standby", "disable"] - - realservers_weight: - description: - - Weight of the real server. If weighted load balancing is enabled, the server with the highest weight gets more - - connections. - required: false - - ssl_cipher_suites: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ssl_cipher_suites_cipher: - description: - - Cipher suite name. - - choice | TLS-RSA-WITH-RC4-128-MD5 | Cipher suite TLS-RSA-WITH-RC4-128-MD5. - - choice | TLS-RSA-WITH-RC4-128-SHA | Cipher suite TLS-RSA-WITH-RC4-128-SHA. - - choice | TLS-RSA-WITH-DES-CBC-SHA | Cipher suite TLS-RSA-WITH-DES-CBC-SHA. - - choice | TLS-RSA-WITH-3DES-EDE-CBC-SHA | Cipher suite TLS-RSA-WITH-3DES-EDE-CBC-SHA. - - choice | TLS-RSA-WITH-AES-128-CBC-SHA | Cipher suite TLS-RSA-WITH-AES-128-CBC-SHA. - - choice | TLS-RSA-WITH-AES-256-CBC-SHA | Cipher suite TLS-RSA-WITH-AES-256-CBC-SHA. - - choice | TLS-RSA-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-RSA-WITH-AES-128-CBC-SHA256. - - choice | TLS-RSA-WITH-AES-256-CBC-SHA256 | Cipher suite TLS-RSA-WITH-AES-256-CBC-SHA256. - - choice | TLS-RSA-WITH-CAMELLIA-128-CBC-SHA | Cipher suite TLS-RSA-WITH-CAMELLIA-128-CBC-SHA. - - choice | TLS-RSA-WITH-CAMELLIA-256-CBC-SHA | Cipher suite TLS-RSA-WITH-CAMELLIA-256-CBC-SHA. - - choice | TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256 | Cipher suite TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256. - - choice | TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256 | Cipher suite TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256. - - choice | TLS-RSA-WITH-SEED-CBC-SHA | Cipher suite TLS-RSA-WITH-SEED-CBC-SHA. - - choice | TLS-RSA-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-RSA-WITH-ARIA-128-CBC-SHA256. - - choice | TLS-RSA-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-RSA-WITH-ARIA-256-CBC-SHA384. - - choice | TLS-DHE-RSA-WITH-DES-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-DES-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-AES-128-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-AES-128-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-AES-256-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-AES-256-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-AES-128-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-AES-256-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-AES-256-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-SEED-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-SEED-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384. - - choice | TLS-ECDHE-RSA-WITH-RC4-128-SHA | Cipher suite TLS-ECDHE-RSA-WITH-RC4-128-SHA. - - choice | TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA | Cipher suite TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA. - - choice | TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA | Cipher suite TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA. - - choice | TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA | Cipher suite TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA. - - choice | TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256 | Cipher suite TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256. - - choice | TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256 | Cipher suite TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256. - - choice | TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256 | Cipher suite TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256. - - choice | TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-DHE-RSA-WITH-AES-128-GCM-SHA256. - - choice | TLS-DHE-RSA-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-DHE-RSA-WITH-AES-256-GCM-SHA384. - - choice | TLS-DHE-DSS-WITH-AES-128-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-AES-128-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-AES-256-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-AES-256-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-AES-128-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-DHE-DSS-WITH-AES-128-GCM-SHA256. - - choice | TLS-DHE-DSS-WITH-AES-256-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-AES-256-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-DHE-DSS-WITH-AES-256-GCM-SHA384. - - choice | TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256. - - choice | TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256. - - choice | TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384 | Cipher suite TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384. - - choice | TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384. - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA. - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256. - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256. - - choice | TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384 | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384. - - choice | TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384. - - choice | TLS-RSA-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-RSA-WITH-AES-128-GCM-SHA256. - - choice | TLS-RSA-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-RSA-WITH-AES-256-GCM-SHA384. - - choice | TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA | Cipher suite TLS-DSS-RSA-WITH-CAMELLIA-128-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-SEED-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-SEED-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384. - - choice | TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256. - - choice | TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384. - - choice | TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC_SHA256. - - choice | TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC_SHA384. - - choice | TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-DES-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-DES-CBC-SHA. - required: false - choices: ["TLS-RSA-WITH-RC4-128-MD5", - "TLS-RSA-WITH-RC4-128-SHA", - "TLS-RSA-WITH-DES-CBC-SHA", - "TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA", - "TLS-RSA-WITH-AES-256-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA256", - "TLS-RSA-WITH-AES-256-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-RSA-WITH-SEED-CBC-SHA", - "TLS-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", - "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-SEED-CBC-SHA", - "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-RC4-128-SHA", - "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", - "TLS-RSA-WITH-AES-128-GCM-SHA256", - "TLS-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-SEED-CBC-SHA", - "TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-DSS-WITH-DES-CBC-SHA"] - - ssl_cipher_suites_versions: - description: - - SSL/TLS versions that the cipher suite can be used with. - - FLAG Based Options. Specify multiple in list form. - - flag | ssl-3.0 | SSL 3.0. - - flag | tls-1.0 | TLS 1.0. - - flag | tls-1.1 | TLS 1.1. - - flag | tls-1.2 | TLS 1.2. - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - ssl_server_cipher_suites: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ssl_server_cipher_suites_cipher: - description: - - Cipher suite name. - - choice | TLS-RSA-WITH-RC4-128-MD5 | Cipher suite TLS-RSA-WITH-RC4-128-MD5. - - choice | TLS-RSA-WITH-RC4-128-SHA | Cipher suite TLS-RSA-WITH-RC4-128-SHA. - - choice | TLS-RSA-WITH-DES-CBC-SHA | Cipher suite TLS-RSA-WITH-DES-CBC-SHA. - - choice | TLS-RSA-WITH-3DES-EDE-CBC-SHA | Cipher suite TLS-RSA-WITH-3DES-EDE-CBC-SHA. - - choice | TLS-RSA-WITH-AES-128-CBC-SHA | Cipher suite TLS-RSA-WITH-AES-128-CBC-SHA. - - choice | TLS-RSA-WITH-AES-256-CBC-SHA | Cipher suite TLS-RSA-WITH-AES-256-CBC-SHA. - - choice | TLS-RSA-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-RSA-WITH-AES-128-CBC-SHA256. - - choice | TLS-RSA-WITH-AES-256-CBC-SHA256 | Cipher suite TLS-RSA-WITH-AES-256-CBC-SHA256. - - choice | TLS-RSA-WITH-CAMELLIA-128-CBC-SHA | Cipher suite TLS-RSA-WITH-CAMELLIA-128-CBC-SHA. - - choice | TLS-RSA-WITH-CAMELLIA-256-CBC-SHA | Cipher suite TLS-RSA-WITH-CAMELLIA-256-CBC-SHA. - - choice | TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256 | Cipher suite TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256. - - choice | TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256 | Cipher suite TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256. - - choice | TLS-RSA-WITH-SEED-CBC-SHA | Cipher suite TLS-RSA-WITH-SEED-CBC-SHA. - - choice | TLS-RSA-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-RSA-WITH-ARIA-128-CBC-SHA256. - - choice | TLS-RSA-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-RSA-WITH-ARIA-256-CBC-SHA384. - - choice | TLS-DHE-RSA-WITH-DES-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-DES-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-AES-128-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-AES-128-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-AES-256-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-AES-256-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-AES-128-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-AES-256-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-AES-256-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-SEED-CBC-SHA | Cipher suite TLS-DHE-RSA-WITH-SEED-CBC-SHA. - - choice | TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256. - - choice | TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384. - - choice | TLS-ECDHE-RSA-WITH-RC4-128-SHA | Cipher suite TLS-ECDHE-RSA-WITH-RC4-128-SHA. - - choice | TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA | Cipher suite TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA. - - choice | TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA | Cipher suite TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA. - - choice | TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA | Cipher suite TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA. - - choice | TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256 | Cipher suite TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256. - - choice | TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256 | Suite TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256. - - choice | TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256 | Cipher suite TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256. - - choice | TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-DHE-RSA-WITH-AES-128-GCM-SHA256. - - choice | TLS-DHE-RSA-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-DHE-RSA-WITH-AES-256-GCM-SHA384. - - choice | TLS-DHE-DSS-WITH-AES-128-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-AES-128-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-AES-256-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-AES-256-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-AES-128-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-DHE-DSS-WITH-AES-128-GCM-SHA256. - - choice | TLS-DHE-DSS-WITH-AES-256-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-AES-256-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-DHE-DSS-WITH-AES-256-GCM-SHA384. - - choice | TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256. - - choice | TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256. - - choice | TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384 | Cipher suite TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384. - - choice | TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384. - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA. - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256 | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256. - - choice | TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256. - - choice | TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384 | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384. - - choice | TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384. - - choice | TLS-RSA-WITH-AES-128-GCM-SHA256 | Cipher suite TLS-RSA-WITH-AES-128-GCM-SHA256. - - choice | TLS-RSA-WITH-AES-256-GCM-SHA384 | Cipher suite TLS-RSA-WITH-AES-256-GCM-SHA384. - - choice | TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA | Cipher suite TLS-DSS-RSA-WITH-CAMELLIA-128-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-SEED-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-SEED-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256. - - choice | TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384. - - choice | TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256. - - choice | TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384. - - choice | TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256 | Cipher suite TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC_SHA256. - - choice | TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384 | Cipher suite TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC_SHA384. - - choice | TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA. - - choice | TLS-DHE-DSS-WITH-DES-CBC-SHA | Cipher suite TLS-DHE-DSS-WITH-DES-CBC-SHA. - required: false - choices: ["TLS-RSA-WITH-RC4-128-MD5", - "TLS-RSA-WITH-RC4-128-SHA", - "TLS-RSA-WITH-DES-CBC-SHA", - "TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA", - "TLS-RSA-WITH-AES-256-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA256", - "TLS-RSA-WITH-AES-256-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-RSA-WITH-SEED-CBC-SHA", - "TLS-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", - "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-SEED-CBC-SHA", - "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-RC4-128-SHA", - "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", - "TLS-RSA-WITH-AES-128-GCM-SHA256", - "TLS-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-SEED-CBC-SHA", - "TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-DSS-WITH-DES-CBC-SHA"] - - ssl_server_cipher_suites_priority: - description: - - SSL/TLS cipher suites priority. - required: false - - ssl_server_cipher_suites_versions: - description: - - SSL/TLS versions that the cipher suite can be used with. - - FLAG Based Options. Specify multiple in list form. - - flag | ssl-3.0 | SSL 3.0. - - flag | tls-1.0 | TLS 1.0. - - flag | tls-1.1 | TLS 1.1. - - flag | tls-1.2 | TLS 1.2. - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - -''' - -EXAMPLES = ''' -# BASIC FULL STATIC NAT MAPPING -- name: EDIT FMGR_FIREWALL_VIP SNAT - fmgr_fwobj_vip: - name: "Basic StaticNAT Map" - mode: "set" - adom: "ansible" - type: "static-nat" - extip: "82.72.192.185" - extintf: "any" - mappedip: "10.7.220.25" - comment: "Created by Ansible" - color: "17" - -# BASIC PORT PNAT MAPPING -- name: EDIT FMGR_FIREWALL_VIP PNAT - fmgr_fwobj_vip: - name: "Basic PNAT Map Port 10443" - mode: "set" - adom: "ansible" - type: "static-nat" - extip: "82.72.192.185" - extport: "10443" - extintf: "any" - portforward: "enable" - protocol: "tcp" - mappedip: "10.7.220.25" - mappedport: "443" - comment: "Created by Ansible" - color: "17" - -# BASIC DNS TRANSLATION NAT -- name: EDIT FMGR_FIREWALL_DNST - fmgr_fwobj_vip: - name: "Basic DNS Translation" - mode: "set" - adom: "ansible" - type: "dns-translation" - extip: "192.168.0.1-192.168.0.100" - extintf: "dmz" - mappedip: "3.3.3.0/24, 4.0.0.0/24" - comment: "Created by Ansible" - color: "12" - -# BASIC FQDN NAT -- name: EDIT FMGR_FIREWALL_FQDN - fmgr_fwobj_vip: - name: "Basic FQDN Translation" - mode: "set" - adom: "ansible" - type: "fqdn" - mapped_addr: "google-play" - comment: "Created by Ansible" - color: "5" - -# DELETE AN ENTRY -- name: DELETE FMGR_FIREWALL_VIP PNAT - fmgr_fwobj_vip: - name: "Basic PNAT Map Port 10443" - mode: "delete" - adom: "ansible" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -def fmgr_firewall_vip_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/firewall/vip'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/firewall/vip/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - websphere_server=dict(required=False, type="str", choices=["disable", "enable"]), - weblogic_server=dict(required=False, type="str", choices=["disable", "enable"]), - type=dict(required=False, type="str", - choices=["static-nat", "load-balance", "server-load-balance", "dns-translation", "fqdn"]), - ssl_server_session_state_type=dict(required=False, type="str", choices=["disable", "time", "count", "both"]), - ssl_server_session_state_timeout=dict(required=False, type="int"), - ssl_server_session_state_max=dict(required=False, type="int"), - ssl_server_min_version=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"]), - ssl_server_max_version=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"]), - ssl_server_algorithm=dict(required=False, type="str", choices=["high", "low", "medium", "custom", "client"]), - ssl_send_empty_frags=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_pfs=dict(required=False, type="str", choices=["require", "deny", "allow"]), - ssl_mode=dict(required=False, type="str", choices=["half", "full"]), - ssl_min_version=dict(required=False, type="str", choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - ssl_max_version=dict(required=False, type="str", choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - ssl_http_match_host=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_http_location_conversion=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_hsts_include_subdomains=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_hsts_age=dict(required=False, type="int"), - ssl_hsts=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_hpkp_report_uri=dict(required=False, type="str"), - ssl_hpkp_primary=dict(required=False, type="str"), - ssl_hpkp_include_subdomains=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_hpkp_backup=dict(required=False, type="str"), - ssl_hpkp_age=dict(required=False, type="int"), - ssl_hpkp=dict(required=False, type="str", choices=["disable", "enable", "report-only"]), - ssl_dh_bits=dict(required=False, type="str", choices=["768", "1024", "1536", "2048", "3072", "4096"]), - ssl_client_session_state_type=dict(required=False, type="str", choices=["disable", "time", "count", "both"]), - ssl_client_session_state_timeout=dict(required=False, type="int"), - ssl_client_session_state_max=dict(required=False, type="int"), - ssl_client_renegotiation=dict(required=False, type="str", choices=["deny", "allow", "secure"]), - ssl_client_fallback=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_certificate=dict(required=False, type="str"), - ssl_algorithm=dict(required=False, type="str", choices=["high", "medium", "low", "custom"]), - srcintf_filter=dict(required=False, type="str"), - src_filter=dict(required=False, type="str"), - service=dict(required=False, type="str"), - server_type=dict(required=False, type="str", - choices=["http", "https", "ssl", "tcp", "udp", "ip", "imaps", "pop3s", "smtps"]), - protocol=dict(required=False, type="str", choices=["tcp", "udp", "sctp", "icmp"]), - portmapping_type=dict(required=False, type="str", choices=["1-to-1", "m-to-n"]), - portforward=dict(required=False, type="str", choices=["disable", "enable"]), - persistence=dict(required=False, type="str", choices=["none", "http-cookie", "ssl-session-id"]), - outlook_web_access=dict(required=False, type="str", choices=["disable", "enable"]), - nat_source_vip=dict(required=False, type="str", choices=["disable", "enable"]), - name=dict(required=False, type="str"), - monitor=dict(required=False, type="str"), - max_embryonic_connections=dict(required=False, type="int"), - mappedport=dict(required=False, type="str"), - mappedip=dict(required=False, type="str"), - mapped_addr=dict(required=False, type="str"), - ldb_method=dict(required=False, type="str", - choices=["static", "round-robin", "weighted", "least-session", "least-rtt", "first-alive", - "http-host"]), - https_cookie_secure=dict(required=False, type="str", choices=["disable", "enable"]), - http_multiplex=dict(required=False, type="str", choices=["disable", "enable"]), - http_ip_header_name=dict(required=False, type="str"), - http_ip_header=dict(required=False, type="str", choices=["disable", "enable"]), - http_cookie_share=dict(required=False, type="str", choices=["disable", "same-ip"]), - http_cookie_path=dict(required=False, type="str"), - http_cookie_generation=dict(required=False, type="int"), - http_cookie_domain_from_host=dict(required=False, type="str", choices=["disable", "enable"]), - http_cookie_domain=dict(required=False, type="str"), - http_cookie_age=dict(required=False, type="int"), - gratuitous_arp_interval=dict(required=False, type="int"), - extport=dict(required=False, type="str"), - extip=dict(required=False, type="str"), - extintf=dict(required=False, type="str"), - extaddr=dict(required=False, type="str"), - dns_mapping_ttl=dict(required=False, type="int"), - comment=dict(required=False, type="str"), - color=dict(required=False, type="int"), - arp_reply=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping=dict(required=False, type="list"), - dynamic_mapping_arp_reply=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_color=dict(required=False, type="int"), - dynamic_mapping_comment=dict(required=False, type="str"), - dynamic_mapping_dns_mapping_ttl=dict(required=False, type="int"), - dynamic_mapping_extaddr=dict(required=False, type="str"), - dynamic_mapping_extintf=dict(required=False, type="str"), - dynamic_mapping_extip=dict(required=False, type="str"), - dynamic_mapping_extport=dict(required=False, type="str"), - dynamic_mapping_gratuitous_arp_interval=dict(required=False, type="int"), - dynamic_mapping_http_cookie_age=dict(required=False, type="int"), - dynamic_mapping_http_cookie_domain=dict(required=False, type="str"), - dynamic_mapping_http_cookie_domain_from_host=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_http_cookie_generation=dict(required=False, type="int"), - dynamic_mapping_http_cookie_path=dict(required=False, type="str"), - dynamic_mapping_http_cookie_share=dict(required=False, type="str", choices=["disable", "same-ip"]), - dynamic_mapping_http_ip_header=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_http_ip_header_name=dict(required=False, type="str"), - dynamic_mapping_http_multiplex=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_https_cookie_secure=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_ldb_method=dict(required=False, type="str", choices=["static", - "round-robin", - "weighted", - "least-session", - "least-rtt", - "first-alive", - "http-host"]), - dynamic_mapping_mapped_addr=dict(required=False, type="str"), - dynamic_mapping_mappedip=dict(required=False, type="str"), - dynamic_mapping_mappedport=dict(required=False, type="str"), - dynamic_mapping_max_embryonic_connections=dict(required=False, type="int"), - dynamic_mapping_monitor=dict(required=False, type="str"), - dynamic_mapping_nat_source_vip=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_outlook_web_access=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_persistence=dict(required=False, type="str", choices=["none", "http-cookie", "ssl-session-id"]), - dynamic_mapping_portforward=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_portmapping_type=dict(required=False, type="str", choices=["1-to-1", "m-to-n"]), - dynamic_mapping_protocol=dict(required=False, type="str", choices=["tcp", "udp", "sctp", "icmp"]), - dynamic_mapping_server_type=dict(required=False, type="str", - choices=["http", "https", "ssl", "tcp", "udp", "ip", "imaps", "pop3s", - "smtps"]), - dynamic_mapping_service=dict(required=False, type="str"), - dynamic_mapping_src_filter=dict(required=False, type="str"), - dynamic_mapping_srcintf_filter=dict(required=False, type="str"), - dynamic_mapping_ssl_algorithm=dict(required=False, type="str", choices=["high", "medium", "low", "custom"]), - dynamic_mapping_ssl_certificate=dict(required=False, type="str"), - dynamic_mapping_ssl_client_fallback=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_ssl_client_renegotiation=dict(required=False, type="str", choices=["deny", "allow", "secure"]), - dynamic_mapping_ssl_client_session_state_max=dict(required=False, type="int"), - dynamic_mapping_ssl_client_session_state_timeout=dict(required=False, type="int"), - dynamic_mapping_ssl_client_session_state_type=dict(required=False, type="str", - choices=["disable", "time", "count", "both"]), - dynamic_mapping_ssl_dh_bits=dict(required=False, type="str", - choices=["768", "1024", "1536", "2048", "3072", "4096"]), - dynamic_mapping_ssl_hpkp=dict(required=False, type="str", choices=["disable", "enable", "report-only"]), - dynamic_mapping_ssl_hpkp_age=dict(required=False, type="int"), - dynamic_mapping_ssl_hpkp_backup=dict(required=False, type="str"), - dynamic_mapping_ssl_hpkp_include_subdomains=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_ssl_hpkp_primary=dict(required=False, type="str"), - dynamic_mapping_ssl_hpkp_report_uri=dict(required=False, type="str"), - dynamic_mapping_ssl_hsts=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_ssl_hsts_age=dict(required=False, type="int"), - dynamic_mapping_ssl_hsts_include_subdomains=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_ssl_http_location_conversion=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_ssl_http_match_host=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_ssl_max_version=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - dynamic_mapping_ssl_min_version=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - dynamic_mapping_ssl_mode=dict(required=False, type="str", choices=["half", "full"]), - dynamic_mapping_ssl_pfs=dict(required=False, type="str", choices=["require", "deny", "allow"]), - dynamic_mapping_ssl_send_empty_frags=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_ssl_server_algorithm=dict(required=False, type="str", - choices=["high", "low", "medium", "custom", "client"]), - dynamic_mapping_ssl_server_max_version=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"]), - dynamic_mapping_ssl_server_min_version=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"]), - dynamic_mapping_ssl_server_session_state_max=dict(required=False, type="int"), - dynamic_mapping_ssl_server_session_state_timeout=dict(required=False, type="int"), - dynamic_mapping_ssl_server_session_state_type=dict(required=False, type="str", - choices=["disable", "time", "count", "both"]), - dynamic_mapping_type=dict(required=False, type="str", - choices=["static-nat", "load-balance", "server-load-balance", "dns-translation", - "fqdn"]), - dynamic_mapping_weblogic_server=dict(required=False, type="str", choices=["disable", "enable"]), - dynamic_mapping_websphere_server=dict(required=False, type="str", choices=["disable", "enable"]), - - dynamic_mapping_realservers_client_ip=dict(required=False, type="str"), - dynamic_mapping_realservers_healthcheck=dict(required=False, type="str", choices=["disable", "enable", "vip"]), - dynamic_mapping_realservers_holddown_interval=dict(required=False, type="int"), - dynamic_mapping_realservers_http_host=dict(required=False, type="str"), - dynamic_mapping_realservers_ip=dict(required=False, type="str"), - dynamic_mapping_realservers_max_connections=dict(required=False, type="int"), - dynamic_mapping_realservers_monitor=dict(required=False, type="str"), - dynamic_mapping_realservers_port=dict(required=False, type="int"), - dynamic_mapping_realservers_seq=dict(required=False, type="str"), - dynamic_mapping_realservers_status=dict(required=False, type="str", choices=["active", "standby", "disable"]), - dynamic_mapping_realservers_weight=dict(required=False, type="int"), - - dynamic_mapping_ssl_cipher_suites_cipher=dict(required=False, - type="str", - choices=["TLS-RSA-WITH-RC4-128-MD5", - "TLS-RSA-WITH-RC4-128-SHA", - "TLS-RSA-WITH-DES-CBC-SHA", - "TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA", - "TLS-RSA-WITH-AES-256-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA256", - "TLS-RSA-WITH-AES-256-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-RSA-WITH-SEED-CBC-SHA", - "TLS-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", - "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-SEED-CBC-SHA", - "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-RC4-128-SHA", - "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", - "TLS-RSA-WITH-AES-128-GCM-SHA256", - "TLS-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-SEED-CBC-SHA", - "TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-DSS-WITH-DES-CBC-SHA"]), - dynamic_mapping_ssl_cipher_suites_versions=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - realservers=dict(required=False, type="list"), - realservers_client_ip=dict(required=False, type="str"), - realservers_healthcheck=dict(required=False, type="str", choices=["disable", "enable", "vip"]), - realservers_holddown_interval=dict(required=False, type="int"), - realservers_http_host=dict(required=False, type="str"), - realservers_ip=dict(required=False, type="str"), - realservers_max_connections=dict(required=False, type="int"), - realservers_monitor=dict(required=False, type="str"), - realservers_port=dict(required=False, type="int"), - realservers_seq=dict(required=False, type="str"), - realservers_status=dict(required=False, type="str", choices=["active", "standby", "disable"]), - realservers_weight=dict(required=False, type="int"), - ssl_cipher_suites=dict(required=False, type="list"), - ssl_cipher_suites_cipher=dict(required=False, - type="str", - choices=["TLS-RSA-WITH-RC4-128-MD5", - "TLS-RSA-WITH-RC4-128-SHA", - "TLS-RSA-WITH-DES-CBC-SHA", - "TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA", - "TLS-RSA-WITH-AES-256-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA256", - "TLS-RSA-WITH-AES-256-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-RSA-WITH-SEED-CBC-SHA", - "TLS-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", - "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-SEED-CBC-SHA", - "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-RC4-128-SHA", - "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", - "TLS-RSA-WITH-AES-128-GCM-SHA256", - "TLS-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-SEED-CBC-SHA", - "TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-DSS-WITH-DES-CBC-SHA"]), - ssl_cipher_suites_versions=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - ssl_server_cipher_suites=dict(required=False, type="list"), - ssl_server_cipher_suites_cipher=dict(required=False, - type="str", - choices=["TLS-RSA-WITH-RC4-128-MD5", - "TLS-RSA-WITH-RC4-128-SHA", - "TLS-RSA-WITH-DES-CBC-SHA", - "TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA", - "TLS-RSA-WITH-AES-256-CBC-SHA", - "TLS-RSA-WITH-AES-128-CBC-SHA256", - "TLS-RSA-WITH-AES-256-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-RSA-WITH-SEED-CBC-SHA", - "TLS-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", - "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-RSA-WITH-SEED-CBC-SHA", - "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-RC4-128-SHA", - "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", - "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", - "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA", - "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256", - "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", - "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", - "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", - "TLS-RSA-WITH-AES-128-GCM-SHA256", - "TLS-RSA-WITH-AES-256-GCM-SHA384", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA", - "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256", - "TLS-DHE-DSS-WITH-SEED-CBC-SHA", - "TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256", - "TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384", - "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256", - "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384", - "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-DSS-WITH-DES-CBC-SHA"]), - ssl_server_cipher_suites_priority=dict(required=False, type="str"), - ssl_server_cipher_suites_versions=dict(required=False, type="str", - choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "websphere-server": module.params["websphere_server"], - "weblogic-server": module.params["weblogic_server"], - "type": module.params["type"], - "ssl-server-session-state-type": module.params["ssl_server_session_state_type"], - "ssl-server-session-state-timeout": module.params["ssl_server_session_state_timeout"], - "ssl-server-session-state-max": module.params["ssl_server_session_state_max"], - "ssl-server-min-version": module.params["ssl_server_min_version"], - "ssl-server-max-version": module.params["ssl_server_max_version"], - "ssl-server-algorithm": module.params["ssl_server_algorithm"], - "ssl-send-empty-frags": module.params["ssl_send_empty_frags"], - "ssl-pfs": module.params["ssl_pfs"], - "ssl-mode": module.params["ssl_mode"], - "ssl-min-version": module.params["ssl_min_version"], - "ssl-max-version": module.params["ssl_max_version"], - "ssl-http-match-host": module.params["ssl_http_match_host"], - "ssl-http-location-conversion": module.params["ssl_http_location_conversion"], - "ssl-hsts-include-subdomains": module.params["ssl_hsts_include_subdomains"], - "ssl-hsts-age": module.params["ssl_hsts_age"], - "ssl-hsts": module.params["ssl_hsts"], - "ssl-hpkp-report-uri": module.params["ssl_hpkp_report_uri"], - "ssl-hpkp-primary": module.params["ssl_hpkp_primary"], - "ssl-hpkp-include-subdomains": module.params["ssl_hpkp_include_subdomains"], - "ssl-hpkp-backup": module.params["ssl_hpkp_backup"], - "ssl-hpkp-age": module.params["ssl_hpkp_age"], - "ssl-hpkp": module.params["ssl_hpkp"], - "ssl-dh-bits": module.params["ssl_dh_bits"], - "ssl-client-session-state-type": module.params["ssl_client_session_state_type"], - "ssl-client-session-state-timeout": module.params["ssl_client_session_state_timeout"], - "ssl-client-session-state-max": module.params["ssl_client_session_state_max"], - "ssl-client-renegotiation": module.params["ssl_client_renegotiation"], - "ssl-client-fallback": module.params["ssl_client_fallback"], - "ssl-certificate": module.params["ssl_certificate"], - "ssl-algorithm": module.params["ssl_algorithm"], - "srcintf-filter": module.params["srcintf_filter"], - "src-filter": module.params["src_filter"], - "service": module.params["service"], - "server-type": module.params["server_type"], - "protocol": module.params["protocol"], - "portmapping-type": module.params["portmapping_type"], - "portforward": module.params["portforward"], - "persistence": module.params["persistence"], - "outlook-web-access": module.params["outlook_web_access"], - "nat-source-vip": module.params["nat_source_vip"], - "name": module.params["name"], - "monitor": module.params["monitor"], - "max-embryonic-connections": module.params["max_embryonic_connections"], - "mappedport": module.params["mappedport"], - "mappedip": module.params["mappedip"], - "mapped-addr": module.params["mapped_addr"], - "ldb-method": module.params["ldb_method"], - "https-cookie-secure": module.params["https_cookie_secure"], - "http-multiplex": module.params["http_multiplex"], - "http-ip-header-name": module.params["http_ip_header_name"], - "http-ip-header": module.params["http_ip_header"], - "http-cookie-share": module.params["http_cookie_share"], - "http-cookie-path": module.params["http_cookie_path"], - "http-cookie-generation": module.params["http_cookie_generation"], - "http-cookie-domain-from-host": module.params["http_cookie_domain_from_host"], - "http-cookie-domain": module.params["http_cookie_domain"], - "http-cookie-age": module.params["http_cookie_age"], - "gratuitous-arp-interval": module.params["gratuitous_arp_interval"], - "extport": module.params["extport"], - "extip": module.params["extip"], - "extintf": module.params["extintf"], - "extaddr": module.params["extaddr"], - "dns-mapping-ttl": module.params["dns_mapping_ttl"], - "comment": module.params["comment"], - "color": module.params["color"], - "arp-reply": module.params["arp_reply"], - "dynamic_mapping": { - "arp-reply": module.params["dynamic_mapping_arp_reply"], - "color": module.params["dynamic_mapping_color"], - "comment": module.params["dynamic_mapping_comment"], - "dns-mapping-ttl": module.params["dynamic_mapping_dns_mapping_ttl"], - "extaddr": module.params["dynamic_mapping_extaddr"], - "extintf": module.params["dynamic_mapping_extintf"], - "extip": module.params["dynamic_mapping_extip"], - "extport": module.params["dynamic_mapping_extport"], - "gratuitous-arp-interval": module.params["dynamic_mapping_gratuitous_arp_interval"], - "http-cookie-age": module.params["dynamic_mapping_http_cookie_age"], - "http-cookie-domain": module.params["dynamic_mapping_http_cookie_domain"], - "http-cookie-domain-from-host": module.params["dynamic_mapping_http_cookie_domain_from_host"], - "http-cookie-generation": module.params["dynamic_mapping_http_cookie_generation"], - "http-cookie-path": module.params["dynamic_mapping_http_cookie_path"], - "http-cookie-share": module.params["dynamic_mapping_http_cookie_share"], - "http-ip-header": module.params["dynamic_mapping_http_ip_header"], - "http-ip-header-name": module.params["dynamic_mapping_http_ip_header_name"], - "http-multiplex": module.params["dynamic_mapping_http_multiplex"], - "https-cookie-secure": module.params["dynamic_mapping_https_cookie_secure"], - "ldb-method": module.params["dynamic_mapping_ldb_method"], - "mapped-addr": module.params["dynamic_mapping_mapped_addr"], - "mappedip": module.params["dynamic_mapping_mappedip"], - "mappedport": module.params["dynamic_mapping_mappedport"], - "max-embryonic-connections": module.params["dynamic_mapping_max_embryonic_connections"], - "monitor": module.params["dynamic_mapping_monitor"], - "nat-source-vip": module.params["dynamic_mapping_nat_source_vip"], - "outlook-web-access": module.params["dynamic_mapping_outlook_web_access"], - "persistence": module.params["dynamic_mapping_persistence"], - "portforward": module.params["dynamic_mapping_portforward"], - "portmapping-type": module.params["dynamic_mapping_portmapping_type"], - "protocol": module.params["dynamic_mapping_protocol"], - "server-type": module.params["dynamic_mapping_server_type"], - "service": module.params["dynamic_mapping_service"], - "src-filter": module.params["dynamic_mapping_src_filter"], - "srcintf-filter": module.params["dynamic_mapping_srcintf_filter"], - "ssl-algorithm": module.params["dynamic_mapping_ssl_algorithm"], - "ssl-certificate": module.params["dynamic_mapping_ssl_certificate"], - "ssl-client-fallback": module.params["dynamic_mapping_ssl_client_fallback"], - "ssl-client-renegotiation": module.params["dynamic_mapping_ssl_client_renegotiation"], - "ssl-client-session-state-max": module.params["dynamic_mapping_ssl_client_session_state_max"], - "ssl-client-session-state-timeout": module.params["dynamic_mapping_ssl_client_session_state_timeout"], - "ssl-client-session-state-type": module.params["dynamic_mapping_ssl_client_session_state_type"], - "ssl-dh-bits": module.params["dynamic_mapping_ssl_dh_bits"], - "ssl-hpkp": module.params["dynamic_mapping_ssl_hpkp"], - "ssl-hpkp-age": module.params["dynamic_mapping_ssl_hpkp_age"], - "ssl-hpkp-backup": module.params["dynamic_mapping_ssl_hpkp_backup"], - "ssl-hpkp-include-subdomains": module.params["dynamic_mapping_ssl_hpkp_include_subdomains"], - "ssl-hpkp-primary": module.params["dynamic_mapping_ssl_hpkp_primary"], - "ssl-hpkp-report-uri": module.params["dynamic_mapping_ssl_hpkp_report_uri"], - "ssl-hsts": module.params["dynamic_mapping_ssl_hsts"], - "ssl-hsts-age": module.params["dynamic_mapping_ssl_hsts_age"], - "ssl-hsts-include-subdomains": module.params["dynamic_mapping_ssl_hsts_include_subdomains"], - "ssl-http-location-conversion": module.params["dynamic_mapping_ssl_http_location_conversion"], - "ssl-http-match-host": module.params["dynamic_mapping_ssl_http_match_host"], - "ssl-max-version": module.params["dynamic_mapping_ssl_max_version"], - "ssl-min-version": module.params["dynamic_mapping_ssl_min_version"], - "ssl-mode": module.params["dynamic_mapping_ssl_mode"], - "ssl-pfs": module.params["dynamic_mapping_ssl_pfs"], - "ssl-send-empty-frags": module.params["dynamic_mapping_ssl_send_empty_frags"], - "ssl-server-algorithm": module.params["dynamic_mapping_ssl_server_algorithm"], - "ssl-server-max-version": module.params["dynamic_mapping_ssl_server_max_version"], - "ssl-server-min-version": module.params["dynamic_mapping_ssl_server_min_version"], - "ssl-server-session-state-max": module.params["dynamic_mapping_ssl_server_session_state_max"], - "ssl-server-session-state-timeout": module.params["dynamic_mapping_ssl_server_session_state_timeout"], - "ssl-server-session-state-type": module.params["dynamic_mapping_ssl_server_session_state_type"], - "type": module.params["dynamic_mapping_type"], - "weblogic-server": module.params["dynamic_mapping_weblogic_server"], - "websphere-server": module.params["dynamic_mapping_websphere_server"], - "realservers": { - "client-ip": module.params["dynamic_mapping_realservers_client_ip"], - "healthcheck": module.params["dynamic_mapping_realservers_healthcheck"], - "holddown-interval": module.params["dynamic_mapping_realservers_holddown_interval"], - "http-host": module.params["dynamic_mapping_realservers_http_host"], - "ip": module.params["dynamic_mapping_realservers_ip"], - "max-connections": module.params["dynamic_mapping_realservers_max_connections"], - "monitor": module.params["dynamic_mapping_realservers_monitor"], - "port": module.params["dynamic_mapping_realservers_port"], - "seq": module.params["dynamic_mapping_realservers_seq"], - "status": module.params["dynamic_mapping_realservers_status"], - "weight": module.params["dynamic_mapping_realservers_weight"], - }, - "ssl-cipher-suites": { - "cipher": module.params["dynamic_mapping_ssl_cipher_suites_cipher"], - "versions": module.params["dynamic_mapping_ssl_cipher_suites_versions"], - }, - }, - "realservers": { - "client-ip": module.params["realservers_client_ip"], - "healthcheck": module.params["realservers_healthcheck"], - "holddown-interval": module.params["realservers_holddown_interval"], - "http-host": module.params["realservers_http_host"], - "ip": module.params["realservers_ip"], - "max-connections": module.params["realservers_max_connections"], - "monitor": module.params["realservers_monitor"], - "port": module.params["realservers_port"], - "seq": module.params["realservers_seq"], - "status": module.params["realservers_status"], - "weight": module.params["realservers_weight"], - }, - "ssl-cipher-suites": { - "cipher": module.params["ssl_cipher_suites_cipher"], - "versions": module.params["ssl_cipher_suites_versions"], - }, - "ssl-server-cipher-suites": { - "cipher": module.params["ssl_server_cipher_suites_cipher"], - "priority": module.params["ssl_server_cipher_suites_priority"], - "versions": module.params["ssl_server_cipher_suites_versions"], - } - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['dynamic_mapping', 'realservers', 'ssl-cipher-suites', 'ssl-server-cipher-suites'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - try: - results = fmgr_firewall_vip_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_fwpol_ipv4.py b/plugins/modules/network/fortimanager/fmgr_fwpol_ipv4.py deleted file mode 100644 index 27063bf276..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_fwpol_ipv4.py +++ /dev/null @@ -1,1359 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_fwpol_ipv4 -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Allows the add/delete of Firewall Policies on Packages in FortiManager. -description: - - Allows the add/delete of Firewall Policies on Packages in FortiManager. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - package_name: - description: - - The policy package you want to modify - required: false - default: "default" - - fail_on_missing_dependency: - description: - - Normal behavior is to "skip" tasks that fail dependency checks, so other tasks can run. - - If set to "enabled" if a failed dependency check happeens, Ansible will exit as with failure instead of skip. - required: false - default: "disable" - choices: ["enable", "disable"] - - wsso: - description: - - Enable/disable WiFi Single Sign On (WSSO). - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - webfilter_profile: - description: - - Name of an existing Web filter profile. - required: false - - webcache_https: - description: - - Enable/disable web cache for HTTPS. - - choice | disable | Disable web cache for HTTPS. - - choice | enable | Enable web cache for HTTPS. - required: false - choices: ["disable", "enable"] - - webcache: - description: - - Enable/disable web cache. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - wccp: - description: - - Enable/disable forwarding traffic matching this policy to a configured WCCP server. - - choice | disable | Disable WCCP setting. - - choice | enable | Enable WCCP setting. - required: false - choices: ["disable", "enable"] - - wanopt_profile: - description: - - WAN optimization profile. - required: false - - wanopt_peer: - description: - - WAN optimization peer. - required: false - - wanopt_passive_opt: - description: - - WAN optimization passive mode options. This option decides what IP address will be used to connect server. - - choice | default | Allow client side WAN opt peer to decide. - - choice | transparent | Use address of client to connect to server. - - choice | non-transparent | Use local FortiGate address to connect to server. - required: false - choices: ["default", "transparent", "non-transparent"] - - wanopt_detection: - description: - - WAN optimization auto-detection mode. - - choice | active | Active WAN optimization peer auto-detection. - - choice | passive | Passive WAN optimization peer auto-detection. - - choice | off | Turn off WAN optimization peer auto-detection. - required: false - choices: ["active", "passive", "off"] - - wanopt: - description: - - Enable/disable WAN optimization. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - waf_profile: - description: - - Name of an existing Web application firewall profile. - required: false - - vpntunnel: - description: - - Policy-based IPsec VPN | name of the IPsec VPN Phase 1. - required: false - - voip_profile: - description: - - Name of an existing VoIP profile. - required: false - - vlan_filter: - description: - - Set VLAN filters. - required: false - - vlan_cos_rev: - description: - - VLAN reverse direction user priority | 255 passthrough, 0 lowest, 7 highest.. - required: false - - vlan_cos_fwd: - description: - - VLAN forward direction user priority | 255 passthrough, 0 lowest, 7 highest. - required: false - - utm_status: - description: - - Enable to add one or more security profiles (AV, IPS, etc.) to the firewall policy. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - users: - description: - - Names of individual users that can authenticate with this policy. - required: false - - url_category: - description: - - URL category ID list. - required: false - - traffic_shaper_reverse: - description: - - Reverse traffic shaper. - required: false - - traffic_shaper: - description: - - Traffic shaper. - required: false - - timeout_send_rst: - description: - - Enable/disable sending RST packets when TCP sessions expire. - - choice | disable | Disable sending of RST packet upon TCP session expiration. - - choice | enable | Enable sending of RST packet upon TCP session expiration. - required: false - choices: ["disable", "enable"] - - tcp_session_without_syn: - description: - - Enable/disable creation of TCP session without SYN flag. - - choice | all | Enable TCP session without SYN. - - choice | data-only | Enable TCP session data only. - - choice | disable | Disable TCP session without SYN. - required: false - choices: ["all", "data-only", "disable"] - - tcp_mss_sender: - description: - - Sender TCP maximum segment size (MSS). - required: false - - tcp_mss_receiver: - description: - - Receiver TCP maximum segment size (MSS). - required: false - - status: - description: - - Enable or disable this policy. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - ssl_ssh_profile: - description: - - Name of an existing SSL SSH profile. - required: false - - ssl_mirror_intf: - description: - - SSL mirror interface name. - required: false - - ssl_mirror: - description: - - Enable to copy decrypted SSL traffic to a FortiGate interface (called SSL mirroring). - - choice | disable | Disable SSL mirror. - - choice | enable | Enable SSL mirror. - required: false - choices: ["disable", "enable"] - - ssh_filter_profile: - description: - - Name of an existing SSH filter profile. - required: false - - srcintf: - description: - - Incoming (ingress) interface. - required: false - - srcaddr_negate: - description: - - When enabled srcaddr specifies what the source address must NOT be. - - choice | disable | Disable source address negate. - - choice | enable | Enable source address negate. - required: false - choices: ["disable", "enable"] - - srcaddr: - description: - - Source address and address group names. - required: false - - spamfilter_profile: - description: - - Name of an existing Spam filter profile. - required: false - - session_ttl: - description: - - TTL in seconds for sessions accepted by this policy (0 means use the system default session TTL). - required: false - - service_negate: - description: - - When enabled service specifies what the service must NOT be. - - choice | disable | Disable negated service match. - - choice | enable | Enable negated service match. - required: false - choices: ["disable", "enable"] - - service: - description: - - Service and service group names. - required: false - - send_deny_packet: - description: - - Enable to send a reply when a session is denied or blocked by a firewall policy. - - choice | disable | Disable deny-packet sending. - - choice | enable | Enable deny-packet sending. - required: false - choices: ["disable", "enable"] - - schedule_timeout: - description: - - Enable to force current sessions to end when the schedule object times out. - - choice | disable | Disable schedule timeout. - - choice | enable | Enable schedule timeout. - required: false - choices: ["disable", "enable"] - - schedule: - description: - - Schedule name. - required: false - - scan_botnet_connections: - description: - - Block or monitor connections to Botnet servers or disable Botnet scanning. - - choice | disable | Do not scan connections to botnet servers. - - choice | block | Block connections to botnet servers. - - choice | monitor | Log connections to botnet servers. - required: false - choices: ["disable", "block", "monitor"] - - rtp_nat: - description: - - Enable Real Time Protocol (RTP) NAT. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - rtp_addr: - description: - - Address names if this is an RTP NAT policy. - required: false - - rsso: - description: - - Enable/disable RADIUS single sign-on (RSSO). - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - replacemsg_override_group: - description: - - Override the default replacement message group for this policy. - required: false - - redirect_url: - description: - - URL users are directed to after seeing and accepting the disclaimer or authenticating. - required: false - - radius_mac_auth_bypass: - description: - - Enable MAC authentication bypass. The bypassed MAC address must be received from RADIUS server. - - choice | disable | Disable MAC authentication bypass. - - choice | enable | Enable MAC authentication bypass. - required: false - choices: ["disable", "enable"] - - profile_type: - description: - - Determine whether the firewall policy allows security profile groups or single profiles only. - - choice | single | Do not allow security profile groups. - - choice | group | Allow security profile groups. - required: false - choices: ["single", "group"] - - profile_protocol_options: - description: - - Name of an existing Protocol options profile. - required: false - - profile_group: - description: - - Name of profile group. - required: false - - poolname: - description: - - IP Pool names. - required: false - - policyid: - description: - - Policy ID. - required: false - - permit_stun_host: - description: - - Accept UDP packets from any Session Traversal Utilities for NAT (STUN) host. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - permit_any_host: - description: - - Accept UDP packets from any host. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - per_ip_shaper: - description: - - Per-IP traffic shaper. - required: false - - outbound: - description: - - Policy-based IPsec VPN | only traffic from the internal network can initiate a VPN. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - ntlm_guest: - description: - - Enable/disable NTLM guest user access. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - ntlm_enabled_browsers: - description: - - HTTP-User-Agent value of supported browsers. - required: false - - ntlm: - description: - - Enable/disable NTLM authentication. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - np_acceleration: - description: - - Enable/disable UTM Network Processor acceleration. - - choice | disable | Disable UTM Network Processor acceleration. - - choice | enable | Enable UTM Network Processor acceleration. - required: false - choices: ["disable", "enable"] - - natoutbound: - description: - - Policy-based IPsec VPN | apply source NAT to outbound traffic. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - natip: - description: - - Policy-based IPsec VPN | source NAT IP address for outgoing traffic. - required: false - - natinbound: - description: - - Policy-based IPsec VPN | apply destination NAT to inbound traffic. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - nat: - description: - - Enable/disable source NAT. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - name: - description: - - Policy name. - required: false - - mms_profile: - description: - - Name of an existing MMS profile. - required: false - - match_vip: - description: - - Enable to match packets that have had their destination addresses changed by a VIP. - - choice | disable | Do not match DNATed packet. - - choice | enable | Match DNATed packet. - required: false - choices: ["disable", "enable"] - - logtraffic_start: - description: - - Record logs when a session starts and ends. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - logtraffic: - description: - - Enable or disable logging. Log all sessions or security profile sessions. - - choice | disable | Disable all logging for this policy. - - choice | all | Log all sessions accepted or denied by this policy. - - choice | utm | Log traffic that has a security profile applied to it. - required: false - choices: ["disable", "all", "utm"] - - learning_mode: - description: - - Enable to allow everything, but log all of the meaningful data for security information gathering. - - choice | disable | Disable learning mode in firewall policy. - - choice | enable | Enable learning mode in firewall policy. - required: false - choices: ["disable", "enable"] - - label: - description: - - Label for the policy that appears when the GUI is in Section View mode. - required: false - - ips_sensor: - description: - - Name of an existing IPS sensor. - required: false - - ippool: - description: - - Enable to use IP Pools for source NAT. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - internet_service_src_negate: - description: - - When enabled internet-service-src specifies what the service must NOT be. - - choice | disable | Disable negated Internet Service source match. - - choice | enable | Enable negated Internet Service source match. - required: false - choices: ["disable", "enable"] - - internet_service_src_id: - description: - - Internet Service source ID. - required: false - - internet_service_src_custom: - description: - - Custom Internet Service source name. - required: false - - internet_service_src: - description: - - Enable/disable use of Internet Services in source for this policy. If enabled, source address is not used. - - choice | disable | Disable use of Internet Services source in policy. - - choice | enable | Enable use of Internet Services source in policy. - required: false - choices: ["disable", "enable"] - - internet_service_negate: - description: - - When enabled internet-service specifies what the service must NOT be. - - choice | disable | Disable negated Internet Service match. - - choice | enable | Enable negated Internet Service match. - required: false - choices: ["disable", "enable"] - - internet_service_id: - description: - - Internet Service ID. - required: false - - internet_service_custom: - description: - - Custom Internet Service name. - required: false - - internet_service: - description: - - Enable/disable use of Internet Services for this policy. If enabled, dstaddr and service are not used. - - choice | disable | Disable use of Internet Services in policy. - - choice | enable | Enable use of Internet Services in policy. - required: false - choices: ["disable", "enable"] - - inbound: - description: - - Policy-based IPsec VPN | only traffic from the remote network can initiate a VPN. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - identity_based_route: - description: - - Name of identity-based routing rule. - required: false - - icap_profile: - description: - - Name of an existing ICAP profile. - required: false - - gtp_profile: - description: - - GTP profile. - required: false - - groups: - description: - - Names of user groups that can authenticate with this policy. - required: false - - global_label: - description: - - Label for the policy that appears when the GUI is in Global View mode. - required: false - - fsso_agent_for_ntlm: - description: - - FSSO agent to use for NTLM authentication. - required: false - - fsso: - description: - - Enable/disable Fortinet Single Sign-On. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - fixedport: - description: - - Enable to prevent source NAT from changing a session's source port. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - firewall_session_dirty: - description: - - How to handle sessions if the configuration of this firewall policy changes. - - choice | check-all | Flush all current sessions accepted by this policy. - - choice | check-new | Continue to allow sessions already accepted by this policy. - required: false - choices: ["check-all", "check-new"] - - dstintf: - description: - - Outgoing (egress) interface. - required: false - - dstaddr_negate: - description: - - When enabled dstaddr specifies what the destination address must NOT be. - - choice | disable | Disable destination address negate. - - choice | enable | Enable destination address negate. - required: false - choices: ["disable", "enable"] - - dstaddr: - description: - - Destination address and address group names. - required: false - - dsri: - description: - - Enable DSRI to ignore HTTP server responses. - - choice | disable | Disable DSRI. - - choice | enable | Enable DSRI. - required: false - choices: ["disable", "enable"] - - dscp_value: - description: - - DSCP value. - required: false - - dscp_negate: - description: - - Enable negated DSCP match. - - choice | disable | Disable DSCP negate. - - choice | enable | Enable DSCP negate. - required: false - choices: ["disable", "enable"] - - dscp_match: - description: - - Enable DSCP check. - - choice | disable | Disable DSCP check. - - choice | enable | Enable DSCP check. - required: false - choices: ["disable", "enable"] - - dnsfilter_profile: - description: - - Name of an existing DNS filter profile. - required: false - - dlp_sensor: - description: - - Name of an existing DLP sensor. - required: false - - disclaimer: - description: - - Enable/disable user authentication disclaimer. - - choice | disable | Disable user authentication disclaimer. - - choice | enable | Enable user authentication disclaimer. - required: false - choices: ["disable", "enable"] - - diffservcode_rev: - description: - - Change packet's reverse (reply) DiffServ to this value. - required: false - - diffservcode_forward: - description: - - Change packet's DiffServ to this value. - required: false - - diffserv_reverse: - description: - - Enable to change packet's reverse (reply) DiffServ values to the specified diffservcode-rev value. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - diffserv_forward: - description: - - Enable to change packet's DiffServ values to the specified diffservcode-forward value. - - choice | disable | Disable WAN optimization. - - choice | enable | Enable WAN optimization. - required: false - choices: ["disable", "enable"] - - devices: - description: - - Names of devices or device groups that can be matched by the policy. - required: false - - delay_tcp_npu_session: - description: - - Enable TCP NPU session delay to guarantee packet order of 3-way handshake. - - choice | disable | Disable TCP NPU session delay in order to guarantee packet order of 3-way handshake. - - choice | enable | Enable TCP NPU session delay in order to guarantee packet order of 3-way handshake. - required: false - choices: ["disable", "enable"] - - custom_log_fields: - description: - - Custom fields to append to log messages for this policy. - required: false - - comments: - description: - - Comment. - required: false - - capture_packet: - description: - - Enable/disable capture packets. - - choice | disable | Disable capture packets. - - choice | enable | Enable capture packets. - required: false - choices: ["disable", "enable"] - - captive_portal_exempt: - description: - - Enable to exempt some users from the captive portal. - - choice | disable | Disable exemption of captive portal. - - choice | enable | Enable exemption of captive portal. - required: false - choices: ["disable", "enable"] - - block_notification: - description: - - Enable/disable block notification. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - av_profile: - description: - - Name of an existing Antivirus profile. - required: false - - auto_asic_offload: - description: - - Enable/disable offloading security profile processing to CP processors. - - choice | disable | Disable ASIC offloading. - - choice | enable | Enable auto ASIC offloading. - required: false - choices: ["disable", "enable"] - - auth_redirect_addr: - description: - - HTTP-to-HTTPS redirect address for firewall authentication. - required: false - - auth_path: - description: - - Enable/disable authentication-based routing. - - choice | disable | Disable authentication-based routing. - - choice | enable | Enable authentication-based routing. - required: false - choices: ["disable", "enable"] - - auth_cert: - description: - - HTTPS server certificate for policy authentication. - required: false - - application_list: - description: - - Name of an existing Application list. - required: false - - application: - description: - - Application ID list. - required: false - - app_group: - description: - - Application group names. - required: false - - app_category: - description: - - Application category ID list. - required: false - - action: - description: - - Policy action (allow/deny/ipsec). - - choice | deny | Blocks sessions that match the firewall policy. - - choice | accept | Allows session that match the firewall policy. - - choice | ipsec | Firewall policy becomes a policy-based IPsec VPN policy. - required: false - choices: ["deny", "accept", "ipsec"] - - vpn_dst_node: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - required: false - - vpn_dst_node_host: - description: - - VPN Destination Node Host. - required: false - - vpn_dst_node_seq: - description: - - VPN Destination Node Seq. - required: false - - vpn_dst_node_subnet: - description: - - VPN Destination Node Seq. - required: false - - vpn_src_node: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - required: false - - vpn_src_node_host: - description: - - VPN Source Node Host. - required: false - - vpn_src_node_seq: - description: - - VPN Source Node Seq. - required: false - - vpn_src_node_subnet: - description: - - VPN Source Node. - required: false - - -''' - -EXAMPLES = ''' -- name: ADD VERY BASIC IPV4 POLICY WITH NO NAT (WIDE OPEN) - fmgr_fwpol_ipv4: - mode: "set" - adom: "ansible" - package_name: "default" - name: "Basic_IPv4_Policy" - comments: "Created by Ansible" - action: "accept" - dstaddr: "all" - srcaddr: "all" - dstintf: "any" - srcintf: "any" - logtraffic: "utm" - service: "ALL" - schedule: "always" - -- name: ADD VERY BASIC IPV4 POLICY WITH NAT AND MULTIPLE ENTRIES - fmgr_fwpol_ipv4: - mode: "set" - adom: "ansible" - package_name: "default" - name: "Basic_IPv4_Policy_2" - comments: "Created by Ansible" - action: "accept" - dstaddr: "google-play" - srcaddr: "all" - dstintf: "any" - srcintf: "any" - logtraffic: "utm" - service: "HTTP, HTTPS" - schedule: "always" - nat: "enable" - users: "karen, kevin" - -- name: ADD VERY BASIC IPV4 POLICY WITH NAT AND MULTIPLE ENTRIES AND SEC PROFILES - fmgr_fwpol_ipv4: - mode: "set" - adom: "ansible" - package_name: "default" - name: "Basic_IPv4_Policy_3" - comments: "Created by Ansible" - action: "accept" - dstaddr: "google-play, autoupdate.opera.com" - srcaddr: "corp_internal" - dstintf: "zone_wan1, zone_wan2" - srcintf: "zone_int1" - logtraffic: "utm" - service: "HTTP, HTTPS" - schedule: "always" - nat: "enable" - users: "karen, kevin" - av_profile: "sniffer-profile" - ips_sensor: "default" - -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -def fmgr_firewall_policy_modify(fmgr, paramgram): - """ - fmgr_firewall_policy -- Add/Set/Deletes Firewall Policy Objects defined in the "paramgram" - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/pkg/{pkg}/firewall/policy'.format(adom=adom, pkg=paramgram["package_name"]) - datagram = scrub_dict((prepare_dict(paramgram))) - del datagram["package_name"] - datagram = fmgr._tools.split_comma_strings_into_lists(datagram) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - url = '/pm/config/adom/{adom}/pkg/{pkg}/firewall' \ - '/policy/{policyid}'.format(adom=paramgram["adom"], - pkg=paramgram["package_name"], - policyid=paramgram["policyid"]) - datagram = { - "policyid": paramgram["policyid"] - } - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - package_name=dict(type="str", required=False, default="default"), - fail_on_missing_dependency=dict(type="str", required=False, default="disable", choices=["enable", - "disable"]), - wsso=dict(required=False, type="str", choices=["disable", "enable"]), - webfilter_profile=dict(required=False, type="str"), - webcache_https=dict(required=False, type="str", choices=["disable", "enable"]), - webcache=dict(required=False, type="str", choices=["disable", "enable"]), - wccp=dict(required=False, type="str", choices=["disable", "enable"]), - wanopt_profile=dict(required=False, type="str"), - wanopt_peer=dict(required=False, type="str"), - wanopt_passive_opt=dict(required=False, type="str", choices=["default", "transparent", "non-transparent"]), - wanopt_detection=dict(required=False, type="str", choices=["active", "passive", "off"]), - wanopt=dict(required=False, type="str", choices=["disable", "enable"]), - waf_profile=dict(required=False, type="str"), - vpntunnel=dict(required=False, type="str"), - voip_profile=dict(required=False, type="str"), - vlan_filter=dict(required=False, type="str"), - vlan_cos_rev=dict(required=False, type="int"), - vlan_cos_fwd=dict(required=False, type="int"), - utm_status=dict(required=False, type="str", choices=["disable", "enable"]), - users=dict(required=False, type="str"), - url_category=dict(required=False, type="str"), - traffic_shaper_reverse=dict(required=False, type="str"), - traffic_shaper=dict(required=False, type="str"), - timeout_send_rst=dict(required=False, type="str", choices=["disable", "enable"]), - tcp_session_without_syn=dict(required=False, type="str", choices=["all", "data-only", "disable"]), - tcp_mss_sender=dict(required=False, type="int"), - tcp_mss_receiver=dict(required=False, type="int"), - status=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_ssh_profile=dict(required=False, type="str"), - ssl_mirror_intf=dict(required=False, type="str"), - ssl_mirror=dict(required=False, type="str", choices=["disable", "enable"]), - ssh_filter_profile=dict(required=False, type="str"), - srcintf=dict(required=False, type="str"), - srcaddr_negate=dict(required=False, type="str", choices=["disable", "enable"]), - srcaddr=dict(required=False, type="str"), - spamfilter_profile=dict(required=False, type="str"), - session_ttl=dict(required=False, type="int"), - service_negate=dict(required=False, type="str", choices=["disable", "enable"]), - service=dict(required=False, type="str"), - send_deny_packet=dict(required=False, type="str", choices=["disable", "enable"]), - schedule_timeout=dict(required=False, type="str", choices=["disable", "enable"]), - schedule=dict(required=False, type="str"), - scan_botnet_connections=dict(required=False, type="str", choices=["disable", "block", "monitor"]), - rtp_nat=dict(required=False, type="str", choices=["disable", "enable"]), - rtp_addr=dict(required=False, type="str"), - rsso=dict(required=False, type="str", choices=["disable", "enable"]), - replacemsg_override_group=dict(required=False, type="str"), - redirect_url=dict(required=False, type="str"), - radius_mac_auth_bypass=dict(required=False, type="str", choices=["disable", "enable"]), - profile_type=dict(required=False, type="str", choices=["single", "group"]), - profile_protocol_options=dict(required=False, type="str"), - profile_group=dict(required=False, type="str"), - poolname=dict(required=False, type="str"), - policyid=dict(required=False, type="str"), - permit_stun_host=dict(required=False, type="str", choices=["disable", "enable"]), - permit_any_host=dict(required=False, type="str", choices=["disable", "enable"]), - per_ip_shaper=dict(required=False, type="str"), - outbound=dict(required=False, type="str", choices=["disable", "enable"]), - ntlm_guest=dict(required=False, type="str", choices=["disable", "enable"]), - ntlm_enabled_browsers=dict(required=False, type="str"), - ntlm=dict(required=False, type="str", choices=["disable", "enable"]), - np_acceleration=dict(required=False, type="str", choices=["disable", "enable"]), - natoutbound=dict(required=False, type="str", choices=["disable", "enable"]), - natip=dict(required=False, type="str"), - natinbound=dict(required=False, type="str", choices=["disable", "enable"]), - nat=dict(required=False, type="str", choices=["disable", "enable"]), - name=dict(required=False, type="str"), - mms_profile=dict(required=False, type="str"), - match_vip=dict(required=False, type="str", choices=["disable", "enable"]), - logtraffic_start=dict(required=False, type="str", choices=["disable", "enable"]), - logtraffic=dict(required=False, type="str", choices=["disable", "all", "utm"]), - learning_mode=dict(required=False, type="str", choices=["disable", "enable"]), - label=dict(required=False, type="str"), - ips_sensor=dict(required=False, type="str"), - ippool=dict(required=False, type="str", choices=["disable", "enable"]), - internet_service_src_negate=dict(required=False, type="str", choices=["disable", "enable"]), - internet_service_src_id=dict(required=False, type="str"), - internet_service_src_custom=dict(required=False, type="str"), - internet_service_src=dict(required=False, type="str", choices=["disable", "enable"]), - internet_service_negate=dict(required=False, type="str", choices=["disable", "enable"]), - internet_service_id=dict(required=False, type="str"), - internet_service_custom=dict(required=False, type="str"), - internet_service=dict(required=False, type="str", choices=["disable", "enable"]), - inbound=dict(required=False, type="str", choices=["disable", "enable"]), - identity_based_route=dict(required=False, type="str"), - icap_profile=dict(required=False, type="str"), - gtp_profile=dict(required=False, type="str"), - groups=dict(required=False, type="str"), - global_label=dict(required=False, type="str"), - fsso_agent_for_ntlm=dict(required=False, type="str"), - fsso=dict(required=False, type="str", choices=["disable", "enable"]), - fixedport=dict(required=False, type="str", choices=["disable", "enable"]), - firewall_session_dirty=dict(required=False, type="str", choices=["check-all", "check-new"]), - dstintf=dict(required=False, type="str"), - dstaddr_negate=dict(required=False, type="str", choices=["disable", "enable"]), - dstaddr=dict(required=False, type="str"), - dsri=dict(required=False, type="str", choices=["disable", "enable"]), - dscp_value=dict(required=False, type="str"), - dscp_negate=dict(required=False, type="str", choices=["disable", "enable"]), - dscp_match=dict(required=False, type="str", choices=["disable", "enable"]), - dnsfilter_profile=dict(required=False, type="str"), - dlp_sensor=dict(required=False, type="str"), - disclaimer=dict(required=False, type="str", choices=["disable", "enable"]), - diffservcode_rev=dict(required=False, type="str"), - diffservcode_forward=dict(required=False, type="str"), - diffserv_reverse=dict(required=False, type="str", choices=["disable", "enable"]), - diffserv_forward=dict(required=False, type="str", choices=["disable", "enable"]), - devices=dict(required=False, type="str"), - delay_tcp_npu_session=dict(required=False, type="str", choices=["disable", "enable"]), - custom_log_fields=dict(required=False, type="str"), - comments=dict(required=False, type="str"), - capture_packet=dict(required=False, type="str", choices=["disable", "enable"]), - captive_portal_exempt=dict(required=False, type="str", choices=["disable", "enable"]), - block_notification=dict(required=False, type="str", choices=["disable", "enable"]), - av_profile=dict(required=False, type="str"), - auto_asic_offload=dict(required=False, type="str", choices=["disable", "enable"]), - auth_redirect_addr=dict(required=False, type="str"), - auth_path=dict(required=False, type="str", choices=["disable", "enable"]), - auth_cert=dict(required=False, type="str"), - application_list=dict(required=False, type="str"), - application=dict(required=False, type="str"), - app_group=dict(required=False, type="str"), - app_category=dict(required=False, type="str"), - action=dict(required=False, type="str", choices=["deny", "accept", "ipsec"]), - vpn_dst_node=dict(required=False, type="list"), - vpn_dst_node_host=dict(required=False, type="str"), - vpn_dst_node_seq=dict(required=False, type="str"), - vpn_dst_node_subnet=dict(required=False, type="str"), - vpn_src_node=dict(required=False, type="list"), - vpn_src_node_host=dict(required=False, type="str"), - vpn_src_node_seq=dict(required=False, type="str"), - vpn_src_node_subnet=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "package_name": module.params["package_name"], - "wsso": module.params["wsso"], - "webfilter-profile": module.params["webfilter_profile"], - "webcache-https": module.params["webcache_https"], - "webcache": module.params["webcache"], - "wccp": module.params["wccp"], - "wanopt-profile": module.params["wanopt_profile"], - "wanopt-peer": module.params["wanopt_peer"], - "wanopt-passive-opt": module.params["wanopt_passive_opt"], - "wanopt-detection": module.params["wanopt_detection"], - "wanopt": module.params["wanopt"], - "waf-profile": module.params["waf_profile"], - "vpntunnel": module.params["vpntunnel"], - "voip-profile": module.params["voip_profile"], - "vlan-filter": module.params["vlan_filter"], - "vlan-cos-rev": module.params["vlan_cos_rev"], - "vlan-cos-fwd": module.params["vlan_cos_fwd"], - "utm-status": module.params["utm_status"], - "users": module.params["users"], - "url-category": module.params["url_category"], - "traffic-shaper-reverse": module.params["traffic_shaper_reverse"], - "traffic-shaper": module.params["traffic_shaper"], - "timeout-send-rst": module.params["timeout_send_rst"], - "tcp-session-without-syn": module.params["tcp_session_without_syn"], - "tcp-mss-sender": module.params["tcp_mss_sender"], - "tcp-mss-receiver": module.params["tcp_mss_receiver"], - "status": module.params["status"], - "ssl-ssh-profile": module.params["ssl_ssh_profile"], - "ssl-mirror-intf": module.params["ssl_mirror_intf"], - "ssl-mirror": module.params["ssl_mirror"], - "ssh-filter-profile": module.params["ssh_filter_profile"], - "srcintf": module.params["srcintf"], - "srcaddr-negate": module.params["srcaddr_negate"], - "srcaddr": module.params["srcaddr"], - "spamfilter-profile": module.params["spamfilter_profile"], - "session-ttl": module.params["session_ttl"], - "service-negate": module.params["service_negate"], - "service": module.params["service"], - "send-deny-packet": module.params["send_deny_packet"], - "schedule-timeout": module.params["schedule_timeout"], - "schedule": module.params["schedule"], - "scan-botnet-connections": module.params["scan_botnet_connections"], - "rtp-nat": module.params["rtp_nat"], - "rtp-addr": module.params["rtp_addr"], - "rsso": module.params["rsso"], - "replacemsg-override-group": module.params["replacemsg_override_group"], - "redirect-url": module.params["redirect_url"], - "radius-mac-auth-bypass": module.params["radius_mac_auth_bypass"], - "profile-type": module.params["profile_type"], - "profile-protocol-options": module.params["profile_protocol_options"], - "profile-group": module.params["profile_group"], - "poolname": module.params["poolname"], - "policyid": module.params["policyid"], - "permit-stun-host": module.params["permit_stun_host"], - "permit-any-host": module.params["permit_any_host"], - "per-ip-shaper": module.params["per_ip_shaper"], - "outbound": module.params["outbound"], - "ntlm-guest": module.params["ntlm_guest"], - "ntlm-enabled-browsers": module.params["ntlm_enabled_browsers"], - "ntlm": module.params["ntlm"], - "np-acceleration": module.params["np_acceleration"], - "natoutbound": module.params["natoutbound"], - "natip": module.params["natip"], - "natinbound": module.params["natinbound"], - "nat": module.params["nat"], - "name": module.params["name"], - "mms-profile": module.params["mms_profile"], - "match-vip": module.params["match_vip"], - "logtraffic-start": module.params["logtraffic_start"], - "logtraffic": module.params["logtraffic"], - "learning-mode": module.params["learning_mode"], - "label": module.params["label"], - "ips-sensor": module.params["ips_sensor"], - "ippool": module.params["ippool"], - "internet-service-src-negate": module.params["internet_service_src_negate"], - "internet-service-src-id": module.params["internet_service_src_id"], - "internet-service-src-custom": module.params["internet_service_src_custom"], - "internet-service-src": module.params["internet_service_src"], - "internet-service-negate": module.params["internet_service_negate"], - "internet-service-id": module.params["internet_service_id"], - "internet-service-custom": module.params["internet_service_custom"], - "internet-service": module.params["internet_service"], - "inbound": module.params["inbound"], - "identity-based-route": module.params["identity_based_route"], - "icap-profile": module.params["icap_profile"], - "gtp-profile": module.params["gtp_profile"], - "groups": module.params["groups"], - "global-label": module.params["global_label"], - "fsso-agent-for-ntlm": module.params["fsso_agent_for_ntlm"], - "fsso": module.params["fsso"], - "fixedport": module.params["fixedport"], - "firewall-session-dirty": module.params["firewall_session_dirty"], - "dstintf": module.params["dstintf"], - "dstaddr-negate": module.params["dstaddr_negate"], - "dstaddr": module.params["dstaddr"], - "dsri": module.params["dsri"], - "dscp-value": module.params["dscp_value"], - "dscp-negate": module.params["dscp_negate"], - "dscp-match": module.params["dscp_match"], - "dnsfilter-profile": module.params["dnsfilter_profile"], - "dlp-sensor": module.params["dlp_sensor"], - "disclaimer": module.params["disclaimer"], - "diffservcode-rev": module.params["diffservcode_rev"], - "diffservcode-forward": module.params["diffservcode_forward"], - "diffserv-reverse": module.params["diffserv_reverse"], - "diffserv-forward": module.params["diffserv_forward"], - "devices": module.params["devices"], - "delay-tcp-npu-session": module.params["delay_tcp_npu_session"], - "custom-log-fields": module.params["custom_log_fields"], - "comments": module.params["comments"], - "capture-packet": module.params["capture_packet"], - "captive-portal-exempt": module.params["captive_portal_exempt"], - "block-notification": module.params["block_notification"], - "av-profile": module.params["av_profile"], - "auto-asic-offload": module.params["auto_asic_offload"], - "auth-redirect-addr": module.params["auth_redirect_addr"], - "auth-path": module.params["auth_path"], - "auth-cert": module.params["auth_cert"], - "application-list": module.params["application_list"], - "application": module.params["application"], - "app-group": module.params["app_group"], - "app-category": module.params["app_category"], - "action": module.params["action"], - "vpn_dst_node": { - "host": module.params["vpn_dst_node_host"], - "seq": module.params["vpn_dst_node_seq"], - "subnet": module.params["vpn_dst_node_subnet"], - }, - "vpn_src_node": { - "host": module.params["vpn_src_node_host"], - "seq": module.params["vpn_src_node_seq"], - "subnet": module.params["vpn_src_node_subnet"], - } - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['vpn_dst_node', 'vpn_src_node'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - # BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION - results = DEFAULT_RESULT_OBJ - try: - if paramgram["mode"] == "delete": - # WE NEED TO GET THE POLICY ID FROM THE NAME OF THE POLICY TO DELETE IT - url = '/pm/config/adom/{adom}/pkg/{pkg}/firewall' \ - '/policy/'.format(adom=paramgram["adom"], - pkg=paramgram["package_name"]) - datagram = { - "filter": ["name", "==", paramgram["name"]] - } - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - try: - if response[1][0]["policyid"]: - policy_id = response[1][0]["policyid"] - paramgram["policyid"] = policy_id - except BaseException: - fmgr.return_response(module=module, results=response, good_codes=[0, ], stop_on_success=True, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram), - msg="Couldn't find policy ID number for policy name specified.") - except Exception as err: - raise FMGBaseException(err) - - try: - results = fmgr_firewall_policy_modify(fmgr, paramgram) - if module.params["fail_on_missing_dependency"] == "disable": - fmgr.govern_response(module=module, results=results, good_codes=[0, -9998], - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - if module.params["fail_on_missing_dependency"] == "enable" and results[0] == -10131: - fmgr.govern_response(module=module, results=results, good_codes=[0, ], failed=True, skipped=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_fwpol_package.py b/plugins/modules/network/fortimanager/fmgr_fwpol_package.py deleted file mode 100644 index d70b9b9fd8..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_fwpol_package.py +++ /dev/null @@ -1,485 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_fwpol_package -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manages FortiManager Firewall Policies Packages. -description: - - Manages FortiManager Firewall Policies Packages. Policy Packages contain one or more Firewall Policies/Rules and - are distritbuted via FortiManager to Fortigates. - - This module controls the creation/edit/delete/assign of these packages. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - choices: ['add', 'set', 'delete'] - default: add - - name: - description: - - Name of the FortiManager package or folder. - required: True - - object_type: - description: - - Are we managing packages or folders, or installing packages? - required: True - choices: ['pkg','folder','install'] - - package_folder: - description: - - Name of the folder you want to put the package into. - required: false - - central_nat: - description: - - Central NAT setting. - required: false - choices: ['enable', 'disable'] - default: disable - - fwpolicy_implicit_log: - description: - - Implicit Log setting for all IPv4 policies in package. - required: false - choices: ['enable', 'disable'] - default: disable - - fwpolicy6_implicit_log: - description: - - Implicit Log setting for all IPv6 policies in package. - required: false - choices: ['enable', 'disable'] - default: disable - - inspection_mode: - description: - - Inspection mode setting for the policies flow or proxy. - required: false - choices: ['flow', 'proxy'] - default: flow - - ngfw_mode: - description: - - NGFW mode setting for the policies flow or proxy. - required: false - choices: ['profile-based', 'policy-based'] - default: profile-based - - ssl_ssh_profile: - description: - - if policy-based ngfw-mode, refer to firewall ssl-ssh-profile. - required: false - - scope_members: - description: - - The devices or scope that you want to assign this policy package to. - required: false - - scope_members_vdom: - description: - - The members VDOM you want to assign the package to. - required: false - default: root - - parent_folder: - description: - - The parent folder name you want to add this object under. - required: false - -''' - - -EXAMPLES = ''' -- name: CREATE BASIC POLICY PACKAGE - fmgr_fwpol_package: - adom: "ansible" - mode: "add" - name: "testPackage" - object_type: "pkg" - -- name: ADD PACKAGE WITH TARGETS - fmgr_fwpol_package: - mode: "add" - adom: "ansible" - name: "ansibleTestPackage1" - object_type: "pkg" - inspection_mode: "flow" - ngfw_mode: "profile-based" - scope_members: "seattle-fgt02, seattle-fgt03" - -- name: ADD FOLDER - fmgr_fwpol_package: - mode: "add" - adom: "ansible" - name: "ansibleTestFolder1" - object_type: "folder" - -- name: ADD PACKAGE INTO PARENT FOLDER - fmgr_fwpol_package: - mode: "set" - adom: "ansible" - name: "ansibleTestPackage2" - object_type: "pkg" - parent_folder: "ansibleTestFolder1" - -- name: ADD FOLDER INTO PARENT FOLDER - fmgr_fwpol_package: - mode: "set" - adom: "ansible" - name: "ansibleTestFolder2" - object_type: "folder" - parent_folder: "ansibleTestFolder1" - -- name: INSTALL PACKAGE - fmgr_fwpol_package: - mode: "set" - adom: "ansible" - name: "ansibleTestPackage1" - object_type: "install" - scope_members: "seattle-fgt03, seattle-fgt02" - -- name: REMOVE PACKAGE - fmgr_fwpol_package: - mode: "delete" - adom: "ansible" - name: "ansibleTestPackage1" - object_type: "pkg" - -- name: REMOVE NESTED PACKAGE - fmgr_fwpol_package: - mode: "delete" - adom: "ansible" - name: "ansibleTestPackage2" - object_type: "pkg" - parent_folder: "ansibleTestFolder1" - -- name: REMOVE NESTED FOLDER - fmgr_fwpol_package: - mode: "delete" - adom: "ansible" - name: "ansibleTestFolder2" - object_type: "folder" - parent_folder: "ansibleTestFolder1" - -- name: REMOVE FOLDER - fmgr_fwpol_package: - mode: "delete" - adom: "ansible" - name: "ansibleTestFolder1" - object_type: "folder" -''' -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods - - -def fmgr_fwpol_package(fmgr, paramgram): - """ - This function will create FMGR Firewall Policy Packages, or delete them. It is also capable of assigning packages. - This function DOES NOT install the package. See the function fmgr_fwpol_package_install() - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - if paramgram["mode"] in ['set', 'add']: - url = '/pm/pkg/adom/{adom}'.format(adom=paramgram["adom"]) - members_list = [] - - # CHECK FOR SCOPE MEMBERS AND CREATE THAT DICT - if paramgram["scope_members"] is not None: - members = FMGRCommon.split_comma_strings_into_lists(paramgram["scope_members"]) - for member in members: - scope_dict = { - "name": member, - "vdom": paramgram["scope_members_vdom"], - } - members_list.append(scope_dict) - - # IF PARENT FOLDER IS NOT DEFINED - if paramgram["parent_folder"] is None: - datagram = { - "type": paramgram["object_type"], - "name": paramgram["name"], - "scope member": members_list, - "package settings": { - "central-nat": paramgram["central-nat"], - "fwpolicy-implicit-log": paramgram["fwpolicy-implicit-log"], - "fwpolicy6-implicit-log": paramgram["fwpolicy6-implicit-log"], - "inspection-mode": paramgram["inspection-mode"], - "ngfw-mode": paramgram["ngfw-mode"], - } - } - - if paramgram["ngfw-mode"] == "policy-based" and paramgram["ssl-ssh-profile"] is not None: - datagram["package settings"]["ssl-ssh-profile"] = paramgram["ssl-ssh-profile"] - - # IF PARENT FOLDER IS DEFINED - if paramgram["parent_folder"] is not None: - datagram = { - "type": "folder", - "name": paramgram["parent_folder"], - "subobj": [{ - "name": paramgram["name"], - "scope member": members_list, - "type": "pkg", - "package settings": { - "central-nat": paramgram["central-nat"], - "fwpolicy-implicit-log": paramgram["fwpolicy-implicit-log"], - "fwpolicy6-implicit-log": paramgram["fwpolicy6-implicit-log"], - "inspection-mode": paramgram["inspection-mode"], - "ngfw-mode": paramgram["ngfw-mode"], - } - }] - } - - # NORMAL DELETE NO PARENT - if paramgram["mode"] == "delete" and paramgram["parent_folder"] is None: - datagram = { - "name": paramgram["name"] - } - # SET DELETE URL - url = '/pm/pkg/adom/{adom}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) - - # DELETE WITH PARENT - if paramgram["mode"] == "delete" and paramgram["parent_folder"] is not None: - datagram = { - "name": paramgram["name"] - } - # SET DELETE URL - url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}'.format(adom=paramgram["adom"], - name=paramgram["name"], - parent_folder=paramgram["parent_folder"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def fmgr_fwpol_package_folder(fmgr, paramgram): - """ - This function will create folders for firewall packages. It can create down to two levels deep. - We haven't yet tested for any more layers below two levels. - parent_folders for multiple levels may need to defined as "level1/level2/level3" for the URL parameters and such. - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - if paramgram["mode"] in ['set', 'add']: - url = '/pm/pkg/adom/{adom}'.format(adom=paramgram["adom"]) - # IF PARENT FOLDER IS NOT DEFINED - if paramgram["parent_folder"] is None: - datagram = { - "type": paramgram["object_type"], - "name": paramgram["name"], - } - - # IF PARENT FOLDER IS DEFINED - if paramgram["parent_folder"] is not None: - datagram = { - "type": paramgram["object_type"], - "name": paramgram["parent_folder"], - "subobj": [{ - "name": paramgram["name"], - "type": paramgram["object_type"], - - }] - } - # NORMAL DELETE NO PARENT - if paramgram["mode"] == "delete" and paramgram["parent_folder"] is None: - datagram = { - "name": paramgram["name"] - } - # SET DELETE URL - url = '/pm/pkg/adom/{adom}/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) - - # DELETE WITH PARENT - if paramgram["mode"] == "delete" and paramgram["parent_folder"] is not None: - datagram = { - "name": paramgram["name"] - } - # SET DELETE URL - url = '/pm/pkg/adom/{adom}/{parent_folder}/{name}'.format(adom=paramgram["adom"], - name=paramgram["name"], - parent_folder=paramgram["parent_folder"]) - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -def fmgr_fwpol_package_install(fmgr, paramgram): - """ - This method/function installs FMGR FW Policy Packages to the scope members defined in the playbook. - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - # INIT BLANK MEMBERS LIST - members_list = [] - # USE THE PARSE CSV FUNCTION TO GET A LIST FORMAT OF THE MEMBERS - members = FMGRCommon.split_comma_strings_into_lists(paramgram["scope_members"]) - # USE THAT LIST TO BUILD THE DICTIONARIES NEEDED, AND ADD TO THE BLANK MEMBERS LIST - for member in members: - scope_dict = { - "name": member, - "vdom": paramgram["scope_members_vdom"], - } - members_list.append(scope_dict) - # THEN FOR THE DATAGRAM, USING THE MEMBERS LIST CREATED ABOVE - datagram = { - "adom": paramgram["adom"], - "pkg": paramgram["name"], - "scope": members_list - } - # EXECUTE THE INSTALL REQUEST - url = '/securityconsole/install/package' - response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) - return response - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - mode=dict(choices=["add", "set", "delete"], type="str", default="add"), - - name=dict(required=False, type="str"), - object_type=dict(required=True, type="str", choices=['pkg', 'folder', 'install']), - package_folder=dict(required=False, type="str"), - central_nat=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), - fwpolicy_implicit_log=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), - fwpolicy6_implicit_log=dict(required=False, type="str", default="disable", choices=['enable', 'disable']), - inspection_mode=dict(required=False, type="str", default="flow", choices=['flow', 'proxy']), - ngfw_mode=dict(required=False, type="str", default="profile-based", choices=['profile-based', 'policy-based']), - ssl_ssh_profile=dict(required=False, type="str"), - scope_members=dict(required=False, type="str"), - scope_members_vdom=dict(required=False, type="str", default="root"), - parent_folder=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE DATAGRAM - paramgram = { - "adom": module.params["adom"], - "name": module.params["name"], - "mode": module.params["mode"], - "object_type": module.params["object_type"], - "package-folder": module.params["package_folder"], - "central-nat": module.params["central_nat"], - "fwpolicy-implicit-log": module.params["fwpolicy_implicit_log"], - "fwpolicy6-implicit-log": module.params["fwpolicy6_implicit_log"], - "inspection-mode": module.params["inspection_mode"], - "ngfw-mode": module.params["ngfw_mode"], - "ssl-ssh-profile": module.params["ssl_ssh_profile"], - "scope_members": module.params["scope_members"], - "scope_members_vdom": module.params["scope_members_vdom"], - "parent_folder": module.params["parent_folder"], - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - # BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION - results = DEFAULT_RESULT_OBJ - - try: - if paramgram["object_type"] == "pkg": - results = fmgr_fwpol_package(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # IF THE object_type IS FOLDER LETS RUN THAT METHOD - if paramgram["object_type"] == "folder": - results = fmgr_fwpol_package_folder(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - try: - # IF THE object_type IS INSTALL AND NEEDED PARAMETERS ARE DEFINED INSTALL THE PACKAGE - if paramgram["scope_members"] is not None and paramgram["name"] is not None and\ - paramgram["object_type"] == "install": - results = fmgr_fwpol_package_install(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_ha.py b/plugins/modules/network/fortimanager/fmgr_ha.py deleted file mode 100644 index cac6697453..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_ha.py +++ /dev/null @@ -1,355 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_ha -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manages the High-Availability State of FortiManager Clusters and Nodes. -description: Change HA state or settings of FortiManager nodes (Standalone/Master/Slave). - -options: - fmgr_ha_mode: - description: - - Sets the role of the FortiManager host for HA. - required: false - choices: ["standalone", "master", "slave"] - - fmgr_ha_peer_ipv4: - description: - - Sets the IPv4 address of a HA peer. - required: false - - fmgr_ha_peer_ipv6: - description: - - Sets the IPv6 address of a HA peer. - required: false - - fmgr_ha_peer_sn: - description: - - Sets the HA Peer Serial Number. - required: false - - fmgr_ha_peer_status: - description: - - Sets the peer status to enable or disable. - required: false - choices: ["enable", "disable"] - - fmgr_ha_cluster_pw: - description: - - Sets the password for the HA cluster. Only required once. System remembers between HA mode switches. - required: false - - fmgr_ha_cluster_id: - description: - - Sets the ID number of the HA cluster. Defaults to 1. - required: false - default: 1 - - fmgr_ha_hb_threshold: - description: - - Sets heartbeat lost threshold (1-255). - required: false - default: 3 - - fmgr_ha_hb_interval: - description: - - Sets the heartbeat interval (1-255). - required: false - default: 5 - - fmgr_ha_file_quota: - description: - - Sets the File quota in MB (2048-20480). - required: false - default: 4096 -''' - - -EXAMPLES = ''' -- name: SET FORTIMANAGER HA NODE TO MASTER - fmgr_ha: - fmgr_ha_mode: "master" - fmgr_ha_cluster_pw: "fortinet" - fmgr_ha_cluster_id: "1" - -- name: SET FORTIMANAGER HA NODE TO SLAVE - fmgr_ha: - fmgr_ha_mode: "slave" - fmgr_ha_cluster_pw: "fortinet" - fmgr_ha_cluster_id: "1" - -- name: SET FORTIMANAGER HA NODE TO STANDALONE - fmgr_ha: - fmgr_ha_mode: "standalone" - -- name: ADD FORTIMANAGER HA PEER - fmgr_ha: - fmgr_ha_peer_ipv4: "192.168.1.254" - fmgr_ha_peer_sn: "FMG-VM1234567890" - fmgr_ha_peer_status: "enable" - -- name: CREATE CLUSTER ON MASTER - fmgr_ha: - fmgr_ha_mode: "master" - fmgr_ha_cluster_pw: "fortinet" - fmgr_ha_cluster_id: "1" - fmgr_ha_hb_threshold: "10" - fmgr_ha_hb_interval: "15" - fmgr_ha_file_quota: "2048" -''' -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG - - -def fmgr_set_ha_mode(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - if paramgram["fmgr_ha_cluster_pw"] is not None and str(paramgram["fmgr_ha_mode"].lower()) != "standalone": - datagram = { - "mode": paramgram["fmgr_ha_mode"], - "file-quota": paramgram["fmgr_ha_file_quota"], - "hb-interval": paramgram["fmgr_ha_hb_interval"], - "hb-lost-threshold": paramgram["fmgr_ha_hb_threshold"], - "password": paramgram["fmgr_ha_cluster_pw"], - "clusterid": paramgram["fmgr_ha_cluster_id"] - } - elif str(paramgram["fmgr_ha_mode"].lower()) == "standalone": - datagram = { - "mode": paramgram["fmgr_ha_mode"], - "file-quota": paramgram["fmgr_ha_file_quota"], - "hb-interval": paramgram["fmgr_ha_hb_interval"], - "hb-lost-threshold": paramgram["fmgr_ha_hb_threshold"], - "clusterid": paramgram["fmgr_ha_cluster_id"] - } - - url = '/cli/global/system/ha' - response = fmgr.process_request(url, datagram, FMGRMethods.SET) - return response - - -def fmgr_get_ha_peer_list(fmgr): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - - datagram = {} - paramgram = {} - - url = '/cli/global/system/ha/peer/' - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - return response - - -def fmgr_set_ha_peer(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - datagram = { - "ip": paramgram["fmgr_ha_peer_ipv4"], - "ip6": paramgram["fmgr_ha_peer_ipv6"], - "serial-number": paramgram["fmgr_ha_peer_sn"], - "status": paramgram["fmgr_ha_peer_status"], - "id": paramgram["peer_id"] - } - - url = '/cli/global/system/ha/peer/' - response = fmgr.process_request(url, datagram, FMGRMethods.SET) - return response - - -def main(): - argument_spec = dict( - fmgr_ha_mode=dict(required=False, type="str", choices=["standalone", "master", "slave"]), - fmgr_ha_cluster_pw=dict(required=False, type="str", no_log=True), - fmgr_ha_peer_status=dict(required=False, type="str", choices=["enable", "disable"]), - fmgr_ha_peer_sn=dict(required=False, type="str"), - fmgr_ha_peer_ipv4=dict(required=False, type="str"), - fmgr_ha_peer_ipv6=dict(required=False, type="str"), - fmgr_ha_hb_threshold=dict(required=False, type="int", default=3), - fmgr_ha_hb_interval=dict(required=False, type="int", default=5), - fmgr_ha_file_quota=dict(required=False, type="int", default=4096), - fmgr_ha_cluster_id=dict(required=False, type="int", default=1) - ) - - required_if = [ - ['fmgr_ha_peer_ipv4', 'present', ['fmgr_ha_peer_sn', 'fmgr_ha_peer_status']], - ['fmgr_ha_peer_ipv6', 'present', ['fmgr_ha_peer_sn', 'fmgr_ha_peer_status']], - ['fmgr_ha_mode', 'master', ['fmgr_ha_cluster_pw', 'fmgr_ha_cluster_id']], - ['fmgr_ha_mode', 'slave', ['fmgr_ha_cluster_pw', 'fmgr_ha_cluster_id']], - ] - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, required_if=required_if) - paramgram = { - "fmgr_ha_mode": module.params["fmgr_ha_mode"], - "fmgr_ha_cluster_pw": module.params["fmgr_ha_cluster_pw"], - "fmgr_ha_peer_status": module.params["fmgr_ha_peer_status"], - "fmgr_ha_peer_sn": module.params["fmgr_ha_peer_sn"], - "fmgr_ha_peer_ipv4": module.params["fmgr_ha_peer_ipv4"], - "fmgr_ha_peer_ipv6": module.params["fmgr_ha_peer_ipv6"], - "fmgr_ha_hb_threshold": module.params["fmgr_ha_hb_threshold"], - "fmgr_ha_hb_interval": module.params["fmgr_ha_hb_interval"], - "fmgr_ha_file_quota": module.params["fmgr_ha_file_quota"], - "fmgr_ha_cluster_id": module.params["fmgr_ha_cluster_id"], - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - # INIT FLAGS AND COUNTERS - get_ha_peers = 0 - results = DEFAULT_RESULT_OBJ - try: - if any(v is not None for v in (paramgram["fmgr_ha_peer_sn"], paramgram["fmgr_ha_peer_ipv4"], - paramgram["fmgr_ha_peer_ipv6"], paramgram["fmgr_ha_peer_status"])): - get_ha_peers = 1 - except Exception as err: - raise FMGBaseException(err) - try: - # IF HA MODE IS NOT NULL, SWITCH THAT - if paramgram["fmgr_ha_mode"] is not None: - if (str.lower(paramgram["fmgr_ha_mode"]) != "standalone" and paramgram["fmgr_ha_cluster_pw"] is not None)\ - or str.lower(paramgram["fmgr_ha_mode"]) == "standalone": - results = fmgr_set_ha_mode(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, stop_on_success=False, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - elif str.lower(paramgram["fmgr_ha_mode"]) != "standalone" and\ - paramgram["fmgr_ha_mode"] is not None and\ - paramgram["fmgr_ha_cluster_pw"] is None: - module.exit_json(msg="If setting HA Mode of MASTER or SLAVE, you must specify a cluster password") - - except Exception as err: - raise FMGBaseException(err) - # IF GET_HA_PEERS IS ENABLED, LETS PROCESS THE PEERS - try: - if get_ha_peers == 1: - # GET THE CURRENT LIST OF PEERS FROM THE NODE - peers = fmgr_get_ha_peer_list(fmgr) - # GET LENGTH OF RETURNED PEERS LIST AND ADD ONE FOR THE NEXT ID - paramgram["next_peer_id"] = len(peers[1]) + 1 - # SET THE ACTUAL NUMBER OF PEERS - num_of_peers = len(peers[1]) - # SET THE PEER ID FOR DISABLE METHOD - paramgram["peer_id"] = len(peers) - 1 - # SET THE PEER LOOPCOUNT TO 1 TO START THE LOOP - peer_loopcount = 1 - - # LOOP THROUGH PEERS TO FIND THE SERIAL NUMBER MATCH TO GET THE RIGHT PEER ID - # IDEA BEING WE DON'T WANT TO SUBMIT A BAD peer_id THAT DOESN'T JIVE WITH CURRENT DB ON FMG - # SO LETS SEARCH FOR IT, AND IF WE FIND IT, WE WILL CHANGE THE PEER ID VARIABLES TO MATCH - # IF NOT FOUND, LIFE GOES ON AND WE ASSUME THAT WE'RE ADDING A PEER - # AT WHICH POINT THE next_peer_id VARIABLE WILL HAVE THE RIGHT PRIMARY KEY - - if paramgram["fmgr_ha_peer_sn"] is not None: - while peer_loopcount <= num_of_peers: - # GET THE SERIAL NUMBER FOR CURRENT PEER IN LOOP TO COMPARE TO SN IN PLAYBOOK - try: - sn_compare = peers[1][peer_loopcount - 1]["serial-number"] - # IF THE SN IN THE PEERS MATCHES THE PLAYBOOK SN, SET THE IDS - if sn_compare == paramgram["fmgr_ha_peer_sn"]: - paramgram["peer_id"] = peer_loopcount - paramgram["next_peer_id"] = paramgram["peer_id"] - except Exception as err: - raise FMGBaseException(err) - # ADVANCE THE LOOP AND REPEAT UNTIL DONE - peer_loopcount += 1 - - # IF THE PEER STATUS ISN'T IN THE PLAYBOOK, ASSUME ITS ENABLE - if paramgram["fmgr_ha_peer_status"] is None: - paramgram["fmgr_ha_peer_status"] = "enable" - - # IF THE PEER STATUS IS ENABLE, USE THE next_peer_id IN THE API CALL FOR THE ID - if paramgram["fmgr_ha_peer_status"] == "enable": - results = fmgr_set_ha_peer(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, stop_on_success=True, - ansible_facts=fmgr.construct_ansible_facts(results, - module.params, paramgram)) - - # IF THE PEER STATUS IS DISABLE, WE HAVE TO HANDLE THAT A BIT DIFFERENTLY - # JUST USING TWO DIFFERENT peer_id 's HERE - if paramgram["fmgr_ha_peer_status"] == "disable": - results = fmgr_set_ha_peer(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, stop_on_success=True, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_provisioning.py b/plugins/modules/network/fortimanager/fmgr_provisioning.py deleted file mode 100644 index 28fa5fbabd..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_provisioning.py +++ /dev/null @@ -1,364 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_provisioning -author: Andrew Welsh (@Ghilli3) -short_description: Provision devices via FortiMananger -description: - - Add model devices on the FortiManager using jsonrpc API and have them pre-configured, - so when central management is configured, the configuration is pushed down to the - registering devices - -options: - adom: - description: - - The administrative domain (admon) the configuration belongs to - required: true - vdom: - description: - - The virtual domain (vdom) the configuration belongs to - host: - description: - - The FortiManager's Address. - required: true - username: - description: - - The username to log into the FortiManager - required: true - password: - description: - - The password associated with the username account. - required: false - - policy_package: - description: - - The name of the policy package to be assigned to the device. - required: True - name: - description: - - The name of the device to be provisioned. - required: True - group: - description: - - The name of the device group the provisioned device can belong to. - required: False - serial: - description: - - The serial number of the device that will be provisioned. - required: True - platform: - description: - - The platform of the device, such as model number or VM. - required: True - description: - description: - - Description of the device to be provisioned. - required: False - os_version: - description: - - The Fortinet OS version to be used for the device, such as 5.0 or 6.0. - required: True - minor_release: - description: - - The minor release number such as 6.X.1, as X being the minor release. - required: False - patch_release: - description: - - The patch release number such as 6.0.X, as X being the patch release. - required: False - os_type: - description: - - The Fortinet OS type to be pushed to the device, such as 'FOS' for FortiOS. - required: True -''' - -EXAMPLES = ''' -- name: Create FGT1 Model Device - fmgr_provisioning: - host: "{{ inventory_hostname }}" - username: "{{ username }}" - password: "{{ password }}" - adom: "root" - vdom: "root" - policy_package: "default" - name: "FGT1" - group: "Ansible" - serial: "FGVM000000117994" - platform: "FortiGate-VM64" - description: "Provisioned by Ansible" - os_version: '6.0' - minor_release: 0 - patch_release: 0 - os_type: 'fos' - - -- name: Create FGT2 Model Device - fmgr_provisioning: - host: "{{ inventory_hostname }}" - username: "{{ username }}" - password: "{{ password }}" - adom: "root" - vdom: "root" - policy_package: "test_pp" - name: "FGT2" - group: "Ansible" - serial: "FGVM000000117992" - platform: "FortiGate-VM64" - description: "Provisioned by Ansible" - os_version: '5.0' - minor_release: 6 - patch_release: 0 - os_type: 'fos' - -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import AnsibleFortiManager - -# check for pyFMG lib -try: - from pyFMG.fortimgr import FortiManager - HAS_PYFMGR = True -except ImportError: - HAS_PYFMGR = False - - -def dev_group_exists(fmg, dev_grp_name, adom): - datagram = { - 'adom': adom, - 'name': dev_grp_name, - } - - url = '/dvmdb/adom/{adom}/group/{dev_grp_name}'.format(adom=adom, dev_grp_name=dev_grp_name) - response = fmg.get(url, datagram) - return response - - -def prov_template_exists(fmg, prov_template, adom, vdom): - datagram = { - 'name': prov_template, - 'adom': adom, - } - - url = '/pm/devprof/adom/{adom}/devprof/{name}'.format(adom=adom, name=prov_template) - response = fmg.get(url, datagram) - return response - - -def create_model_device(fmg, name, serial, group, platform, os_version, - os_type, minor_release, patch_release=0, adom='root'): - datagram = { - 'adom': adom, - 'flags': ['create_task', 'nonblocking'], - 'groups': [{'name': group, 'vdom': 'root'}], - 'device': { - 'mr': minor_release, - 'name': name, - 'sn': serial, - 'mgmt_mode': 'fmg', - 'device action': 'add_model', - 'platform_str': platform, - 'os_ver': os_version, - 'os_type': os_type, - 'patch': patch_release, - 'desc': 'Provisioned by Ansible', - } - } - - url = '/dvm/cmd/add/device' - response = fmg.execute(url, datagram) - return response - - -def update_flags(fmg, name): - datagram = { - 'flags': ['is_model', 'linked_to_model'] - } - url = 'dvmdb/device/{name}'.format(name=name) - response = fmg.update(url, datagram) - return response - - -def assign_provision_template(fmg, template, adom, target): - datagram = { - 'name': template, - 'type': 'devprof', - 'description': 'Provisioned by Ansible', - 'scope member': [{'name': target}] - } - url = "/pm/devprof/adom/{adom}".format(adom=adom) - response = fmg.update(url, datagram) - return response - - -def set_devprof_scope(self, provisioning_template, adom, provision_targets): - """ - GET the DevProf (check to see if exists) - """ - fields = dict() - targets = [] - fields["name"] = provisioning_template - fields["type"] = "devprof" - fields["description"] = "CreatedByAnsible" - - for target in provision_targets.strip().split(","): - # split the host on the space to get the mask out - new_target = {"name": target} - targets.append(new_target) - - fields["scope member"] = targets - - body = {"method": "set", "params": [{"url": "/pm/devprof/adom/{adom}".format(adom=adom), - "data": fields, "session": self.session}]} - response = self.make_request(body).json() - return response - - -def assign_dev_grp(fmg, grp_name, device_name, vdom, adom): - datagram = { - 'name': device_name, - 'vdom': vdom, - } - url = "/dvmdb/adom/{adom}/group/{grp_name}/object member".format(adom=adom, grp_name=grp_name) - response = fmg.set(url, datagram) - return response - - -def update_install_target(fmg, device, pp='default', vdom='root', adom='root'): - datagram = { - 'scope member': [{'name': device, 'vdom': vdom}], - 'type': 'pkg' - } - url = '/pm/pkg/adom/{adom}/{pkg_name}'.format(adom=adom, pkg_name=pp) - response = fmg.update(url, datagram) - return response - - -def install_pp(fmg, device, pp='default', vdom='root', adom='root'): - datagram = { - 'adom': adom, - 'flags': 'nonblocking', - 'pkg': pp, - 'scope': [{'name': device, 'vdom': vdom}], - } - url = 'securityconsole/install/package' - response = fmg.execute(url, datagram) - return response - - -def main(): - - argument_spec = dict( - adom=dict(required=False, type="str"), - vdom=dict(required=False, type="str"), - host=dict(required=True, type="str"), - password=dict(fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True), - username=dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"]), no_log=True), - - policy_package=dict(required=False, type="str"), - name=dict(required=False, type="str"), - group=dict(required=False, type="str"), - serial=dict(required=True, type="str"), - platform=dict(required=True, type="str"), - description=dict(required=False, type="str"), - os_version=dict(required=True, type="str"), - minor_release=dict(required=False, type="str"), - patch_release=dict(required=False, type="str"), - os_type=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec, supports_check_mode=True, ) - - # check if params are set - if module.params["host"] is None or module.params["username"] is None: - module.fail_json(msg="Host and username are required for connection") - - # check if login failed - fmg = AnsibleFortiManager(module, module.params["host"], module.params["username"], module.params["password"]) - response = fmg.login() - - if "FortiManager instance connnected" not in str(response): - module.fail_json(msg="Connection to FortiManager Failed") - else: - - if module.params["policy_package"] is None: - module.params["policy_package"] = 'default' - if module.params["adom"] is None: - module.params["adom"] = 'root' - if module.params["vdom"] is None: - module.params["vdom"] = 'root' - if module.params["platform"] is None: - module.params["platform"] = 'FortiGate-VM64' - if module.params["os_type"] is None: - module.params["os_type"] = 'fos' - - results = create_model_device(fmg, - module.params["name"], - module.params["serial"], - module.params["group"], - module.params["platform"], - module.params["os_ver"], - module.params["os_type"], - module.params["minor_release"], - module.params["patch_release"], - module.params["adom"]) - if results[0] != 0: - module.fail_json(msg="Create model failed", **results) - - results = update_flags(fmg, module.params["name"]) - if results[0] != 0: - module.fail_json(msg="Update device flags failed", **results) - - # results = assign_dev_grp(fmg, 'Ansible', 'FGVM000000117992', 'root', 'root') - # if not results[0] == 0: - # module.fail_json(msg="Setting device group failed", **results) - - results = update_install_target(fmg, module.params["name"], module.params["policy_package"]) - if results[0] != 0: - module.fail_json(msg="Adding device target to package failed", **results) - - results = install_pp(fmg, module.params["name"], module.params["policy_package"]) - if results[0] != 0: - module.fail_json(msg="Installing policy package failed", **results) - - fmg.logout() - - # results is returned as a tuple - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_query.py b/plugins/modules/network/fortimanager/fmgr_query.py deleted file mode 100644 index d87be8b474..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_query.py +++ /dev/null @@ -1,430 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community" -} - -DOCUMENTATION = ''' ---- -module: fmgr_query -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: Luke Weighall (@lweighall) -short_description: Query FortiManager data objects for use in Ansible workflows. -description: - - Provides information on data objects within FortiManager so that playbooks can perform conditionals. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - object: - description: - - The data object we wish to query (device, package, rule, etc). Will expand choices as improves. - required: true - choices: - - device - - cluster_nodes - - task - - custom - - custom_endpoint: - description: - - ADVANCED USERS ONLY! REQUIRES KNOWLEDGE OF FMGR JSON API! - - The HTTP Endpoint on FortiManager you wish to GET from. - required: false - - custom_dict: - description: - - ADVANCED USERS ONLY! REQUIRES KNOWLEDGE OF FMGR JSON API! - - DICTIONARY JSON FORMAT ONLY -- Custom dictionary/datagram to send to the endpoint. - required: false - - device_ip: - description: - - The IP of the device you want to query. - required: false - - device_unique_name: - description: - - The desired "friendly" name of the device you want to query. - required: false - - device_serial: - description: - - The serial number of the device you want to query. - required: false - - task_id: - description: - - The ID of the task you wish to query status on. If left blank and object = 'task' a list of tasks are returned. - required: false - - nodes: - description: - - A LIST of firewalls in the cluster you want to verify i.e. ["firewall_A","firewall_B"]. - required: false -''' - - -EXAMPLES = ''' -- name: QUERY FORTIGATE DEVICE BY IP - fmgr_query: - object: "device" - adom: "ansible" - device_ip: "10.7.220.41" - -- name: QUERY FORTIGATE DEVICE BY SERIAL - fmgr_query: - adom: "ansible" - object: "device" - device_serial: "FGVM000000117992" - -- name: QUERY FORTIGATE DEVICE BY FRIENDLY NAME - fmgr_query: - adom: "ansible" - object: "device" - device_unique_name: "ansible-fgt01" - -- name: VERIFY CLUSTER MEMBERS AND STATUS - fmgr_query: - adom: "ansible" - object: "cluster_nodes" - device_unique_name: "fgt-cluster01" - nodes: ["ansible-fgt01", "ansible-fgt02", "ansible-fgt03"] - -- name: GET STATUS OF TASK ID - fmgr_query: - adom: "ansible" - object: "task" - task_id: "3" - -- name: USE CUSTOM TYPE TO QUERY AVAILABLE SCRIPTS - fmgr_query: - adom: "ansible" - object: "custom" - custom_endpoint: "/dvmdb/adom/ansible/script" - custom_dict: { "type": "cli" } -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG - - -def fmgr_get_custom(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # IF THE CUSTOM DICTIONARY (OFTEN CONTAINING FILTERS) IS DEFINED CREATED THAT - if paramgram["custom_dict"] is not None: - datagram = paramgram["custom_dict"] - else: - datagram = dict() - - # SET THE CUSTOM ENDPOINT PROVIDED - url = paramgram["custom_endpoint"] - # MAKE THE CALL AND RETURN RESULTS - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - return response - - -def fmgr_get_task_status(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # IF THE TASK_ID IS DEFINED, THEN GET THAT SPECIFIC TASK - # OTHERWISE, GET ALL RECENT TASKS IN A LIST - if paramgram["task_id"] is not None: - - datagram = { - "adom": paramgram["adom"] - } - url = '/task/task/{task_id}'.format(task_id=paramgram["task_id"]) - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - else: - datagram = { - "adom": paramgram["adom"] - } - url = '/task/task' - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - return response - - -def fmgr_get_device(fmgr, paramgram): - """ - This method is used to get information on devices. This will not work on HA_SLAVE nodes, only top level devices. - Such as cluster objects and standalone devices. - - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - # FIRST TRY TO RUN AN UPDATE ON THE DEVICE - # RUN A QUICK CLUSTER REFRESH/UPDATE ATTEMPT TO ENSURE WE'RE GETTING THE LATEST INFORMOATION - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - update_url = '/dvm/cmd/update/device' - update_dict = { - "adom": paramgram['adom'], - "device": paramgram['device_unique_name'], - "flags": "create_task" - } - # DO THE UPDATE CALL - fmgr.process_request(update_url, update_dict, FMGRMethods.EXEC) - - # SET THE URL - url = '/dvmdb/adom/{adom}/device'.format(adom=paramgram["adom"]) - device_found = 0 - response = [] - - # TRY TO FIND IT FIRST BY SERIAL NUMBER - if paramgram["device_serial"] is not None: - datagram = { - "filter": ["sn", "==", paramgram["device_serial"]] - } - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - if len(response[1]) >= 0: - device_found = 1 - - # CHECK IF ANYTHING WAS RETURNED, IF NOT TRY DEVICE NAME PARAMETER - if device_found == 0 and paramgram["device_unique_name"] is not None: - datagram = { - "filter": ["name", "==", paramgram["device_unique_name"]] - } - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - if len(response[1]) >= 0: - device_found = 1 - - # CHECK IF ANYTHING WAS RETURNED, IF NOT TRY DEVICE IP ADDRESS - if device_found == 0 and paramgram["device_ip"] is not None: - datagram = { - "filter": ["ip", "==", paramgram["device_ip"]] - } - response = fmgr.process_request(url, datagram, FMGRMethods.GET) - if len(response[1]) >= 0: - device_found = 1 - - return response - - -def fmgr_get_cluster_nodes(fmgr, paramgram): - """ - This method is used to get information on devices. This WILL work on HA_SLAVE nodes, but NOT top level standalone - devices. - Such as cluster objects and standalone devices. - - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - # USE THE DEVICE METHOD TO GET THE CLUSTER INFORMATION SO WE CAN SEE THE HA_SLAVE NODES - response = fmgr_get_device(fmgr, paramgram) - # CHECK FOR HA_SLAVE NODES, IF CLUSTER IS MISSING COMPLETELY THEN QUIT - try: - returned_nodes = response[1][0]["ha_slave"] - num_of_nodes = len(returned_nodes) - except Exception: - error_msg = {"cluster_status": "MISSING"} - return error_msg - - # INIT LOOP RESOURCES - loop_count = 0 - good_nodes = [] - expected_nodes = list(paramgram["nodes"]) - missing_nodes = list(paramgram["nodes"]) - bad_status_nodes = [] - - # LOOP THROUGH THE NODES AND GET THEIR STATUS TO BUILD THE RETURN JSON OBJECT - # WE'RE ALSO CHECKING THE NODES IF THEY ARE BAD STATUS, OR PLAIN MISSING - while loop_count < num_of_nodes: - node_append = { - "node_name": returned_nodes[loop_count]["name"], - "node_serial": returned_nodes[loop_count]["sn"], - "node_parent": returned_nodes[loop_count]["did"], - "node_status": returned_nodes[loop_count]["status"], - } - # IF THE NODE IS IN THE EXPECTED NODES LIST AND WORKING THEN ADD IT TO GOOD NODES LIST - if node_append["node_name"] in expected_nodes and node_append["node_status"] == 1: - good_nodes.append(node_append["node_name"]) - # IF THE NODE IS IN THE EXPECTED NODES LIST BUT NOT WORKING THEN ADDED IT TO BAD_STATUS_NODES - # IF THE NODE STATUS IS NOT 1 THEN ITS BAD - if node_append["node_name"] in expected_nodes and node_append["node_status"] != 1: - bad_status_nodes.append(node_append["node_name"]) - # REMOVE THE NODE FROM MISSING NODES LIST IF NOTHING IS WRONG WITH NODE -- LEAVING US A LIST OF - # NOT WORKING NODES - missing_nodes.remove(node_append["node_name"]) - loop_count += 1 - - # BUILD RETURN OBJECT FROM NODE LISTS - nodes = { - "good_nodes": good_nodes, - "expected_nodes": expected_nodes, - "missing_nodes": missing_nodes, - "bad_nodes": bad_status_nodes, - "query_status": "good", - } - if len(nodes["good_nodes"]) == len(nodes["expected_nodes"]): - nodes["cluster_status"] = "OK" - else: - nodes["cluster_status"] = "NOT-COMPLIANT" - return nodes - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - object=dict(required=True, type="str", choices=["device", "cluster_nodes", "task", "custom"]), - custom_endpoint=dict(required=False, type="str"), - custom_dict=dict(required=False, type="dict"), - device_ip=dict(required=False, type="str"), - device_unique_name=dict(required=False, type="str"), - device_serial=dict(required=False, type="str"), - nodes=dict(required=False, type="list"), - task_id=dict(required=False, type="str") - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - paramgram = { - "adom": module.params["adom"], - "object": module.params["object"], - "device_ip": module.params["device_ip"], - "device_unique_name": module.params["device_unique_name"], - "device_serial": module.params["device_serial"], - "nodes": module.params["nodes"], - "task_id": module.params["task_id"], - "custom_endpoint": module.params["custom_endpoint"], - "custom_dict": module.params["custom_dict"] - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - results = DEFAULT_RESULT_OBJ - - try: - # IF OBJECT IS DEVICE - if paramgram["object"] == "device" and any(v is not None for v in [paramgram["device_unique_name"], - paramgram["device_serial"], - paramgram["device_ip"]]): - results = fmgr_get_device(fmgr, paramgram) - if results[0] not in [0]: - module.fail_json(msg="Device query failed!") - elif len(results[1]) == 0: - module.exit_json(msg="Device NOT FOUND!") - else: - module.exit_json(msg="Device Found", **results[1][0]) - except Exception as err: - raise FMGBaseException(err) - - try: - # IF OBJECT IS CLUSTER_NODES - if paramgram["object"] == "cluster_nodes" and paramgram["nodes"] is not None: - results = fmgr_get_cluster_nodes(fmgr, paramgram) - if results["cluster_status"] == "MISSING": - module.exit_json(msg="No cluster device found!", **results) - elif results["query_status"] == "good": - module.exit_json(msg="Cluster Found - Showing Nodes", **results) - elif results is None: - module.fail_json(msg="Query FAILED -- Check module or playbook syntax") - except Exception as err: - raise FMGBaseException(err) - - try: - # IF OBJECT IS TASK - if paramgram["object"] == "task": - results = fmgr_get_task_status(fmgr, paramgram) - if results[0] != 0: - module.fail_json(**results[1]) - if results[0] == 0: - module.exit_json(**results[1]) - except Exception as err: - raise FMGBaseException(err) - - try: - # IF OBJECT IS CUSTOM - if paramgram["object"] == "custom": - results = fmgr_get_custom(fmgr, paramgram) - if results[0] != 0: - module.fail_json(msg="QUERY FAILED -- Please check syntax check JSON guide if needed.") - if results[0] == 0: - results_len = len(results[1]) - if results_len > 0: - results_combine = dict() - if isinstance(results[1], dict): - results_combine["results"] = results[1] - if isinstance(results[1], list): - results_combine["results"] = results[1][0:results_len] - module.exit_json(msg="Custom Query Success", **results_combine) - else: - module.exit_json(msg="NO RESULTS") - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_script.py b/plugins/modules/network/fortimanager/fmgr_script.py deleted file mode 100644 index 0a23480715..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_script.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_script -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: Andrew Welsh (@Ghilli3) -short_description: Add/Edit/Delete and execute scripts -description: Create/edit/delete scripts and execute the scripts on the FortiManager using jsonrpc API - -options: - adom: - description: - - The administrative domain (admon) the configuration belongs to - required: true - - vdom: - description: - - The virtual domain (vdom) the configuration belongs to - - mode: - description: - - The desired mode of the specified object. Execute will run the script. - required: false - default: "add" - choices: ["add", "delete", "execute", "set"] - - script_name: - description: - - The name of the script. - required: True - - script_type: - description: - - The type of script (CLI or TCL). - required: false - - script_target: - description: - - The target of the script to be run. - required: false - - script_description: - description: - - The description of the script. - required: false - - script_content: - description: - - The script content that will be executed. - required: false - - script_scope: - description: - - (datasource) The devices that the script will run on, can have both device member and device group member. - required: false - - script_package: - description: - - (datasource) Policy package object to run the script against - required: false -''' - -EXAMPLES = ''' -- name: CREATE SCRIPT - fmgr_script: - adom: "root" - script_name: "TestScript" - script_type: "cli" - script_target: "remote_device" - script_description: "Create by Ansible" - script_content: "get system status" - -- name: EXECUTE SCRIPT - fmgr_script: - adom: "root" - script_name: "TestScript" - mode: "execute" - script_scope: "FGT1,FGT2" - -- name: DELETE SCRIPT - fmgr_script: - adom: "root" - script_name: "TestScript" - mode: "delete" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG - - -def set_script(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - datagram = { - 'content': paramgram["script_content"], - 'desc': paramgram["script_description"], - 'name': paramgram["script_name"], - 'target': paramgram["script_target"], - 'type': paramgram["script_type"], - } - - url = '/dvmdb/adom/{adom}/script/'.format(adom=paramgram["adom"]) - response = fmgr.process_request(url, datagram, FMGRMethods.SET) - return response - - -def delete_script(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - datagram = { - 'name': paramgram["script_name"], - } - - url = '/dvmdb/adom/{adom}/script/{script_name}'.format(adom=paramgram["adom"], script_name=paramgram["script_name"]) - response = fmgr.process_request(url, datagram, FMGRMethods.DELETE) - return response - - -def execute_script(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - scope_list = list() - scope = paramgram["script_scope"].replace(' ', '') - scope = scope.split(',') - for dev_name in scope: - scope_list.append({'name': dev_name, 'vdom': paramgram["vdom"]}) - - datagram = { - 'adom': paramgram["adom"], - 'script': paramgram["script_name"], - 'package': paramgram["script_package"], - 'scope': scope_list, - } - - url = '/dvmdb/adom/{adom}/script/execute'.format(adom=paramgram["adom"]) - response = fmgr.process_request(url, datagram, FMGRMethods.EXEC) - return response - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - vdom=dict(required=False, type="str", default="root"), - mode=dict(choices=["add", "execute", "set", "delete"], type="str", default="add"), - script_name=dict(required=True, type="str"), - script_type=dict(required=False, type="str"), - script_target=dict(required=False, type="str"), - script_description=dict(required=False, type="str"), - script_content=dict(required=False, type="str"), - script_scope=dict(required=False, type="str"), - script_package=dict(required=False, type="str"), - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - paramgram = { - "script_name": module.params["script_name"], - "script_type": module.params["script_type"], - "script_target": module.params["script_target"], - "script_description": module.params["script_description"], - "script_content": module.params["script_content"], - "script_scope": module.params["script_scope"], - "script_package": module.params["script_package"], - "adom": module.params["adom"], - "vdom": module.params["vdom"], - "mode": module.params["mode"], - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - results = DEFAULT_RESULT_OBJ - - try: - if paramgram["mode"] in ['add', 'set']: - results = set_script(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, msg="Operation Finished", - ansible_facts=fmgr.construct_ansible_facts(results, module.params, module.params)) - except Exception as err: - raise FMGBaseException(err) - - try: - if paramgram["mode"] == "execute": - results = execute_script(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, msg="Operation Finished", - ansible_facts=fmgr.construct_ansible_facts(results, module.params, module.params)) - except Exception as err: - raise FMGBaseException(err) - - try: - if paramgram["mode"] == "delete": - results = delete_script(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, msg="Operation Finished", - ansible_facts=fmgr.construct_ansible_facts(results, module.params, module.params)) - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_appctrl.py b/plugins/modules/network/fortimanager/fmgr_secprof_appctrl.py deleted file mode 100644 index 7a3d567477..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_appctrl.py +++ /dev/null @@ -1,520 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_appctrl -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manage application control security profiles -description: - - Manage application control security profiles within FortiManager - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - unknown_application_log: - description: - - Enable/disable logging for unknown applications. - - choice | disable | Disable logging for unknown applications. - - choice | enable | Enable logging for unknown applications. - required: false - choices: ["disable", "enable"] - - unknown_application_action: - description: - - Pass or block traffic from unknown applications. - - choice | pass | Pass or allow unknown applications. - - choice | block | Drop or block unknown applications. - required: false - choices: ["pass", "block"] - - replacemsg_group: - description: - - Replacement message group. - required: false - - p2p_black_list: - description: - - NO DESCRIPTION PARSED ENTER MANUALLY - - FLAG Based Options. Specify multiple in list form. - - flag | skype | Skype. - - flag | edonkey | Edonkey. - - flag | bittorrent | Bit torrent. - required: false - choices: ["skype", "edonkey", "bittorrent"] - - other_application_log: - description: - - Enable/disable logging for other applications. - - choice | disable | Disable logging for other applications. - - choice | enable | Enable logging for other applications. - required: false - choices: ["disable", "enable"] - - other_application_action: - description: - - Action for other applications. - - choice | pass | Allow sessions matching an application in this application list. - - choice | block | Block sessions matching an application in this application list. - required: false - choices: ["pass", "block"] - - options: - description: - - NO DESCRIPTION PARSED ENTER MANUALLY - - FLAG Based Options. Specify multiple in list form. - - flag | allow-dns | Allow DNS. - - flag | allow-icmp | Allow ICMP. - - flag | allow-http | Allow generic HTTP web browsing. - - flag | allow-ssl | Allow generic SSL communication. - - flag | allow-quic | Allow QUIC. - required: false - choices: ["allow-dns", "allow-icmp", "allow-http", "allow-ssl", "allow-quic"] - - name: - description: - - List name. - required: false - - extended_log: - description: - - Enable/disable extended logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - deep_app_inspection: - description: - - Enable/disable deep application inspection. - - choice | disable | Disable deep application inspection. - - choice | enable | Enable deep application inspection. - required: false - choices: ["disable", "enable"] - - comment: - description: - - comments - required: false - - app_replacemsg: - description: - - Enable/disable replacement messages for blocked applications. - - choice | disable | Disable replacement messages for blocked applications. - - choice | enable | Enable replacement messages for blocked applications. - required: false - choices: ["disable", "enable"] - - entries: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, OMIT THE USE OF THIS PARAMETER - - AND USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - entries_action: - description: - - Pass or block traffic, or reset connection for traffic from this application. - - choice | pass | Pass or allow matching traffic. - - choice | block | Block or drop matching traffic. - - choice | reset | Reset sessions for matching traffic. - required: false - choices: ["pass", "block", "reset"] - - entries_application: - description: - - ID of allowed applications. - required: false - - entries_behavior: - description: - - Application behavior filter. - required: false - - entries_category: - description: - - Category ID list. - required: false - - entries_log: - description: - - Enable/disable logging for this application list. - - choice | disable | Disable logging. - - choice | enable | Enable logging. - required: false - choices: ["disable", "enable"] - - entries_log_packet: - description: - - Enable/disable packet logging. - - choice | disable | Disable packet logging. - - choice | enable | Enable packet logging. - required: false - choices: ["disable", "enable"] - - entries_per_ip_shaper: - description: - - Per-IP traffic shaper. - required: false - - entries_popularity: - description: - - Application popularity filter (1 - 5, from least to most popular). - - FLAG Based Options. Specify multiple in list form. - - flag | 1 | Popularity level 1. - - flag | 2 | Popularity level 2. - - flag | 3 | Popularity level 3. - - flag | 4 | Popularity level 4. - - flag | 5 | Popularity level 5. - required: false - choices: ["1", "2", "3", "4", "5"] - - entries_protocols: - description: - - Application protocol filter. - required: false - - entries_quarantine: - description: - - Quarantine method. - - choice | none | Quarantine is disabled. - - choice | attacker | Block all traffic sent from attacker's IP address. - - The attacker's IP address is also added to the banned user list. The target's address is not affected. - required: false - choices: ["none", "attacker"] - - entries_quarantine_expiry: - description: - - Duration of quarantine. (Format ###d##h##m, minimum 1m, maximum 364d23h59m, default = 5m). - - Requires quarantine set to attacker. - required: false - - entries_quarantine_log: - description: - - Enable/disable quarantine logging. - - choice | disable | Disable quarantine logging. - - choice | enable | Enable quarantine logging. - required: false - choices: ["disable", "enable"] - - entries_rate_count: - description: - - Count of the rate. - required: false - - entries_rate_duration: - description: - - Duration (sec) of the rate. - required: false - - entries_rate_mode: - description: - - Rate limit mode. - - choice | periodical | Allow configured number of packets every rate-duration. - - choice | continuous | Block packets once the rate is reached. - required: false - choices: ["periodical", "continuous"] - - entries_rate_track: - description: - - Track the packet protocol field. - - choice | none | - - choice | src-ip | Source IP. - - choice | dest-ip | Destination IP. - - choice | dhcp-client-mac | DHCP client. - - choice | dns-domain | DNS domain. - required: false - choices: ["none", "src-ip", "dest-ip", "dhcp-client-mac", "dns-domain"] - - entries_risk: - description: - - Risk, or impact, of allowing traffic from this application to occur 1 - 5; - - (Low, Elevated, Medium, High, and Critical). - required: false - - entries_session_ttl: - description: - - Session TTL (0 = default). - required: false - - entries_shaper: - description: - - Traffic shaper. - required: false - - entries_shaper_reverse: - description: - - Reverse traffic shaper. - required: false - - entries_sub_category: - description: - - Application Sub-category ID list. - required: false - - entries_technology: - description: - - Application technology filter. - required: false - - entries_vendor: - description: - - Application vendor filter. - required: false - - entries_parameters_value: - description: - - Parameter value. - required: false - - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_appctrl: - name: "Ansible_Application_Control_Profile" - comment: "Created by Ansible Module TEST" - mode: "delete" - - - name: CREATE Profile - fmgr_secprof_appctrl: - name: "Ansible_Application_Control_Profile" - comment: "Created by Ansible Module TEST" - mode: "set" - entries: [{ - action: "block", - log: "enable", - log-packet: "enable", - protocols: ["1"], - quarantine: "attacker", - quarantine-log: "enable", - }, - {action: "pass", - category: ["2","3","4"]}, - ] -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - -############### -# START METHODS -############### - - -def fmgr_application_list_modify(fmgr, paramgram): - """ - fmgr_application_list -- Modifies Application Control Profiles on FortiManager - - :param fmgr: The fmgr object instance from fmgr_utils.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - - :return: The response from the FortiManager - :rtype: dict - """ - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if paramgram["mode"] in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/application/list'.format(adom=paramgram["adom"]) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif paramgram["mode"] == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/application/list/{name}'.format(adom=paramgram["adom"], - name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - unknown_application_log=dict(required=False, type="str", choices=["disable", "enable"]), - unknown_application_action=dict(required=False, type="str", choices=["pass", "block"]), - replacemsg_group=dict(required=False, type="str"), - p2p_black_list=dict(required=False, type="str", choices=["skype", "edonkey", "bittorrent"]), - other_application_log=dict(required=False, type="str", choices=["disable", "enable"]), - other_application_action=dict(required=False, type="str", choices=["pass", "block"]), - options=dict(required=False, type="str", - choices=["allow-dns", "allow-icmp", "allow-http", "allow-ssl", "allow-quic"]), - name=dict(required=False, type="str"), - extended_log=dict(required=False, type="str", choices=["disable", "enable"]), - deep_app_inspection=dict(required=False, type="str", choices=["disable", "enable"]), - comment=dict(required=False, type="str"), - app_replacemsg=dict(required=False, type="str", choices=["disable", "enable"]), - entries=dict(required=False, type="list"), - entries_action=dict(required=False, type="str", choices=["pass", "block", "reset"]), - entries_application=dict(required=False, type="str"), - entries_behavior=dict(required=False, type="str"), - entries_category=dict(required=False, type="str"), - entries_log=dict(required=False, type="str", choices=["disable", "enable"]), - entries_log_packet=dict(required=False, type="str", choices=["disable", "enable"]), - entries_per_ip_shaper=dict(required=False, type="str"), - entries_popularity=dict(required=False, type="str", choices=["1", "2", "3", "4", "5"]), - entries_protocols=dict(required=False, type="str"), - entries_quarantine=dict(required=False, type="str", choices=["none", "attacker"]), - entries_quarantine_expiry=dict(required=False, type="str"), - entries_quarantine_log=dict(required=False, type="str", choices=["disable", "enable"]), - entries_rate_count=dict(required=False, type="int"), - entries_rate_duration=dict(required=False, type="int"), - entries_rate_mode=dict(required=False, type="str", choices=["periodical", "continuous"]), - entries_rate_track=dict(required=False, type="str", - choices=["none", "src-ip", "dest-ip", "dhcp-client-mac", "dns-domain"]), - entries_risk=dict(required=False, type="str"), - entries_session_ttl=dict(required=False, type="int"), - entries_shaper=dict(required=False, type="str"), - entries_shaper_reverse=dict(required=False, type="str"), - entries_sub_category=dict(required=False, type="str"), - entries_technology=dict(required=False, type="str"), - entries_vendor=dict(required=False, type="str"), - - entries_parameters_value=dict(required=False, type="str"), - - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "unknown-application-log": module.params["unknown_application_log"], - "unknown-application-action": module.params["unknown_application_action"], - "replacemsg-group": module.params["replacemsg_group"], - "p2p-black-list": module.params["p2p_black_list"], - "other-application-log": module.params["other_application_log"], - "other-application-action": module.params["other_application_action"], - "options": module.params["options"], - "name": module.params["name"], - "extended-log": module.params["extended_log"], - "deep-app-inspection": module.params["deep_app_inspection"], - "comment": module.params["comment"], - "app-replacemsg": module.params["app_replacemsg"], - "entries": { - "action": module.params["entries_action"], - "application": module.params["entries_application"], - "behavior": module.params["entries_behavior"], - "category": module.params["entries_category"], - "log": module.params["entries_log"], - "log-packet": module.params["entries_log_packet"], - "per-ip-shaper": module.params["entries_per_ip_shaper"], - "popularity": module.params["entries_popularity"], - "protocols": module.params["entries_protocols"], - "quarantine": module.params["entries_quarantine"], - "quarantine-expiry": module.params["entries_quarantine_expiry"], - "quarantine-log": module.params["entries_quarantine_log"], - "rate-count": module.params["entries_rate_count"], - "rate-duration": module.params["entries_rate_duration"], - "rate-mode": module.params["entries_rate_mode"], - "rate-track": module.params["entries_rate_track"], - "risk": module.params["entries_risk"], - "session-ttl": module.params["entries_session_ttl"], - "shaper": module.params["entries_shaper"], - "shaper-reverse": module.params["entries_shaper_reverse"], - "sub-category": module.params["entries_sub_category"], - "technology": module.params["entries_technology"], - "vendor": module.params["entries_vendor"], - "parameters": { - "value": module.params["entries_parameters_value"], - } - } - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['entries'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - try: - results = fmgr_application_list_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_av.py b/plugins/modules/network/fortimanager/fmgr_secprof_av.py deleted file mode 100644 index ba8ea37051..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_av.py +++ /dev/null @@ -1,1390 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_av -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manage security profile -description: - - Manage security profile groups for FortiManager objects - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - scan_mode: - description: - - Choose between full scan mode and quick scan mode. - required: false - choices: - - quick - - full - - replacemsg_group: - description: - - Replacement message group customized for this profile. - required: false - - name: - description: - - Profile name. - required: false - - mobile_malware_db: - description: - - Enable/disable using the mobile malware signature database. - required: false - choices: - - disable - - enable - - inspection_mode: - description: - - Inspection mode. - required: false - choices: - - proxy - - flow-based - - ftgd_analytics: - description: - - Settings to control which files are uploaded to FortiSandbox. - required: false - choices: - - disable - - suspicious - - everything - - extended_log: - description: - - Enable/disable extended logging for antivirus. - required: false - choices: - - disable - - enable - - comment: - description: - - Comment. - required: false - - av_virus_log: - description: - - Enable/disable AntiVirus logging. - required: false - choices: - - disable - - enable - - av_block_log: - description: - - Enable/disable logging for AntiVirus file blocking. - required: false - choices: - - disable - - enable - - analytics_wl_filetype: - description: - - Do not submit files matching this DLP file-pattern to FortiSandbox. - required: false - - analytics_max_upload: - description: - - Maximum size of files that can be uploaded to FortiSandbox (1 - 395 MBytes, default = 10). - required: false - - analytics_db: - description: - - Enable/disable using the FortiSandbox signature database to supplement the AV signature databases. - required: false - choices: - - disable - - enable - - analytics_bl_filetype: - description: - - Only submit files matching this DLP file-pattern to FortiSandbox. - required: false - - content_disarm: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - content_disarm_cover_page: - description: - - Enable/disable inserting a cover page into the disarmed document. - required: false - choices: - - disable - - enable - - content_disarm_detect_only: - description: - - Enable/disable only detect disarmable files, do not alter content. - required: false - choices: - - disable - - enable - - content_disarm_office_embed: - description: - - Enable/disable stripping of embedded objects in Microsoft Office documents. - required: false - choices: - - disable - - enable - - content_disarm_office_hylink: - description: - - Enable/disable stripping of hyperlinks in Microsoft Office documents. - required: false - choices: - - disable - - enable - - content_disarm_office_linked: - description: - - Enable/disable stripping of linked objects in Microsoft Office documents. - required: false - choices: - - disable - - enable - - content_disarm_office_macro: - description: - - Enable/disable stripping of macros in Microsoft Office documents. - required: false - choices: - - disable - - enable - - content_disarm_original_file_destination: - description: - - Destination to send original file if active content is removed. - required: false - choices: - - fortisandbox - - quarantine - - discard - - content_disarm_pdf_act_form: - description: - - Enable/disable stripping of actions that submit data to other targets in PDF documents. - required: false - choices: - - disable - - enable - - content_disarm_pdf_act_gotor: - description: - - Enable/disable stripping of links to other PDFs in PDF documents. - required: false - choices: - - disable - - enable - - content_disarm_pdf_act_java: - description: - - Enable/disable stripping of actions that execute JavaScript code in PDF documents. - required: false - choices: - - disable - - enable - - content_disarm_pdf_act_launch: - description: - - Enable/disable stripping of links to external applications in PDF documents. - required: false - choices: - - disable - - enable - - content_disarm_pdf_act_movie: - description: - - Enable/disable stripping of embedded movies in PDF documents. - required: false - choices: - - disable - - enable - - content_disarm_pdf_act_sound: - description: - - Enable/disable stripping of embedded sound files in PDF documents. - required: false - choices: - - disable - - enable - - content_disarm_pdf_embedfile: - description: - - Enable/disable stripping of embedded files in PDF documents. - required: false - choices: - - disable - - enable - - content_disarm_pdf_hyperlink: - description: - - Enable/disable stripping of hyperlinks from PDF documents. - required: false - choices: - - disable - - enable - - content_disarm_pdf_javacode: - description: - - Enable/disable stripping of JavaScript code in PDF documents. - required: false - choices: - - disable - - enable - - ftp: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ftp_archive_block: - description: - - Select the archive types to block. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - ftp_archive_log: - description: - - Select the archive types to log. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - ftp_emulator: - description: - - Enable/disable the virus emulator. - required: false - choices: - - disable - - enable - - ftp_options: - description: - - Enable/disable FTP AntiVirus scanning, monitoring, and quarantine. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - scan - - quarantine - - avmonitor - - ftp_outbreak_prevention: - description: - - Enable FortiGuard Virus Outbreak Prevention service. - required: false - choices: - - disabled - - files - - full-archive - - http: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - http_archive_block: - description: - - Select the archive types to block. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - http_archive_log: - description: - - Select the archive types to log. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - http_content_disarm: - description: - - Enable Content Disarm and Reconstruction for this protocol. - required: false - choices: - - disable - - enable - - http_emulator: - description: - - Enable/disable the virus emulator. - required: false - choices: - - disable - - enable - - http_options: - description: - - Enable/disable HTTP AntiVirus scanning, monitoring, and quarantine. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - scan - - quarantine - - avmonitor - - http_outbreak_prevention: - description: - - Enable FortiGuard Virus Outbreak Prevention service. - required: false - choices: - - disabled - - files - - full-archive - - imap: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - imap_archive_block: - description: - - Select the archive types to block. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - imap_archive_log: - description: - - Select the archive types to log. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - imap_content_disarm: - description: - - Enable Content Disarm and Reconstruction for this protocol. - required: false - choices: - - disable - - enable - - imap_emulator: - description: - - Enable/disable the virus emulator. - required: false - choices: - - disable - - enable - - imap_executables: - description: - - Treat Windows executable files as viruses for the purpose of blocking or monitoring. - required: false - choices: - - default - - virus - - imap_options: - description: - - Enable/disable IMAP AntiVirus scanning, monitoring, and quarantine. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - scan - - quarantine - - avmonitor - - imap_outbreak_prevention: - description: - - Enable FortiGuard Virus Outbreak Prevention service. - required: false - choices: - - disabled - - files - - full-archive - - mapi: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - mapi_archive_block: - description: - - Select the archive types to block. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - mapi_archive_log: - description: - - Select the archive types to log. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - mapi_emulator: - description: - - Enable/disable the virus emulator. - required: false - choices: - - disable - - enable - - mapi_executables: - description: - - Treat Windows executable files as viruses for the purpose of blocking or monitoring. - required: false - choices: - - default - - virus - - mapi_options: - description: - - Enable/disable MAPI AntiVirus scanning, monitoring, and quarantine. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - scan - - quarantine - - avmonitor - - mapi_outbreak_prevention: - description: - - Enable FortiGuard Virus Outbreak Prevention service. - required: false - choices: - - disabled - - files - - full-archive - - nac_quar: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - nac_quar_expiry: - description: - - Duration of quarantine. - required: false - - nac_quar_infected: - description: - - Enable/Disable quarantining infected hosts to the banned user list. - required: false - choices: - - none - - quar-src-ip - - nac_quar_log: - description: - - Enable/disable AntiVirus quarantine logging. - required: false - choices: - - disable - - enable - - nntp: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - nntp_archive_block: - description: - - Select the archive types to block. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - nntp_archive_log: - description: - - Select the archive types to log. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - nntp_emulator: - description: - - Enable/disable the virus emulator. - required: false - choices: - - disable - - enable - - nntp_options: - description: - - Enable/disable NNTP AntiVirus scanning, monitoring, and quarantine. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - scan - - quarantine - - avmonitor - - nntp_outbreak_prevention: - description: - - Enable FortiGuard Virus Outbreak Prevention service. - required: false - choices: - - disabled - - files - - full-archive - - pop3: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - pop3_archive_block: - description: - - Select the archive types to block. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - pop3_archive_log: - description: - - Select the archive types to log. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - pop3_content_disarm: - description: - - Enable Content Disarm and Reconstruction for this protocol. - required: false - choices: - - disable - - enable - - pop3_emulator: - description: - - Enable/disable the virus emulator. - required: false - choices: - - disable - - enable - - pop3_executables: - description: - - Treat Windows executable files as viruses for the purpose of blocking or monitoring. - required: false - choices: - - default - - virus - - pop3_options: - description: - - Enable/disable POP3 AntiVirus scanning, monitoring, and quarantine. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - scan - - quarantine - - avmonitor - - pop3_outbreak_prevention: - description: - - Enable FortiGuard Virus Outbreak Prevention service. - required: false - choices: - - disabled - - files - - full-archive - - smb: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - smb_archive_block: - description: - - Select the archive types to block. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - smb_archive_log: - description: - - Select the archive types to log. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - smb_emulator: - description: - - Enable/disable the virus emulator. - required: false - choices: - - disable - - enable - - smb_options: - description: - - Enable/disable SMB AntiVirus scanning, monitoring, and quarantine. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - scan - - quarantine - - avmonitor - - smb_outbreak_prevention: - description: - - Enable FortiGuard Virus Outbreak Prevention service. - required: false - choices: - - disabled - - files - - full-archive - - smtp: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - smtp_archive_block: - description: - - Select the archive types to block. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - smtp_archive_log: - description: - - Select the archive types to log. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - encrypted - - corrupted - - multipart - - nested - - mailbomb - - unhandled - - partiallycorrupted - - fileslimit - - timeout - - smtp_content_disarm: - description: - - Enable Content Disarm and Reconstruction for this protocol. - required: false - choices: - - disable - - enable - - smtp_emulator: - description: - - Enable/disable the virus emulator. - required: false - choices: - - disable - - enable - - smtp_executables: - description: - - Treat Windows executable files as viruses for the purpose of blocking or monitoring. - required: false - choices: - - default - - virus - - smtp_options: - description: - - Enable/disable SMTP AntiVirus scanning, monitoring, and quarantine. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - scan - - quarantine - - avmonitor - - smtp_outbreak_prevention: - description: - - Enable FortiGuard Virus Outbreak Prevention service. - required: false - choices: - - disabled - - files - - full-archive -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_av: - name: "Ansible_AV_Profile" - mode: "delete" - - - name: CREATE Profile - fmgr_secprof_av: - name: "Ansible_AV_Profile" - comment: "Created by Ansible Module TEST" - mode: "set" - inspection_mode: "proxy" - ftgd_analytics: "everything" - av_block_log: "enable" - av_virus_log: "enable" - scan_mode: "full" - mobile_malware_db: "enable" - ftp_archive_block: "encrypted" - ftp_outbreak_prevention: "files" - ftp_archive_log: "timeout" - ftp_emulator: "disable" - ftp_options: "scan" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - -############### -# START METHODS -############### - - -def fmgr_antivirus_profile_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/antivirus/profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - else: - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/antivirus/profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - return response - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(required=False, type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - scan_mode=dict(required=False, type="str", choices=["quick", "full"]), - replacemsg_group=dict(required=False, type="dict"), - name=dict(required=False, type="str"), - mobile_malware_db=dict(required=False, type="str", choices=["disable", "enable"]), - inspection_mode=dict(required=False, type="str", choices=["proxy", "flow-based"]), - ftgd_analytics=dict(required=False, type="str", choices=["disable", "suspicious", "everything"]), - extended_log=dict(required=False, type="str", choices=["disable", "enable"]), - comment=dict(required=False, type="str"), - av_virus_log=dict(required=False, type="str", choices=["disable", "enable"]), - av_block_log=dict(required=False, type="str", choices=["disable", "enable"]), - analytics_wl_filetype=dict(required=False, type="dict"), - analytics_max_upload=dict(required=False, type="int"), - analytics_db=dict(required=False, type="str", choices=["disable", "enable"]), - analytics_bl_filetype=dict(required=False, type="dict"), - content_disarm=dict(required=False, type="list"), - content_disarm_cover_page=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_detect_only=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_office_embed=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_office_hylink=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_office_linked=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_office_macro=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_original_file_destination=dict(required=False, type="str", choices=["fortisandbox", - "quarantine", - "discard"]), - content_disarm_pdf_act_form=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_pdf_act_gotor=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_pdf_act_java=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_pdf_act_launch=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_pdf_act_movie=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_pdf_act_sound=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_pdf_embedfile=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_pdf_hyperlink=dict(required=False, type="str", choices=["disable", "enable"]), - content_disarm_pdf_javacode=dict(required=False, type="str", choices=["disable", "enable"]), - ftp=dict(required=False, type="list"), - ftp_archive_block=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - ftp_archive_log=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - ftp_emulator=dict(required=False, type="str", choices=["disable", "enable"]), - ftp_options=dict(required=False, type="str", choices=["scan", "quarantine", "avmonitor"]), - ftp_outbreak_prevention=dict(required=False, type="str", choices=["disabled", "files", "full-archive"]), - http=dict(required=False, type="list"), - http_archive_block=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - http_archive_log=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - http_content_disarm=dict(required=False, type="str", choices=["disable", "enable"]), - http_emulator=dict(required=False, type="str", choices=["disable", "enable"]), - http_options=dict(required=False, type="str", choices=["scan", "quarantine", "avmonitor"]), - http_outbreak_prevention=dict(required=False, type="str", choices=["disabled", "files", "full-archive"]), - imap=dict(required=False, type="list"), - imap_archive_block=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - imap_archive_log=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - imap_content_disarm=dict(required=False, type="str", choices=["disable", "enable"]), - imap_emulator=dict(required=False, type="str", choices=["disable", "enable"]), - imap_executables=dict(required=False, type="str", choices=["default", "virus"]), - imap_options=dict(required=False, type="str", choices=["scan", "quarantine", "avmonitor"]), - imap_outbreak_prevention=dict(required=False, type="str", choices=["disabled", "files", "full-archive"]), - mapi=dict(required=False, type="list"), - mapi_archive_block=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - mapi_archive_log=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - mapi_emulator=dict(required=False, type="str", choices=["disable", "enable"]), - mapi_executables=dict(required=False, type="str", choices=["default", "virus"]), - mapi_options=dict(required=False, type="str", choices=["scan", "quarantine", "avmonitor"]), - mapi_outbreak_prevention=dict(required=False, type="str", choices=["disabled", "files", "full-archive"]), - nac_quar=dict(required=False, type="list"), - nac_quar_expiry=dict(required=False, type="str"), - nac_quar_infected=dict(required=False, type="str", choices=["none", "quar-src-ip"]), - nac_quar_log=dict(required=False, type="str", choices=["disable", "enable"]), - nntp=dict(required=False, type="list"), - nntp_archive_block=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - nntp_archive_log=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - nntp_emulator=dict(required=False, type="str", choices=["disable", "enable"]), - nntp_options=dict(required=False, type="str", choices=["scan", "quarantine", "avmonitor"]), - nntp_outbreak_prevention=dict(required=False, type="str", choices=["disabled", "files", "full-archive"]), - pop3=dict(required=False, type="list"), - pop3_archive_block=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - pop3_archive_log=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - pop3_content_disarm=dict(required=False, type="str", choices=["disable", "enable"]), - pop3_emulator=dict(required=False, type="str", choices=["disable", "enable"]), - pop3_executables=dict(required=False, type="str", choices=["default", "virus"]), - pop3_options=dict(required=False, type="str", choices=["scan", "quarantine", "avmonitor"]), - pop3_outbreak_prevention=dict(required=False, type="str", choices=["disabled", "files", "full-archive"]), - smb=dict(required=False, type="list"), - smb_archive_block=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - smb_archive_log=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - smb_emulator=dict(required=False, type="str", choices=["disable", "enable"]), - smb_options=dict(required=False, type="str", choices=["scan", "quarantine", "avmonitor"]), - smb_outbreak_prevention=dict(required=False, type="str", choices=["disabled", "files", "full-archive"]), - smtp=dict(required=False, type="list"), - smtp_archive_block=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - smtp_archive_log=dict(required=False, type="str", choices=["encrypted", - "corrupted", - "multipart", - "nested", - "mailbomb", - "unhandled", - "partiallycorrupted", - "fileslimit", - "timeout"]), - smtp_content_disarm=dict(required=False, type="str", choices=["disable", "enable"]), - smtp_emulator=dict(required=False, type="str", choices=["disable", "enable"]), - smtp_executables=dict(required=False, type="str", choices=["default", "virus"]), - smtp_options=dict(required=False, type="str", choices=["scan", "quarantine", "avmonitor"]), - smtp_outbreak_prevention=dict(required=False, type="str", choices=["disabled", "files", "full-archive"]), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "scan-mode": module.params["scan_mode"], - "replacemsg-group": module.params["replacemsg_group"], - "name": module.params["name"], - "mobile-malware-db": module.params["mobile_malware_db"], - "inspection-mode": module.params["inspection_mode"], - "ftgd-analytics": module.params["ftgd_analytics"], - "extended-log": module.params["extended_log"], - "comment": module.params["comment"], - "av-virus-log": module.params["av_virus_log"], - "av-block-log": module.params["av_block_log"], - "analytics-wl-filetype": module.params["analytics_wl_filetype"], - "analytics-max-upload": module.params["analytics_max_upload"], - "analytics-db": module.params["analytics_db"], - "analytics-bl-filetype": module.params["analytics_bl_filetype"], - "content-disarm": { - "cover-page": module.params["content_disarm_cover_page"], - "detect-only": module.params["content_disarm_detect_only"], - "office-embed": module.params["content_disarm_office_embed"], - "office-hylink": module.params["content_disarm_office_hylink"], - "office-linked": module.params["content_disarm_office_linked"], - "office-macro": module.params["content_disarm_office_macro"], - "original-file-destination": module.params["content_disarm_original_file_destination"], - "pdf-act-form": module.params["content_disarm_pdf_act_form"], - "pdf-act-gotor": module.params["content_disarm_pdf_act_gotor"], - "pdf-act-java": module.params["content_disarm_pdf_act_java"], - "pdf-act-launch": module.params["content_disarm_pdf_act_launch"], - "pdf-act-movie": module.params["content_disarm_pdf_act_movie"], - "pdf-act-sound": module.params["content_disarm_pdf_act_sound"], - "pdf-embedfile": module.params["content_disarm_pdf_embedfile"], - "pdf-hyperlink": module.params["content_disarm_pdf_hyperlink"], - "pdf-javacode": module.params["content_disarm_pdf_javacode"], - }, - "ftp": { - "archive-block": module.params["ftp_archive_block"], - "archive-log": module.params["ftp_archive_log"], - "emulator": module.params["ftp_emulator"], - "options": module.params["ftp_options"], - "outbreak-prevention": module.params["ftp_outbreak_prevention"], - }, - "http": { - "archive-block": module.params["http_archive_block"], - "archive-log": module.params["http_archive_log"], - "content-disarm": module.params["http_content_disarm"], - "emulator": module.params["http_emulator"], - "options": module.params["http_options"], - "outbreak-prevention": module.params["http_outbreak_prevention"], - }, - "imap": { - "archive-block": module.params["imap_archive_block"], - "archive-log": module.params["imap_archive_log"], - "content-disarm": module.params["imap_content_disarm"], - "emulator": module.params["imap_emulator"], - "executables": module.params["imap_executables"], - "options": module.params["imap_options"], - "outbreak-prevention": module.params["imap_outbreak_prevention"], - }, - "mapi": { - "archive-block": module.params["mapi_archive_block"], - "archive-log": module.params["mapi_archive_log"], - "emulator": module.params["mapi_emulator"], - "executables": module.params["mapi_executables"], - "options": module.params["mapi_options"], - "outbreak-prevention": module.params["mapi_outbreak_prevention"], - }, - "nac-quar": { - "expiry": module.params["nac_quar_expiry"], - "infected": module.params["nac_quar_infected"], - "log": module.params["nac_quar_log"], - }, - "nntp": { - "archive-block": module.params["nntp_archive_block"], - "archive-log": module.params["nntp_archive_log"], - "emulator": module.params["nntp_emulator"], - "options": module.params["nntp_options"], - "outbreak-prevention": module.params["nntp_outbreak_prevention"], - }, - "pop3": { - "archive-block": module.params["pop3_archive_block"], - "archive-log": module.params["pop3_archive_log"], - "content-disarm": module.params["pop3_content_disarm"], - "emulator": module.params["pop3_emulator"], - "executables": module.params["pop3_executables"], - "options": module.params["pop3_options"], - "outbreak-prevention": module.params["pop3_outbreak_prevention"], - }, - "smb": { - "archive-block": module.params["smb_archive_block"], - "archive-log": module.params["smb_archive_log"], - "emulator": module.params["smb_emulator"], - "options": module.params["smb_options"], - "outbreak-prevention": module.params["smb_outbreak_prevention"], - }, - "smtp": { - "archive-block": module.params["smtp_archive_block"], - "archive-log": module.params["smtp_archive_log"], - "content-disarm": module.params["smtp_content_disarm"], - "emulator": module.params["smtp_emulator"], - "executables": module.params["smtp_executables"], - "options": module.params["smtp_options"], - "outbreak-prevention": module.params["smtp_outbreak_prevention"], - } - } - - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ["content-disarm", "ftp", "http", "imap", "mapi", "nac-quar", "nntp", "pop3", "smb", "smtp"] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - module.paramgram = paramgram - - results = DEFAULT_RESULT_OBJ - - try: - results = fmgr_antivirus_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_dns.py b/plugins/modules/network/fortimanager/fmgr_secprof_dns.py deleted file mode 100644 index 62732df202..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_dns.py +++ /dev/null @@ -1,343 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_dns -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manage DNS security profiles in FortiManager -description: - - Manage DNS security profiles in FortiManager - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values. - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - youtube_restrict: - type: str - description: - - Set safe search for YouTube restriction level. - - choice | strict | Enable strict safe seach for YouTube. - - choice | moderate | Enable moderate safe search for YouTube. - required: false - choices: ["strict", "moderate"] - - sdns_ftgd_err_log: - type: str - description: - - Enable/disable FortiGuard SDNS rating error logging. - - choice | disable | Disable FortiGuard SDNS rating error logging. - - choice | enable | Enable FortiGuard SDNS rating error logging. - required: false - choices: ["disable", "enable"] - - sdns_domain_log: - type: str - description: - - Enable/disable domain filtering and botnet domain logging. - - choice | disable | Disable domain filtering and botnet domain logging. - - choice | enable | Enable domain filtering and botnet domain logging. - required: false - choices: ["disable", "enable"] - - safe_search: - type: str - description: - - Enable/disable Google, Bing, and YouTube safe search. - - choice | disable | Disable Google, Bing, and YouTube safe search. - - choice | enable | Enable Google, Bing, and YouTube safe search. - required: false - choices: ["disable", "enable"] - - redirect_portal: - type: str - description: - - IP address of the SDNS redirect portal. - required: false - - name: - type: str - description: - - Profile name. - required: false - - log_all_domain: - type: str - description: - - Enable/disable logging of all domains visited (detailed DNS logging). - - choice | disable | Disable logging of all domains visited. - - choice | enable | Enable logging of all domains visited. - required: false - choices: ["disable", "enable"] - - external_ip_blocklist: - type: str - description: - - One or more external IP block lists. - required: false - - comment: - type: str - description: - - Comment for the security profile to show in the FortiManager GUI. - required: false - - block_botnet: - type: str - description: - - Enable/disable blocking botnet C&C; DNS lookups. - - choice | disable | Disable blocking botnet C&C; DNS lookups. - - choice | enable | Enable blocking botnet C&C; DNS lookups. - required: false - choices: ["disable", "enable"] - - block_action: - type: str - description: - - Action to take for blocked domains. - - choice | block | Return NXDOMAIN for blocked domains. - - choice | redirect | Redirect blocked domains to SDNS portal. - required: false - choices: ["block", "redirect"] - - domain_filter_domain_filter_table: - type: str - description: - - DNS domain filter table ID. - required: false - - ftgd_dns_options: - type: str - description: - - FortiGuard DNS filter options. - - FLAG Based Options. Specify multiple in list form. - - flag | error-allow | Allow all domains when FortiGuard DNS servers fail. - - flag | ftgd-disable | Disable FortiGuard DNS domain rating. - required: false - choices: ["error-allow", "ftgd-disable"] - - ftgd_dns_filters_action: - type: str - description: - - Action to take for DNS requests matching the category. - - choice | monitor | Allow DNS requests matching the category and log the result. - - choice | block | Block DNS requests matching the category. - required: false - choices: ["monitor", "block"] - - ftgd_dns_filters_category: - type: str - description: - - Category number. - required: false - - ftgd_dns_filters_log: - type: str - description: - - Enable/disable DNS filter logging for this DNS profile. - - choice | disable | Disable DNS filter logging. - - choice | enable | Enable DNS filter logging. - required: false - choices: ["disable", "enable"] - - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_dns: - name: "Ansible_DNS_Profile" - comment: "Created by Ansible Module TEST" - mode: "delete" - - - name: CREATE Profile - fmgr_secprof_dns: - name: "Ansible_DNS_Profile" - comment: "Created by Ansible Module TEST" - mode: "set" - block_action: "block" - - -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -############### -# START METHODS -############### - - -def fmgr_dnsfilter_profile_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - url = "" - datagram = {} - - response = DEFAULT_RESULT_OBJ - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/dnsfilter/profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/dnsfilter/profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - youtube_restrict=dict(required=False, type="str", choices=["strict", "moderate"]), - sdns_ftgd_err_log=dict(required=False, type="str", choices=["disable", "enable"]), - sdns_domain_log=dict(required=False, type="str", choices=["disable", "enable"]), - safe_search=dict(required=False, type="str", choices=["disable", "enable"]), - redirect_portal=dict(required=False, type="str"), - name=dict(required=False, type="str"), - log_all_domain=dict(required=False, type="str", choices=["disable", "enable"]), - external_ip_blocklist=dict(required=False, type="str"), - comment=dict(required=False, type="str"), - block_botnet=dict(required=False, type="str", choices=["disable", "enable"]), - block_action=dict(required=False, type="str", choices=["block", "redirect"]), - - domain_filter_domain_filter_table=dict(required=False, type="str"), - - ftgd_dns_options=dict(required=False, type="str", choices=["error-allow", "ftgd-disable"]), - - ftgd_dns_filters_action=dict(required=False, type="str", choices=["monitor", "block"]), - ftgd_dns_filters_category=dict(required=False, type="str"), - ftgd_dns_filters_log=dict(required=False, type="str", choices=["disable", "enable"]), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "youtube-restrict": module.params["youtube_restrict"], - "sdns-ftgd-err-log": module.params["sdns_ftgd_err_log"], - "sdns-domain-log": module.params["sdns_domain_log"], - "safe-search": module.params["safe_search"], - "redirect-portal": module.params["redirect_portal"], - "name": module.params["name"], - "log-all-domain": module.params["log_all_domain"], - "external-ip-blocklist": module.params["external_ip_blocklist"], - "comment": module.params["comment"], - "block-botnet": module.params["block_botnet"], - "block-action": module.params["block_action"], - "domain-filter": { - "domain-filter-table": module.params["domain_filter_domain_filter_table"], - }, - "ftgd-dns": { - "options": module.params["ftgd_dns_options"], - "filters": { - "action": module.params["ftgd_dns_filters_action"], - "category": module.params["ftgd_dns_filters_category"], - "log": module.params["ftgd_dns_filters_log"], - } - } - } - - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - results = DEFAULT_RESULT_OBJ - - try: - results = fmgr_dnsfilter_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_ips.py b/plugins/modules/network/fortimanager/fmgr_secprof_ips.py deleted file mode 100644 index 525ab8cec3..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_ips.py +++ /dev/null @@ -1,668 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_ips -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Managing IPS security profiles in FortiManager -description: - - Managing IPS security profiles in FortiManager - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - replacemsg_group: - description: - - Replacement message group. - required: false - - name: - description: - - Sensor name. - required: false - - extended_log: - description: - - Enable/disable extended logging. - required: false - choices: - - disable - - enable - - comment: - description: - - Comment. - required: false - - block_malicious_url: - description: - - Enable/disable malicious URL blocking. - required: false - choices: - - disable - - enable - - entries: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - entries_action: - description: - - Action taken with traffic in which signatures are detected. - required: false - choices: - - pass - - block - - reset - - default - - entries_application: - description: - - Applications to be protected. set application ? lists available applications. all includes - all applications. other includes all unlisted applications. - required: false - - entries_location: - description: - - Protect client or server traffic. - required: false - - entries_log: - description: - - Enable/disable logging of signatures included in filter. - required: false - choices: - - disable - - enable - - entries_log_attack_context: - description: - - Enable/disable logging of attack context| URL buffer, header buffer, body buffer, packet buffer. - required: false - choices: - - disable - - enable - - entries_log_packet: - description: - - Enable/disable packet logging. Enable to save the packet that triggers the filter. You can - download the packets in pcap format for diagnostic use. - required: false - choices: - - disable - - enable - - entries_os: - description: - - Operating systems to be protected. all includes all operating systems. other includes all - unlisted operating systems. - required: false - - entries_protocol: - description: - - Protocols to be examined. set protocol ? lists available protocols. all includes all protocols. - other includes all unlisted protocols. - required: false - - entries_quarantine: - description: - - Quarantine method. - required: false - choices: - - none - - attacker - - entries_quarantine_expiry: - description: - - Duration of quarantine. - required: false - - entries_quarantine_log: - description: - - Enable/disable quarantine logging. - required: false - choices: - - disable - - enable - - entries_rate_count: - description: - - Count of the rate. - required: false - - entries_rate_duration: - description: - - Duration (sec) of the rate. - required: false - - entries_rate_mode: - description: - - Rate limit mode. - required: false - choices: - - periodical - - continuous - - entries_rate_track: - description: - - Track the packet protocol field. - required: false - choices: - - none - - src-ip - - dest-ip - - dhcp-client-mac - - dns-domain - - entries_rule: - description: - - Identifies the predefined or custom IPS signatures to add to the sensor. - required: false - - entries_severity: - description: - - Relative severity of the signature, from info to critical. Log messages generated by the signature - include the severity. - required: false - - entries_status: - description: - - Status of the signatures included in filter. default enables the filter and only use filters - with default status of enable. Filters with default status of disable will not be used. - required: false - choices: - - disable - - enable - - default - - entries_exempt_ip_dst_ip: - description: - - Destination IP address and netmask. - required: false - - entries_exempt_ip_src_ip: - description: - - Source IP address and netmask. - required: false - - filter: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - filter_action: - description: - - Action of selected rules. - required: false - choices: - - pass - - block - - default - - reset - - filter_application: - description: - - Vulnerable application filter. - required: false - - filter_location: - description: - - Vulnerability location filter. - required: false - - filter_log: - description: - - Enable/disable logging of selected rules. - required: false - choices: - - disable - - enable - - filter_log_packet: - description: - - Enable/disable packet logging of selected rules. - required: false - choices: - - disable - - enable - - filter_name: - description: - - Filter name. - required: false - - filter_os: - description: - - Vulnerable OS filter. - required: false - - filter_protocol: - description: - - Vulnerable protocol filter. - required: false - - filter_quarantine: - description: - - Quarantine IP or interface. - required: false - choices: - - none - - attacker - - filter_quarantine_expiry: - description: - - Duration of quarantine in minute. - required: false - - filter_quarantine_log: - description: - - Enable/disable logging of selected quarantine. - required: false - choices: - - disable - - enable - - filter_severity: - description: - - Vulnerability severity filter. - required: false - - filter_status: - description: - - Selected rules status. - required: false - choices: - - disable - - enable - - default - - override: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - override_action: - description: - - Action of override rule. - required: false - choices: - - pass - - block - - reset - - override_log: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - override_log_packet: - description: - - Enable/disable packet logging. - required: false - choices: - - disable - - enable - - override_quarantine: - description: - - Quarantine IP or interface. - required: false - choices: - - none - - attacker - - override_quarantine_expiry: - description: - - Duration of quarantine in minute. - required: false - - override_quarantine_log: - description: - - Enable/disable logging of selected quarantine. - required: false - choices: - - disable - - enable - - override_rule_id: - description: - - Override rule ID. - required: false - - override_status: - description: - - Enable/disable status of override rule. - required: false - choices: - - disable - - enable - - override_exempt_ip_dst_ip: - description: - - Destination IP address and netmask. - required: false - - override_exempt_ip_src_ip: - description: - - Source IP address and netmask. - required: false -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_ips: - name: "Ansible_IPS_Profile" - comment: "Created by Ansible Module TEST" - mode: "delete" - - - name: CREATE Profile - fmgr_secprof_ips: - name: "Ansible_IPS_Profile" - comment: "Created by Ansible Module TEST" - mode: "set" - block_malicious_url: "enable" - entries: [{severity: "high", action: "block", log-packet: "enable"}, {severity: "medium", action: "pass"}] -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -############### -# START METHODS -############### - - -def fmgr_ips_sensor_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/ips/sensor'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/ips/sensor/{name}'.format( - adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], - type="str", default="add"), - - replacemsg_group=dict(required=False, type="str"), - name=dict(required=False, type="str"), - extended_log=dict(required=False, type="str", - choices=["disable", "enable"]), - comment=dict(required=False, type="str"), - block_malicious_url=dict(required=False, type="str", choices=[ - "disable", "enable"]), - entries=dict(required=False, type="list"), - entries_action=dict(required=False, type="str", choices=[ - "pass", "block", "reset", "default"]), - entries_application=dict(required=False, type="str"), - entries_location=dict(required=False, type="str"), - entries_log=dict(required=False, type="str", - choices=["disable", "enable"]), - entries_log_attack_context=dict( - required=False, type="str", choices=["disable", "enable"]), - entries_log_packet=dict(required=False, type="str", choices=[ - "disable", "enable"]), - entries_os=dict(required=False, type="str"), - entries_protocol=dict(required=False, type="str"), - entries_quarantine=dict(required=False, type="str", choices=[ - "none", "attacker"]), - entries_quarantine_expiry=dict(required=False, type="str"), - entries_quarantine_log=dict( - required=False, type="str", choices=["disable", "enable"]), - entries_rate_count=dict(required=False, type="int"), - entries_rate_duration=dict(required=False, type="int"), - entries_rate_mode=dict(required=False, type="str", choices=[ - "periodical", "continuous"]), - entries_rate_track=dict(required=False, type="str", - choices=["none", "src-ip", "dest-ip", "dhcp-client-mac", "dns-domain"]), - entries_rule=dict(required=False, type="str"), - entries_severity=dict(required=False, type="str"), - entries_status=dict(required=False, type="str", choices=[ - "disable", "enable", "default"]), - - entries_exempt_ip_dst_ip=dict(required=False, type="str"), - entries_exempt_ip_src_ip=dict(required=False, type="str"), - filter=dict(required=False, type="list"), - filter_action=dict(required=False, type="str", choices=[ - "pass", "block", "default", "reset"]), - filter_application=dict(required=False, type="str"), - filter_location=dict(required=False, type="str"), - filter_log=dict(required=False, type="str", - choices=["disable", "enable"]), - filter_log_packet=dict(required=False, type="str", - choices=["disable", "enable"]), - filter_name=dict(required=False, type="str"), - filter_os=dict(required=False, type="str"), - filter_protocol=dict(required=False, type="str"), - filter_quarantine=dict(required=False, type="str", - choices=["none", "attacker"]), - filter_quarantine_expiry=dict(required=False, type="int"), - filter_quarantine_log=dict(required=False, type="str", choices=[ - "disable", "enable"]), - filter_severity=dict(required=False, type="str"), - filter_status=dict(required=False, type="str", choices=[ - "disable", "enable", "default"]), - override=dict(required=False, type="list"), - override_action=dict(required=False, type="str", - choices=["pass", "block", "reset"]), - override_log=dict(required=False, type="str", - choices=["disable", "enable"]), - override_log_packet=dict(required=False, type="str", choices=[ - "disable", "enable"]), - override_quarantine=dict(required=False, type="str", choices=[ - "none", "attacker"]), - override_quarantine_expiry=dict(required=False, type="int"), - override_quarantine_log=dict( - required=False, type="str", choices=["disable", "enable"]), - override_rule_id=dict(required=False, type="str"), - override_status=dict(required=False, type="str", - choices=["disable", "enable"]), - - override_exempt_ip_dst_ip=dict(required=False, type="str"), - override_exempt_ip_src_ip=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "replacemsg-group": module.params["replacemsg_group"], - "name": module.params["name"], - "extended-log": module.params["extended_log"], - "comment": module.params["comment"], - "block-malicious-url": module.params["block_malicious_url"], - "entries": { - "action": module.params["entries_action"], - "application": module.params["entries_application"], - "location": module.params["entries_location"], - "log": module.params["entries_log"], - "log-attack-context": module.params["entries_log_attack_context"], - "log-packet": module.params["entries_log_packet"], - "os": module.params["entries_os"], - "protocol": module.params["entries_protocol"], - "quarantine": module.params["entries_quarantine"], - "quarantine-expiry": module.params["entries_quarantine_expiry"], - "quarantine-log": module.params["entries_quarantine_log"], - "rate-count": module.params["entries_rate_count"], - "rate-duration": module.params["entries_rate_duration"], - "rate-mode": module.params["entries_rate_mode"], - "rate-track": module.params["entries_rate_track"], - "rule": module.params["entries_rule"], - "severity": module.params["entries_severity"], - "status": module.params["entries_status"], - "exempt-ip": { - "dst-ip": module.params["entries_exempt_ip_dst_ip"], - "src-ip": module.params["entries_exempt_ip_src_ip"], - }, - }, - "filter": { - "action": module.params["filter_action"], - "application": module.params["filter_application"], - "location": module.params["filter_location"], - "log": module.params["filter_log"], - "log-packet": module.params["filter_log_packet"], - "name": module.params["filter_name"], - "os": module.params["filter_os"], - "protocol": module.params["filter_protocol"], - "quarantine": module.params["filter_quarantine"], - "quarantine-expiry": module.params["filter_quarantine_expiry"], - "quarantine-log": module.params["filter_quarantine_log"], - "severity": module.params["filter_severity"], - "status": module.params["filter_status"], - }, - "override": { - "action": module.params["override_action"], - "log": module.params["override_log"], - "log-packet": module.params["override_log_packet"], - "quarantine": module.params["override_quarantine"], - "quarantine-expiry": module.params["override_quarantine_expiry"], - "quarantine-log": module.params["override_quarantine_log"], - "rule-id": module.params["override_rule_id"], - "status": module.params["override_status"], - "exempt-ip": { - "dst-ip": module.params["override_exempt_ip_dst_ip"], - "src-ip": module.params["override_exempt_ip_src_ip"], - } - } - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['entries', 'filter', 'override'] - - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - try: - results = fmgr_ips_sensor_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_profile_group.py b/plugins/modules/network/fortimanager/fmgr_secprof_profile_group.py deleted file mode 100644 index ac1fb88586..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_profile_group.py +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_profile_group -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manage security profiles within FortiManager -description: - - Manage security profile group which allows you to create a group of security profiles and apply that to a policy. - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values. - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - webfilter_profile: - type: str - description: - - Name of an existing Web filter profile. - required: false - - waf_profile: - type: str - description: - - Name of an existing Web application firewall profile. - required: false - - voip_profile: - type: str - description: - - Name of an existing VoIP profile. - required: false - - ssl_ssh_profile: - type: str - description: - - Name of an existing SSL SSH profile. - required: false - - ssh_filter_profile: - type: str - description: - - Name of an existing SSH filter profile. - required: false - - spamfilter_profile: - type: str - description: - - Name of an existing Spam filter profile. - required: false - - profile_protocol_options: - type: str - description: - - Name of an existing Protocol options profile. - required: false - - name: - type: str - description: - - Profile group name. - required: false - - mms_profile: - type: str - description: - - Name of an existing MMS profile. - required: false - - ips_sensor: - type: str - description: - - Name of an existing IPS sensor. - required: false - - icap_profile: - type: str - description: - - Name of an existing ICAP profile. - required: false - - dnsfilter_profile: - type: str - description: - - Name of an existing DNS filter profile. - required: false - - dlp_sensor: - type: str - description: - - Name of an existing DLP sensor. - required: false - - av_profile: - type: str - description: - - Name of an existing Antivirus profile. - required: false - - application_list: - type: str - description: - - Name of an existing Application list. - required: false - - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_profile_group: - name: "Ansible_TEST_Profile_Group" - mode: "delete" - - - name: CREATE Profile - fmgr_secprof_profile_group: - name: "Ansible_TEST_Profile_Group" - mode: "set" - av_profile: "Ansible_AV_Profile" - profile_protocol_options: "default" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -############### -# START METHODS -############### - - -def fmgr_firewall_profile_group_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - url = "" - datagram = {} - - response = DEFAULT_RESULT_OBJ - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/firewall/profile-group'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/firewall/profile-group/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - webfilter_profile=dict(required=False, type="str"), - waf_profile=dict(required=False, type="str"), - voip_profile=dict(required=False, type="str"), - ssl_ssh_profile=dict(required=False, type="str"), - ssh_filter_profile=dict(required=False, type="str"), - spamfilter_profile=dict(required=False, type="str"), - profile_protocol_options=dict(required=False, type="str"), - name=dict(required=False, type="str"), - mms_profile=dict(required=False, type="str"), - ips_sensor=dict(required=False, type="str"), - icap_profile=dict(required=False, type="str"), - dnsfilter_profile=dict(required=False, type="str"), - dlp_sensor=dict(required=False, type="str"), - av_profile=dict(required=False, type="str"), - application_list=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "webfilter-profile": module.params["webfilter_profile"], - "waf-profile": module.params["waf_profile"], - "voip-profile": module.params["voip_profile"], - "ssl-ssh-profile": module.params["ssl_ssh_profile"], - "ssh-filter-profile": module.params["ssh_filter_profile"], - "spamfilter-profile": module.params["spamfilter_profile"], - "profile-protocol-options": module.params["profile_protocol_options"], - "name": module.params["name"], - "mms-profile": module.params["mms_profile"], - "ips-sensor": module.params["ips_sensor"], - "icap-profile": module.params["icap_profile"], - "dnsfilter-profile": module.params["dnsfilter_profile"], - "dlp-sensor": module.params["dlp_sensor"], - "av-profile": module.params["av_profile"], - "application-list": module.params["application_list"], - - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - results = DEFAULT_RESULT_OBJ - - try: - results = fmgr_firewall_profile_group_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_proxy.py b/plugins/modules/network/fortimanager/fmgr_secprof_proxy.py deleted file mode 100644 index 7765ce0581..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_proxy.py +++ /dev/null @@ -1,336 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_proxy -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manage proxy security profiles in FortiManager -description: - - Manage proxy security profiles for FortiGates via FortiManager using the FMG API with playbooks - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - strip_encoding: - description: - - Enable/disable stripping unsupported encoding from the request header. - - choice | disable | Disable stripping of unsupported encoding from the request header. - - choice | enable | Enable stripping of unsupported encoding from the request header. - required: false - choices: ["disable", "enable"] - - name: - description: - - Profile name. - required: false - - log_header_change: - description: - - Enable/disable logging HTTP header changes. - - choice | disable | Disable Enable/disable logging HTTP header changes. - - choice | enable | Enable Enable/disable logging HTTP header changes. - required: false - choices: ["disable", "enable"] - - header_x_forwarded_for: - description: - - Action to take on the HTTP x-forwarded-for header in forwarded requests| forwards (pass), adds, or removes the - - HTTP header. - - choice | pass | Forward the same HTTP header. - - choice | add | Add the HTTP header. - - choice | remove | Remove the HTTP header. - required: false - choices: ["pass", "add", "remove"] - - header_x_authenticated_user: - description: - - Action to take on the HTTP x-authenticated-user header in forwarded requests| forwards (pass), adds, or remove - - s the HTTP header. - - choice | pass | Forward the same HTTP header. - - choice | add | Add the HTTP header. - - choice | remove | Remove the HTTP header. - required: false - choices: ["pass", "add", "remove"] - - header_x_authenticated_groups: - description: - - Action to take on the HTTP x-authenticated-groups header in forwarded requests| forwards (pass), adds, or remo - - ves the HTTP header. - - choice | pass | Forward the same HTTP header. - - choice | add | Add the HTTP header. - - choice | remove | Remove the HTTP header. - required: false - choices: ["pass", "add", "remove"] - - header_via_response: - description: - - Action to take on the HTTP via header in forwarded responses| forwards (pass), adds, or removes the HTTP heade - - r. - - choice | pass | Forward the same HTTP header. - - choice | add | Add the HTTP header. - - choice | remove | Remove the HTTP header. - required: false - choices: ["pass", "add", "remove"] - - header_via_request: - description: - - Action to take on the HTTP via header in forwarded requests| forwards (pass), adds, or removes the HTTP header - - . - - choice | pass | Forward the same HTTP header. - - choice | add | Add the HTTP header. - - choice | remove | Remove the HTTP header. - required: false - choices: ["pass", "add", "remove"] - - header_front_end_https: - description: - - Action to take on the HTTP front-end-HTTPS header in forwarded requests| forwards (pass), adds, or removes the - - HTTP header. - - choice | pass | Forward the same HTTP header. - - choice | add | Add the HTTP header. - - choice | remove | Remove the HTTP header. - required: false - choices: ["pass", "add", "remove"] - - header_client_ip: - description: - - Actions to take on the HTTP client-IP header in forwarded requests| forwards (pass), adds, or removes the HTTP - - header. - - choice | pass | Forward the same HTTP header. - - choice | add | Add the HTTP header. - - choice | remove | Remove the HTTP header. - required: false - choices: ["pass", "add", "remove"] - - headers: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - headers_action: - description: - - Action when HTTP the header forwarded. - - choice | add-to-request | Add the HTTP header to request. - - choice | add-to-response | Add the HTTP header to response. - - choice | remove-from-request | Remove the HTTP header from request. - - choice | remove-from-response | Remove the HTTP header from response. - required: false - choices: ["add-to-request", "add-to-response", "remove-from-request", "remove-from-response"] - - headers_content: - description: - - HTTP header's content. - required: false - - headers_name: - description: - - HTTP forwarded header name. - required: false - - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_proxy: - name: "Ansible_Web_Proxy_Profile" - mode: "delete" - - - name: CREATE Profile - fmgr_secprof_proxy: - name: "Ansible_Web_Proxy_Profile" - mode: "set" - header_client_ip: "pass" - header_front_end_https: "add" - header_via_request: "remove" - header_via_response: "pass" - header_x_authenticated_groups: "add" - header_x_authenticated_user: "remove" - strip_encoding: "enable" - log_header_change: "enable" - header_x_forwarded_for: "pass" - headers_action: "add-to-request" - headers_content: "test" - headers_name: "test_header" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -############### -# START METHODS -############### - - -def fmgr_web_proxy_profile_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/web-proxy/profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/web-proxy/profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - strip_encoding=dict(required=False, type="str", choices=["disable", "enable"]), - name=dict(required=False, type="str"), - log_header_change=dict(required=False, type="str", choices=["disable", "enable"]), - header_x_forwarded_for=dict(required=False, type="str", choices=["pass", "add", "remove"]), - header_x_authenticated_user=dict(required=False, type="str", choices=["pass", "add", "remove"]), - header_x_authenticated_groups=dict(required=False, type="str", choices=["pass", "add", "remove"]), - header_via_response=dict(required=False, type="str", choices=["pass", "add", "remove"]), - header_via_request=dict(required=False, type="str", choices=["pass", "add", "remove"]), - header_front_end_https=dict(required=False, type="str", choices=["pass", "add", "remove"]), - header_client_ip=dict(required=False, type="str", choices=["pass", "add", "remove"]), - headers=dict(required=False, type="list"), - headers_action=dict(required=False, type="str", choices=["add-to-request", "add-to-response", - "remove-from-request", "remove-from-response"]), - headers_content=dict(required=False, type="str"), - headers_name=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "strip-encoding": module.params["strip_encoding"], - "name": module.params["name"], - "log-header-change": module.params["log_header_change"], - "header-x-forwarded-for": module.params["header_x_forwarded_for"], - "header-x-authenticated-user": module.params["header_x_authenticated_user"], - "header-x-authenticated-groups": module.params["header_x_authenticated_groups"], - "header-via-response": module.params["header_via_response"], - "header-via-request": module.params["header_via_request"], - "header-front-end-https": module.params["header_front_end_https"], - "header-client-ip": module.params["header_client_ip"], - "headers": { - "action": module.params["headers_action"], - "content": module.params["headers_content"], - "name": module.params["headers_name"], - } - } - - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['headers'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - module.paramgram = paramgram - - results = DEFAULT_RESULT_OBJ - try: - results = fmgr_web_proxy_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_spam.py b/plugins/modules/network/fortimanager/fmgr_secprof_spam.py deleted file mode 100644 index ebc374ce0d..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_spam.py +++ /dev/null @@ -1,611 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_spam -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: spam filter profile for FMG -description: - - Manage spam filter security profiles within FortiManager via API - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - spam_rbl_table: - description: - - Anti-spam DNSBL table ID. - required: false - - spam_mheader_table: - description: - - Anti-spam MIME header table ID. - required: false - - spam_log_fortiguard_response: - description: - - Enable/disable logging FortiGuard spam response. - required: false - choices: - - disable - - enable - - spam_log: - description: - - Enable/disable spam logging for email filtering. - required: false - choices: - - disable - - enable - - spam_iptrust_table: - description: - - Anti-spam IP trust table ID. - required: false - - spam_filtering: - description: - - Enable/disable spam filtering. - required: false - choices: - - disable - - enable - - spam_bword_threshold: - description: - - Spam banned word threshold. - required: false - - spam_bword_table: - description: - - Anti-spam banned word table ID. - required: false - - spam_bwl_table: - description: - - Anti-spam black/white list table ID. - required: false - - replacemsg_group: - description: - - Replacement message group. - required: false - - options: - description: - - None - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - bannedword - - spamfsip - - spamfssubmit - - spamfschksum - - spamfsurl - - spamhelodns - - spamraddrdns - - spamrbl - - spamhdrcheck - - spamfsphish - - spambwl - - name: - description: - - Profile name. - required: false - - flow_based: - description: - - Enable/disable flow-based spam filtering. - required: false - choices: - - disable - - enable - - external: - description: - - Enable/disable external Email inspection. - required: false - choices: - - disable - - enable - - comment: - description: - - Comment. - required: false - - gmail: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - gmail_log: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - imap: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - imap_action: - description: - - Action for spam email. - required: false - choices: - - pass - - tag - - imap_log: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - imap_tag_msg: - description: - - Subject text or header added to spam email. - required: false - - imap_tag_type: - description: - - Tag subject or header for spam email. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - subject - - header - - spaminfo - - mapi: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - mapi_action: - description: - - Action for spam email. - required: false - choices: - - pass - - discard - - mapi_log: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - msn_hotmail: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - msn_hotmail_log: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - pop3: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - pop3_action: - description: - - Action for spam email. - required: false - choices: - - pass - - tag - - pop3_log: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - pop3_tag_msg: - description: - - Subject text or header added to spam email. - required: false - - pop3_tag_type: - description: - - Tag subject or header for spam email. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - subject - - header - - spaminfo - - smtp: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - smtp_action: - description: - - Action for spam email. - required: false - choices: - - pass - - tag - - discard - - smtp_hdrip: - description: - - Enable/disable SMTP email header IP checks for spamfsip, spamrbl and spambwl filters. - required: false - choices: - - disable - - enable - - smtp_local_override: - description: - - Enable/disable local filter to override SMTP remote check result. - required: false - choices: - - disable - - enable - - smtp_log: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - smtp_tag_msg: - description: - - Subject text or header added to spam email. - required: false - - smtp_tag_type: - description: - - Tag subject or header for spam email. - - FLAG Based Options. Specify multiple in list form. - required: false - choices: - - subject - - header - - spaminfo - - yahoo_mail: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - yahoo_mail_log: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_spam: - name: "Ansible_Spam_Filter_Profile" - mode: "delete" - - - name: Create FMGR_SPAMFILTER_PROFILE - fmgr_secprof_spam: - host: "{{ inventory_hostname }}" - username: "{{ username }}" - password: "{{ password }}" - mode: "set" - adom: "root" - spam_log_fortiguard_response: "enable" - spam_iptrust_table: - spam_filtering: "enable" - spam_bword_threshold: 10 - options: ["bannedword", "spamfsip", "spamfsurl", "spamrbl", "spamfsphish", "spambwl"] - name: "Ansible_Spam_Filter_Profile" - flow_based: "enable" - external: "enable" - comment: "Created by Ansible" - gmail_log: "enable" - spam_log: "enable" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - -############### -# START METHODS -############### - - -def fmgr_spamfilter_profile_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/spamfilter/profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/spamfilter/profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - spam_rbl_table=dict(required=False, type="str"), - spam_mheader_table=dict(required=False, type="str"), - spam_log_fortiguard_response=dict(required=False, type="str", choices=["disable", "enable"]), - spam_log=dict(required=False, type="str", choices=["disable", "enable"]), - spam_iptrust_table=dict(required=False, type="str"), - spam_filtering=dict(required=False, type="str", choices=["disable", "enable"]), - spam_bword_threshold=dict(required=False, type="int"), - spam_bword_table=dict(required=False, type="str"), - spam_bwl_table=dict(required=False, type="str"), - replacemsg_group=dict(required=False, type="str"), - options=dict(required=False, type="list", choices=["bannedword", - "spamfsip", - "spamfssubmit", - "spamfschksum", - "spamfsurl", - "spamhelodns", - "spamraddrdns", - "spamrbl", - "spamhdrcheck", - "spamfsphish", - "spambwl"]), - name=dict(required=False, type="str"), - flow_based=dict(required=False, type="str", choices=["disable", "enable"]), - external=dict(required=False, type="str", choices=["disable", "enable"]), - comment=dict(required=False, type="str"), - gmail=dict(required=False, type="dict"), - gmail_log=dict(required=False, type="str", choices=["disable", "enable"]), - imap=dict(required=False, type="dict"), - imap_action=dict(required=False, type="str", choices=["pass", "tag"]), - imap_log=dict(required=False, type="str", choices=["disable", "enable"]), - imap_tag_msg=dict(required=False, type="str"), - imap_tag_type=dict(required=False, type="str", choices=["subject", "header", "spaminfo"]), - mapi=dict(required=False, type="dict"), - mapi_action=dict(required=False, type="str", choices=["pass", "discard"]), - mapi_log=dict(required=False, type="str", choices=["disable", "enable"]), - msn_hotmail=dict(required=False, type="dict"), - msn_hotmail_log=dict(required=False, type="str", choices=["disable", "enable"]), - pop3=dict(required=False, type="dict"), - pop3_action=dict(required=False, type="str", choices=["pass", "tag"]), - pop3_log=dict(required=False, type="str", choices=["disable", "enable"]), - pop3_tag_msg=dict(required=False, type="str"), - pop3_tag_type=dict(required=False, type="str", choices=["subject", "header", "spaminfo"]), - smtp=dict(required=False, type="dict"), - smtp_action=dict(required=False, type="str", choices=["pass", "tag", "discard"]), - smtp_hdrip=dict(required=False, type="str", choices=["disable", "enable"]), - smtp_local_override=dict(required=False, type="str", choices=["disable", "enable"]), - smtp_log=dict(required=False, type="str", choices=["disable", "enable"]), - smtp_tag_msg=dict(required=False, type="str"), - smtp_tag_type=dict(required=False, type="str", choices=["subject", "header", "spaminfo"]), - yahoo_mail=dict(required=False, type="dict"), - yahoo_mail_log=dict(required=False, type="str", choices=["disable", "enable"]), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "spam-rbl-table": module.params["spam_rbl_table"], - "spam-mheader-table": module.params["spam_mheader_table"], - "spam-log-fortiguard-response": module.params["spam_log_fortiguard_response"], - "spam-log": module.params["spam_log"], - "spam-iptrust-table": module.params["spam_iptrust_table"], - "spam-filtering": module.params["spam_filtering"], - "spam-bword-threshold": module.params["spam_bword_threshold"], - "spam-bword-table": module.params["spam_bword_table"], - "spam-bwl-table": module.params["spam_bwl_table"], - "replacemsg-group": module.params["replacemsg_group"], - "options": module.params["options"], - "name": module.params["name"], - "flow-based": module.params["flow_based"], - "external": module.params["external"], - "comment": module.params["comment"], - "gmail": { - "log": module.params["gmail_log"], - }, - "imap": { - "action": module.params["imap_action"], - "log": module.params["imap_log"], - "tag-msg": module.params["imap_tag_msg"], - "tag-type": module.params["imap_tag_type"], - }, - "mapi": { - "action": module.params["mapi_action"], - "log": module.params["mapi_log"], - }, - "msn-hotmail": { - "log": module.params["msn_hotmail_log"], - }, - "pop3": { - "action": module.params["pop3_action"], - "log": module.params["pop3_log"], - "tag-msg": module.params["pop3_tag_msg"], - "tag-type": module.params["pop3_tag_type"], - }, - "smtp": { - "action": module.params["smtp_action"], - "hdrip": module.params["smtp_hdrip"], - "local-override": module.params["smtp_local_override"], - "log": module.params["smtp_log"], - "tag-msg": module.params["smtp_tag_msg"], - "tag-type": module.params["smtp_tag_type"], - }, - "yahoo-mail": { - "log": module.params["yahoo_mail_log"], - } - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['gmail', 'imap', 'mapi', 'msn-hotmail', 'pop3', 'smtp', 'yahoo-mail'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - try: - - results = fmgr_spamfilter_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_ssl_ssh.py b/plugins/modules/network/fortimanager/fmgr_secprof_ssl_ssh.py deleted file mode 100644 index e5c71c480d..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_ssl_ssh.py +++ /dev/null @@ -1,958 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_ssl_ssh -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manage SSL and SSH security profiles in FortiManager -description: - - Manage SSL and SSH security profiles in FortiManager via the FMG API - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - whitelist: - description: - - Enable/disable exempting servers by FortiGuard whitelist. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - use_ssl_server: - description: - - Enable/disable the use of SSL server table for SSL offloading. - - choice | disable | Don't use SSL server configuration. - - choice | enable | Use SSL server configuration. - required: false - choices: ["disable", "enable"] - - untrusted_caname: - description: - - Untrusted CA certificate used by SSL Inspection. - required: false - - ssl_exemptions_log: - description: - - Enable/disable logging SSL exemptions. - - choice | disable | Disable logging SSL exemptions. - - choice | enable | Enable logging SSL exemptions. - required: false - choices: ["disable", "enable"] - - ssl_anomalies_log: - description: - - Enable/disable logging SSL anomalies. - - choice | disable | Disable logging SSL anomalies. - - choice | enable | Enable logging SSL anomalies. - required: false - choices: ["disable", "enable"] - - server_cert_mode: - description: - - Re-sign or replace the server's certificate. - - choice | re-sign | Multiple clients connecting to multiple servers. - - choice | replace | Protect an SSL server. - required: false - choices: ["re-sign", "replace"] - - server_cert: - description: - - Certificate used by SSL Inspection to replace server certificate. - required: false - - rpc_over_https: - description: - - Enable/disable inspection of RPC over HTTPS. - - choice | disable | Disable inspection of RPC over HTTPS. - - choice | enable | Enable inspection of RPC over HTTPS. - required: false - choices: ["disable", "enable"] - - name: - description: - - Name. - required: false - - mapi_over_https: - description: - - Enable/disable inspection of MAPI over HTTPS. - - choice | disable | Disable inspection of MAPI over HTTPS. - - choice | enable | Enable inspection of MAPI over HTTPS. - required: false - choices: ["disable", "enable"] - - comment: - description: - - Optional comments. - required: false - - caname: - description: - - CA certificate used by SSL Inspection. - required: false - - ftps: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ftps_allow_invalid_server_cert: - description: - - When enabled, allows SSL sessions whose server certificate validation failed. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - ftps_client_cert_request: - description: - - Action based on client certificate request failure. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ftps_ports: - description: - - Ports to use for scanning (1 - 65535, default = 443). - required: false - - ftps_status: - description: - - Configure protocol inspection status. - - choice | disable | Disable. - - choice | deep-inspection | Full SSL inspection. - required: false - choices: ["disable", "deep-inspection"] - - ftps_unsupported_ssl: - description: - - Action based on the SSL encryption used being unsupported. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ftps_untrusted_cert: - description: - - Allow, ignore, or block the untrusted SSL session server certificate. - - choice | allow | Allow the untrusted server certificate. - - choice | block | Block the connection when an untrusted server certificate is detected. - - choice | ignore | Always take the server certificate as trusted. - required: false - choices: ["allow", "block", "ignore"] - - https: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - https_allow_invalid_server_cert: - description: - - When enabled, allows SSL sessions whose server certificate validation failed. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - https_client_cert_request: - description: - - Action based on client certificate request failure. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - https_ports: - description: - - Ports to use for scanning (1 - 65535, default = 443). - required: false - - https_status: - description: - - Configure protocol inspection status. - - choice | disable | Disable. - - choice | certificate-inspection | Inspect SSL handshake only. - - choice | deep-inspection | Full SSL inspection. - required: false - choices: ["disable", "certificate-inspection", "deep-inspection"] - - https_unsupported_ssl: - description: - - Action based on the SSL encryption used being unsupported. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - https_untrusted_cert: - description: - - Allow, ignore, or block the untrusted SSL session server certificate. - - choice | allow | Allow the untrusted server certificate. - - choice | block | Block the connection when an untrusted server certificate is detected. - - choice | ignore | Always take the server certificate as trusted. - required: false - choices: ["allow", "block", "ignore"] - - imaps: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - imaps_allow_invalid_server_cert: - description: - - When enabled, allows SSL sessions whose server certificate validation failed. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - imaps_client_cert_request: - description: - - Action based on client certificate request failure. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - imaps_ports: - description: - - Ports to use for scanning (1 - 65535, default = 443). - required: false - - imaps_status: - description: - - Configure protocol inspection status. - - choice | disable | Disable. - - choice | deep-inspection | Full SSL inspection. - required: false - choices: ["disable", "deep-inspection"] - - imaps_unsupported_ssl: - description: - - Action based on the SSL encryption used being unsupported. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - imaps_untrusted_cert: - description: - - Allow, ignore, or block the untrusted SSL session server certificate. - - choice | allow | Allow the untrusted server certificate. - - choice | block | Block the connection when an untrusted server certificate is detected. - - choice | ignore | Always take the server certificate as trusted. - required: false - choices: ["allow", "block", "ignore"] - - pop3s: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - pop3s_allow_invalid_server_cert: - description: - - When enabled, allows SSL sessions whose server certificate validation failed. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - pop3s_client_cert_request: - description: - - Action based on client certificate request failure. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - pop3s_ports: - description: - - Ports to use for scanning (1 - 65535, default = 443). - required: false - - pop3s_status: - description: - - Configure protocol inspection status. - - choice | disable | Disable. - - choice | deep-inspection | Full SSL inspection. - required: false - choices: ["disable", "deep-inspection"] - - pop3s_unsupported_ssl: - description: - - Action based on the SSL encryption used being unsupported. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - pop3s_untrusted_cert: - description: - - Allow, ignore, or block the untrusted SSL session server certificate. - - choice | allow | Allow the untrusted server certificate. - - choice | block | Block the connection when an untrusted server certificate is detected. - - choice | ignore | Always take the server certificate as trusted. - required: false - choices: ["allow", "block", "ignore"] - - smtps: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - smtps_allow_invalid_server_cert: - description: - - When enabled, allows SSL sessions whose server certificate validation failed. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - smtps_client_cert_request: - description: - - Action based on client certificate request failure. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - smtps_ports: - description: - - Ports to use for scanning (1 - 65535, default = 443). - required: false - - smtps_status: - description: - - Configure protocol inspection status. - - choice | disable | Disable. - - choice | deep-inspection | Full SSL inspection. - required: false - choices: ["disable", "deep-inspection"] - - smtps_unsupported_ssl: - description: - - Action based on the SSL encryption used being unsupported. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - smtps_untrusted_cert: - description: - - Allow, ignore, or block the untrusted SSL session server certificate. - - choice | allow | Allow the untrusted server certificate. - - choice | block | Block the connection when an untrusted server certificate is detected. - - choice | ignore | Always take the server certificate as trusted. - required: false - choices: ["allow", "block", "ignore"] - - ssh: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ssh_inspect_all: - description: - - Level of SSL inspection. - - choice | disable | Disable. - - choice | deep-inspection | Full SSL inspection. - required: false - choices: ["disable", "deep-inspection"] - - ssh_ports: - description: - - Ports to use for scanning (1 - 65535, default = 443). - required: false - - ssh_ssh_algorithm: - description: - - Relative strength of encryption algorithms accepted during negotiation. - - choice | compatible | Allow a broader set of encryption algorithms for best compatibility. - - choice | high-encryption | Allow only AES-CTR, AES-GCM ciphers and high encryption algorithms. - required: false - choices: ["compatible", "high-encryption"] - - ssh_ssh_policy_check: - description: - - Enable/disable SSH policy check. - - choice | disable | Disable SSH policy check. - - choice | enable | Enable SSH policy check. - required: false - choices: ["disable", "enable"] - - ssh_ssh_tun_policy_check: - description: - - Enable/disable SSH tunnel policy check. - - choice | disable | Disable SSH tunnel policy check. - - choice | enable | Enable SSH tunnel policy check. - required: false - choices: ["disable", "enable"] - - ssh_status: - description: - - Configure protocol inspection status. - - choice | disable | Disable. - - choice | deep-inspection | Full SSL inspection. - required: false - choices: ["disable", "deep-inspection"] - - ssh_unsupported_version: - description: - - Action based on SSH version being unsupported. - - choice | block | Block. - - choice | bypass | Bypass. - required: false - choices: ["block", "bypass"] - - ssl: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ssl_allow_invalid_server_cert: - description: - - When enabled, allows SSL sessions whose server certificate validation failed. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - ssl_client_cert_request: - description: - - Action based on client certificate request failure. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ssl_inspect_all: - description: - - Level of SSL inspection. - - choice | disable | Disable. - - choice | certificate-inspection | Inspect SSL handshake only. - - choice | deep-inspection | Full SSL inspection. - required: false - choices: ["disable", "certificate-inspection", "deep-inspection"] - - ssl_unsupported_ssl: - description: - - Action based on the SSL encryption used being unsupported. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ssl_untrusted_cert: - description: - - Allow, ignore, or block the untrusted SSL session server certificate. - - choice | allow | Allow the untrusted server certificate. - - choice | block | Block the connection when an untrusted server certificate is detected. - - choice | ignore | Always take the server certificate as trusted. - required: false - choices: ["allow", "block", "ignore"] - - ssl_exempt: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ssl_exempt_address: - description: - - IPv4 address object. - required: false - - ssl_exempt_address6: - description: - - IPv6 address object. - required: false - - ssl_exempt_fortiguard_category: - description: - - FortiGuard category ID. - required: false - - ssl_exempt_regex: - description: - - Exempt servers by regular expression. - required: false - - ssl_exempt_type: - description: - - Type of address object (IPv4 or IPv6) or FortiGuard category. - - choice | fortiguard-category | FortiGuard category. - - choice | address | Firewall IPv4 address. - - choice | address6 | Firewall IPv6 address. - - choice | wildcard-fqdn | Fully Qualified Domain Name with wildcard characters. - - choice | regex | Regular expression FQDN. - required: false - choices: ["fortiguard-category", "address", "address6", "wildcard-fqdn", "regex"] - - ssl_exempt_wildcard_fqdn: - description: - - Exempt servers by wildcard FQDN. - required: false - - ssl_server: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ssl_server_ftps_client_cert_request: - description: - - Action based on client certificate request failure during the FTPS handshake. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ssl_server_https_client_cert_request: - description: - - Action based on client certificate request failure during the HTTPS handshake. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ssl_server_imaps_client_cert_request: - description: - - Action based on client certificate request failure during the IMAPS handshake. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ssl_server_ip: - description: - - IPv4 address of the SSL server. - required: false - - ssl_server_pop3s_client_cert_request: - description: - - Action based on client certificate request failure during the POP3S handshake. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ssl_server_smtps_client_cert_request: - description: - - Action based on client certificate request failure during the SMTPS handshake. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - ssl_server_ssl_other_client_cert_request: - description: - - Action based on client certificate request failure during an SSL protocol handshake. - - choice | bypass | Bypass. - - choice | inspect | Inspect. - - choice | block | Block. - required: false - choices: ["bypass", "inspect", "block"] - - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_ssl_ssh: - name: Ansible_SSL_SSH_Profile - mode: delete - - - name: CREATE Profile - fmgr_secprof_ssl_ssh: - name: Ansible_SSL_SSH_Profile - comment: "Created by Ansible Module TEST" - mode: set - mapi_over_https: enable - rpc_over_https: enable - server_cert_mode: replace - ssl_anomalies_log: enable - ssl_exemptions_log: enable - use_ssl_server: enable - whitelist: enable -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - -############### -# START METHODS -############### - - -def fmgr_firewall_ssl_ssh_profile_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/firewall/ssl-ssh-profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/firewall/ssl-ssh-profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - whitelist=dict(required=False, type="str", choices=["disable", "enable"]), - use_ssl_server=dict(required=False, type="str", choices=["disable", "enable"]), - untrusted_caname=dict(required=False, type="str"), - ssl_exemptions_log=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_anomalies_log=dict(required=False, type="str", choices=["disable", "enable"]), - server_cert_mode=dict(required=False, type="str", choices=["re-sign", "replace"]), - server_cert=dict(required=False, type="str"), - rpc_over_https=dict(required=False, type="str", choices=["disable", "enable"]), - name=dict(required=False, type="str"), - mapi_over_https=dict(required=False, type="str", choices=["disable", "enable"]), - comment=dict(required=False, type="str"), - caname=dict(required=False, type="str"), - ftps=dict(required=False, type="list"), - ftps_allow_invalid_server_cert=dict(required=False, type="str", choices=["disable", "enable"]), - ftps_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ftps_ports=dict(required=False, type="str"), - ftps_status=dict(required=False, type="str", choices=["disable", "deep-inspection"]), - ftps_unsupported_ssl=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ftps_untrusted_cert=dict(required=False, type="str", choices=["allow", "block", "ignore"]), - https=dict(required=False, type="list"), - https_allow_invalid_server_cert=dict(required=False, type="str", choices=["disable", "enable"]), - https_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - https_ports=dict(required=False, type="str"), - https_status=dict(required=False, type="str", choices=["disable", "certificate-inspection", "deep-inspection"]), - https_unsupported_ssl=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - https_untrusted_cert=dict(required=False, type="str", choices=["allow", "block", "ignore"]), - imaps=dict(required=False, type="list"), - imaps_allow_invalid_server_cert=dict(required=False, type="str", choices=["disable", "enable"]), - imaps_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - imaps_ports=dict(required=False, type="str"), - imaps_status=dict(required=False, type="str", choices=["disable", "deep-inspection"]), - imaps_unsupported_ssl=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - imaps_untrusted_cert=dict(required=False, type="str", choices=["allow", "block", "ignore"]), - pop3s=dict(required=False, type="list"), - pop3s_allow_invalid_server_cert=dict(required=False, type="str", choices=["disable", "enable"]), - pop3s_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - pop3s_ports=dict(required=False, type="str"), - pop3s_status=dict(required=False, type="str", choices=["disable", "deep-inspection"]), - pop3s_unsupported_ssl=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - pop3s_untrusted_cert=dict(required=False, type="str", choices=["allow", "block", "ignore"]), - smtps=dict(required=False, type="list"), - smtps_allow_invalid_server_cert=dict(required=False, type="str", choices=["disable", "enable"]), - smtps_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - smtps_ports=dict(required=False, type="str"), - smtps_status=dict(required=False, type="str", choices=["disable", "deep-inspection"]), - smtps_unsupported_ssl=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - smtps_untrusted_cert=dict(required=False, type="str", choices=["allow", "block", "ignore"]), - ssh=dict(required=False, type="list"), - ssh_inspect_all=dict(required=False, type="str", choices=["disable", "deep-inspection"]), - ssh_ports=dict(required=False, type="str"), - ssh_ssh_algorithm=dict(required=False, type="str", choices=["compatible", "high-encryption"]), - ssh_ssh_policy_check=dict(required=False, type="str", choices=["disable", "enable"]), - ssh_ssh_tun_policy_check=dict(required=False, type="str", choices=["disable", "enable"]), - ssh_status=dict(required=False, type="str", choices=["disable", "deep-inspection"]), - ssh_unsupported_version=dict(required=False, type="str", choices=["block", "bypass"]), - ssl=dict(required=False, type="list"), - ssl_allow_invalid_server_cert=dict(required=False, type="str", choices=["disable", "enable"]), - ssl_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ssl_inspect_all=dict(required=False, type="str", choices=["disable", "certificate-inspection", - "deep-inspection"]), - ssl_unsupported_ssl=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ssl_untrusted_cert=dict(required=False, type="str", choices=["allow", "block", "ignore"]), - ssl_exempt=dict(required=False, type="list"), - ssl_exempt_address=dict(required=False, type="str"), - ssl_exempt_address6=dict(required=False, type="str"), - ssl_exempt_fortiguard_category=dict(required=False, type="str"), - ssl_exempt_regex=dict(required=False, type="str"), - ssl_exempt_type=dict(required=False, type="str", choices=["fortiguard-category", "address", "address6", - "wildcard-fqdn", "regex"]), - ssl_exempt_wildcard_fqdn=dict(required=False, type="str"), - ssl_server=dict(required=False, type="list"), - ssl_server_ftps_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ssl_server_https_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ssl_server_imaps_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ssl_server_ip=dict(required=False, type="str"), - ssl_server_pop3s_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ssl_server_smtps_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", "block"]), - ssl_server_ssl_other_client_cert_request=dict(required=False, type="str", choices=["bypass", "inspect", - "block"]), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "whitelist": module.params["whitelist"], - "use-ssl-server": module.params["use_ssl_server"], - "untrusted-caname": module.params["untrusted_caname"], - "ssl-exemptions-log": module.params["ssl_exemptions_log"], - "ssl-anomalies-log": module.params["ssl_anomalies_log"], - "server-cert-mode": module.params["server_cert_mode"], - "server-cert": module.params["server_cert"], - "rpc-over-https": module.params["rpc_over_https"], - "name": module.params["name"], - "mapi-over-https": module.params["mapi_over_https"], - "comment": module.params["comment"], - "caname": module.params["caname"], - "ftps": { - "allow-invalid-server-cert": module.params["ftps_allow_invalid_server_cert"], - "client-cert-request": module.params["ftps_client_cert_request"], - "ports": module.params["ftps_ports"], - "status": module.params["ftps_status"], - "unsupported-ssl": module.params["ftps_unsupported_ssl"], - "untrusted-cert": module.params["ftps_untrusted_cert"], - }, - "https": { - "allow-invalid-server-cert": module.params["https_allow_invalid_server_cert"], - "client-cert-request": module.params["https_client_cert_request"], - "ports": module.params["https_ports"], - "status": module.params["https_status"], - "unsupported-ssl": module.params["https_unsupported_ssl"], - "untrusted-cert": module.params["https_untrusted_cert"], - }, - "imaps": { - "allow-invalid-server-cert": module.params["imaps_allow_invalid_server_cert"], - "client-cert-request": module.params["imaps_client_cert_request"], - "ports": module.params["imaps_ports"], - "status": module.params["imaps_status"], - "unsupported-ssl": module.params["imaps_unsupported_ssl"], - "untrusted-cert": module.params["imaps_untrusted_cert"], - }, - "pop3s": { - "allow-invalid-server-cert": module.params["pop3s_allow_invalid_server_cert"], - "client-cert-request": module.params["pop3s_client_cert_request"], - "ports": module.params["pop3s_ports"], - "status": module.params["pop3s_status"], - "unsupported-ssl": module.params["pop3s_unsupported_ssl"], - "untrusted-cert": module.params["pop3s_untrusted_cert"], - }, - "smtps": { - "allow-invalid-server-cert": module.params["smtps_allow_invalid_server_cert"], - "client-cert-request": module.params["smtps_client_cert_request"], - "ports": module.params["smtps_ports"], - "status": module.params["smtps_status"], - "unsupported-ssl": module.params["smtps_unsupported_ssl"], - "untrusted-cert": module.params["smtps_untrusted_cert"], - }, - "ssh": { - "inspect-all": module.params["ssh_inspect_all"], - "ports": module.params["ssh_ports"], - "ssh-algorithm": module.params["ssh_ssh_algorithm"], - "ssh-policy-check": module.params["ssh_ssh_policy_check"], - "ssh-tun-policy-check": module.params["ssh_ssh_tun_policy_check"], - "status": module.params["ssh_status"], - "unsupported-version": module.params["ssh_unsupported_version"], - }, - "ssl": { - "allow-invalid-server-cert": module.params["ssl_allow_invalid_server_cert"], - "client-cert-request": module.params["ssl_client_cert_request"], - "inspect-all": module.params["ssl_inspect_all"], - "unsupported-ssl": module.params["ssl_unsupported_ssl"], - "untrusted-cert": module.params["ssl_untrusted_cert"], - }, - "ssl-exempt": { - "address": module.params["ssl_exempt_address"], - "address6": module.params["ssl_exempt_address6"], - "fortiguard-category": module.params["ssl_exempt_fortiguard_category"], - "regex": module.params["ssl_exempt_regex"], - "type": module.params["ssl_exempt_type"], - "wildcard-fqdn": module.params["ssl_exempt_wildcard_fqdn"], - }, - "ssl-server": { - "ftps-client-cert-request": module.params["ssl_server_ftps_client_cert_request"], - "https-client-cert-request": module.params["ssl_server_https_client_cert_request"], - "imaps-client-cert-request": module.params["ssl_server_imaps_client_cert_request"], - "ip": module.params["ssl_server_ip"], - "pop3s-client-cert-request": module.params["ssl_server_pop3s_client_cert_request"], - "smtps-client-cert-request": module.params["ssl_server_smtps_client_cert_request"], - "ssl-other-client-cert-request": module.params["ssl_server_ssl_other_client_cert_request"], - } - } - - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['ftps', 'https', 'imaps', 'pop3s', 'smtps', 'ssh', 'ssl', 'ssl-exempt', 'ssl-server'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - - try: - - results = fmgr_firewall_ssl_ssh_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_voip.py b/plugins/modules/network/fortimanager/fmgr_secprof_voip.py deleted file mode 100644 index f35ebc8c1e..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_voip.py +++ /dev/null @@ -1,1202 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_voip -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: VOIP security profiles in FMG -description: - - Manage VOIP security profiles in FortiManager via API - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - name: - description: - - Profile name. - required: false - - comment: - description: - - Comment. - required: false - - sccp: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - sccp_block_mcast: - description: - - Enable/disable block multicast RTP connections. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sccp_log_call_summary: - description: - - Enable/disable log summary of SCCP calls. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sccp_log_violations: - description: - - Enable/disable logging of SCCP violations. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sccp_max_calls: - description: - - Maximum calls per minute per SCCP client (max 65535). - required: false - - sccp_status: - description: - - Enable/disable SCCP. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sccp_verify_header: - description: - - Enable/disable verify SCCP header content. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - sip_ack_rate: - description: - - ACK request rate limit (per second, per policy). - required: false - - sip_block_ack: - description: - - Enable/disable block ACK requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_bye: - description: - - Enable/disable block BYE requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_cancel: - description: - - Enable/disable block CANCEL requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_geo_red_options: - description: - - Enable/disable block OPTIONS requests, but OPTIONS requests still notify for redundancy. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_info: - description: - - Enable/disable block INFO requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_invite: - description: - - Enable/disable block INVITE requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_long_lines: - description: - - Enable/disable block requests with headers exceeding max-line-length. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_message: - description: - - Enable/disable block MESSAGE requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_notify: - description: - - Enable/disable block NOTIFY requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_options: - description: - - Enable/disable block OPTIONS requests and no OPTIONS as notifying message for redundancy either. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_prack: - description: - - Enable/disable block prack requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_publish: - description: - - Enable/disable block PUBLISH requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_refer: - description: - - Enable/disable block REFER requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_register: - description: - - Enable/disable block REGISTER requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_subscribe: - description: - - Enable/disable block SUBSCRIBE requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_unknown: - description: - - Block unrecognized SIP requests (enabled by default). - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_block_update: - description: - - Enable/disable block UPDATE requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_bye_rate: - description: - - BYE request rate limit (per second, per policy). - required: false - - sip_call_keepalive: - description: - - Continue tracking calls with no RTP for this many minutes. - required: false - - sip_cancel_rate: - description: - - CANCEL request rate limit (per second, per policy). - required: false - - sip_contact_fixup: - description: - - Fixup contact anyway even if contact's IP|port doesn't match session's IP|port. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_hnt_restrict_source_ip: - description: - - Enable/disable restrict RTP source IP to be the same as SIP source IP when HNT is enabled. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_hosted_nat_traversal: - description: - - Hosted NAT Traversal (HNT). - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_info_rate: - description: - - INFO request rate limit (per second, per policy). - required: false - - sip_invite_rate: - description: - - INVITE request rate limit (per second, per policy). - required: false - - sip_ips_rtp: - description: - - Enable/disable allow IPS on RTP. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_log_call_summary: - description: - - Enable/disable logging of SIP call summary. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_log_violations: - description: - - Enable/disable logging of SIP violations. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_malformed_header_allow: - description: - - Action for malformed Allow header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_call_id: - description: - - Action for malformed Call-ID header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_contact: - description: - - Action for malformed Contact header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_content_length: - description: - - Action for malformed Content-Length header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_content_type: - description: - - Action for malformed Content-Type header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_cseq: - description: - - Action for malformed CSeq header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_expires: - description: - - Action for malformed Expires header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_from: - description: - - Action for malformed From header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_max_forwards: - description: - - Action for malformed Max-Forwards header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_p_asserted_identity: - description: - - Action for malformed P-Asserted-Identity header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_rack: - description: - - Action for malformed RAck header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_record_route: - description: - - Action for malformed Record-Route header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_route: - description: - - Action for malformed Route header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_rseq: - description: - - Action for malformed RSeq header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_a: - description: - - Action for malformed SDP a line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_b: - description: - - Action for malformed SDP b line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_c: - description: - - Action for malformed SDP c line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_i: - description: - - Action for malformed SDP i line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_k: - description: - - Action for malformed SDP k line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_m: - description: - - Action for malformed SDP m line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_o: - description: - - Action for malformed SDP o line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_r: - description: - - Action for malformed SDP r line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_s: - description: - - Action for malformed SDP s line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_t: - description: - - Action for malformed SDP t line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_v: - description: - - Action for malformed SDP v line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_sdp_z: - description: - - Action for malformed SDP z line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_to: - description: - - Action for malformed To header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_header_via: - description: - - Action for malformed VIA header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_malformed_request_line: - description: - - Action for malformed request line. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_max_body_length: - description: - - Maximum SIP message body length (0 meaning no limit). - required: false - - sip_max_dialogs: - description: - - Maximum number of concurrent calls/dialogs (per policy). - required: false - - sip_max_idle_dialogs: - description: - - Maximum number established but idle dialogs to retain (per policy). - required: false - - sip_max_line_length: - description: - - Maximum SIP header line length (78-4096). - required: false - - sip_message_rate: - description: - - MESSAGE request rate limit (per second, per policy). - required: false - - sip_nat_trace: - description: - - Enable/disable preservation of original IP in SDP i line. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_no_sdp_fixup: - description: - - Enable/disable no SDP fix-up. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_notify_rate: - description: - - NOTIFY request rate limit (per second, per policy). - required: false - - sip_open_contact_pinhole: - description: - - Enable/disable open pinhole for non-REGISTER Contact port. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_open_record_route_pinhole: - description: - - Enable/disable open pinhole for Record-Route port. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_open_register_pinhole: - description: - - Enable/disable open pinhole for REGISTER Contact port. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_open_via_pinhole: - description: - - Enable/disable open pinhole for Via port. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_options_rate: - description: - - OPTIONS request rate limit (per second, per policy). - required: false - - sip_prack_rate: - description: - - PRACK request rate limit (per second, per policy). - required: false - - sip_preserve_override: - description: - - Override i line to preserve original IPS (default| append). - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_provisional_invite_expiry_time: - description: - - Expiry time for provisional INVITE (10 - 3600 sec). - required: false - - sip_publish_rate: - description: - - PUBLISH request rate limit (per second, per policy). - required: false - - sip_refer_rate: - description: - - REFER request rate limit (per second, per policy). - required: false - - sip_register_contact_trace: - description: - - Enable/disable trace original IP/port within the contact header of REGISTER requests. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_register_rate: - description: - - REGISTER request rate limit (per second, per policy). - required: false - - sip_rfc2543_branch: - description: - - Enable/disable support via branch compliant with RFC 2543. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_rtp: - description: - - Enable/disable create pinholes for RTP traffic to traverse firewall. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_ssl_algorithm: - description: - - Relative strength of encryption algorithms accepted in negotiation. - - choice | high | High encryption. Allow only AES and ChaCha. - - choice | medium | Medium encryption. Allow AES, ChaCha, 3DES, and RC4. - - choice | low | Low encryption. Allow AES, ChaCha, 3DES, RC4, and DES. - required: false - choices: ["high", "medium", "low"] - - sip_ssl_auth_client: - description: - - Require a client certificate and authenticate it with the peer/peergrp. - required: false - - sip_ssl_auth_server: - description: - - Authenticate the server's certificate with the peer/peergrp. - required: false - - sip_ssl_client_certificate: - description: - - Name of Certificate to offer to server if requested. - required: false - - sip_ssl_client_renegotiation: - description: - - Allow/block client renegotiation by server. - - choice | allow | Allow a SSL client to renegotiate. - - choice | deny | Abort any SSL connection that attempts to renegotiate. - - choice | secure | Reject any SSL connection that does not offer a RFC 5746 Secure Renegotiation Indication. - required: false - choices: ["allow", "deny", "secure"] - - sip_ssl_max_version: - description: - - Highest SSL/TLS version to negotiate. - - choice | ssl-3.0 | SSL 3.0. - - choice | tls-1.0 | TLS 1.0. - - choice | tls-1.1 | TLS 1.1. - - choice | tls-1.2 | TLS 1.2. - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - sip_ssl_min_version: - description: - - Lowest SSL/TLS version to negotiate. - - choice | ssl-3.0 | SSL 3.0. - - choice | tls-1.0 | TLS 1.0. - - choice | tls-1.1 | TLS 1.1. - - choice | tls-1.2 | TLS 1.2. - required: false - choices: ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"] - - sip_ssl_mode: - description: - - SSL/TLS mode for encryption & decryption of traffic. - - choice | off | No SSL. - - choice | full | Client to FortiGate and FortiGate to Server SSL. - required: false - choices: ["off", "full"] - - sip_ssl_pfs: - description: - - SSL Perfect Forward Secrecy. - - choice | require | PFS mandatory. - - choice | deny | PFS rejected. - - choice | allow | PFS allowed. - required: false - choices: ["require", "deny", "allow"] - - sip_ssl_send_empty_frags: - description: - - Send empty fragments to avoid attack on CBC IV (SSL 3.0 & TLS 1.0 only). - - choice | disable | Do not send empty fragments. - - choice | enable | Send empty fragments. - required: false - choices: ["disable", "enable"] - - sip_ssl_server_certificate: - description: - - Name of Certificate return to the client in every SSL connection. - required: false - - sip_status: - description: - - Enable/disable SIP. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_strict_register: - description: - - Enable/disable only allow the registrar to connect. - - choice | disable | Disable status. - - choice | enable | Enable status. - required: false - choices: ["disable", "enable"] - - sip_subscribe_rate: - description: - - SUBSCRIBE request rate limit (per second, per policy). - required: false - - sip_unknown_header: - description: - - Action for unknown SIP header. - - choice | pass | Bypass malformed messages. - - choice | discard | Discard malformed messages. - - choice | respond | Respond with error code. - required: false - choices: ["pass", "discard", "respond"] - - sip_update_rate: - description: - - UPDATE request rate limit (per second, per policy). - required: false - - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_voip: - name: "Ansible_VOIP_Profile" - mode: "delete" - - - name: Create FMGR_VOIP_PROFILE - fmgr_secprof_voip: - mode: "set" - adom: "root" - name: "Ansible_VOIP_Profile" - comment: "Created by Ansible" - sccp: {block-mcast: "enable", log-call-summary: "enable", log-violations: "enable", status: "enable"} -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -############### -# START METHODS -############### - - -def fmgr_voip_profile_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/voip/profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/voip/profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - name=dict(required=False, type="str"), - comment=dict(required=False, type="str"), - sccp=dict(required=False, type="dict"), - sccp_block_mcast=dict(required=False, type="str", choices=["disable", "enable"]), - sccp_log_call_summary=dict(required=False, type="str", choices=["disable", "enable"]), - sccp_log_violations=dict(required=False, type="str", choices=["disable", "enable"]), - sccp_max_calls=dict(required=False, type="int"), - sccp_status=dict(required=False, type="str", choices=["disable", "enable"]), - sccp_verify_header=dict(required=False, type="str", choices=["disable", "enable"]), - sip=dict(required=False, type="dict"), - sip_ack_rate=dict(required=False, type="int"), - sip_block_ack=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_bye=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_cancel=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_geo_red_options=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_info=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_invite=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_long_lines=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_message=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_notify=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_options=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_prack=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_publish=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_refer=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_register=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_subscribe=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_unknown=dict(required=False, type="str", choices=["disable", "enable"]), - sip_block_update=dict(required=False, type="str", choices=["disable", "enable"]), - sip_bye_rate=dict(required=False, type="int"), - sip_call_keepalive=dict(required=False, type="int"), - sip_cancel_rate=dict(required=False, type="int"), - sip_contact_fixup=dict(required=False, type="str", choices=["disable", "enable"]), - sip_hnt_restrict_source_ip=dict(required=False, type="str", choices=["disable", "enable"]), - sip_hosted_nat_traversal=dict(required=False, type="str", choices=["disable", "enable"]), - sip_info_rate=dict(required=False, type="int"), - sip_invite_rate=dict(required=False, type="int"), - sip_ips_rtp=dict(required=False, type="str", choices=["disable", "enable"]), - sip_log_call_summary=dict(required=False, type="str", choices=["disable", "enable"]), - sip_log_violations=dict(required=False, type="str", choices=["disable", "enable"]), - sip_malformed_header_allow=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_call_id=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_contact=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_content_length=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_content_type=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_cseq=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_expires=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_from=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_max_forwards=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_p_asserted_identity=dict(required=False, type="str", choices=["pass", - "discard", - "respond"]), - sip_malformed_header_rack=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_record_route=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_route=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_rseq=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_a=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_b=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_c=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_i=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_k=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_m=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_o=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_r=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_s=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_t=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_v=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_sdp_z=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_to=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_header_via=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_malformed_request_line=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_max_body_length=dict(required=False, type="int"), - sip_max_dialogs=dict(required=False, type="int"), - sip_max_idle_dialogs=dict(required=False, type="int"), - sip_max_line_length=dict(required=False, type="int"), - sip_message_rate=dict(required=False, type="int"), - sip_nat_trace=dict(required=False, type="str", choices=["disable", "enable"]), - sip_no_sdp_fixup=dict(required=False, type="str", choices=["disable", "enable"]), - sip_notify_rate=dict(required=False, type="int"), - sip_open_contact_pinhole=dict(required=False, type="str", choices=["disable", "enable"]), - sip_open_record_route_pinhole=dict(required=False, type="str", choices=["disable", "enable"]), - sip_open_register_pinhole=dict(required=False, type="str", choices=["disable", "enable"]), - sip_open_via_pinhole=dict(required=False, type="str", choices=["disable", "enable"]), - sip_options_rate=dict(required=False, type="int"), - sip_prack_rate=dict(required=False, type="int"), - sip_preserve_override=dict(required=False, type="str", choices=["disable", "enable"]), - sip_provisional_invite_expiry_time=dict(required=False, type="int"), - sip_publish_rate=dict(required=False, type="int"), - sip_refer_rate=dict(required=False, type="int"), - sip_register_contact_trace=dict(required=False, type="str", choices=["disable", "enable"]), - sip_register_rate=dict(required=False, type="int"), - sip_rfc2543_branch=dict(required=False, type="str", choices=["disable", "enable"]), - sip_rtp=dict(required=False, type="str", choices=["disable", "enable"]), - sip_ssl_algorithm=dict(required=False, type="str", choices=["high", "medium", "low"]), - sip_ssl_auth_client=dict(required=False, type="str"), - sip_ssl_auth_server=dict(required=False, type="str"), - sip_ssl_client_certificate=dict(required=False, type="str"), - sip_ssl_client_renegotiation=dict(required=False, type="str", choices=["allow", "deny", "secure"]), - sip_ssl_max_version=dict(required=False, type="str", choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - sip_ssl_min_version=dict(required=False, type="str", choices=["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]), - sip_ssl_mode=dict(required=False, type="str", choices=["off", "full"]), - sip_ssl_pfs=dict(required=False, type="str", choices=["require", "deny", "allow"]), - sip_ssl_send_empty_frags=dict(required=False, type="str", choices=["disable", "enable"]), - sip_ssl_server_certificate=dict(required=False, type="str"), - sip_status=dict(required=False, type="str", choices=["disable", "enable"]), - sip_strict_register=dict(required=False, type="str", choices=["disable", "enable"]), - sip_subscribe_rate=dict(required=False, type="int"), - sip_unknown_header=dict(required=False, type="str", choices=["pass", "discard", "respond"]), - sip_update_rate=dict(required=False, type="int"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "name": module.params["name"], - "comment": module.params["comment"], - "sccp": { - "block-mcast": module.params["sccp_block_mcast"], - "log-call-summary": module.params["sccp_log_call_summary"], - "log-violations": module.params["sccp_log_violations"], - "max-calls": module.params["sccp_max_calls"], - "status": module.params["sccp_status"], - "verify-header": module.params["sccp_verify_header"], - }, - "sip": { - "ack-rate": module.params["sip_ack_rate"], - "block-ack": module.params["sip_block_ack"], - "block-bye": module.params["sip_block_bye"], - "block-cancel": module.params["sip_block_cancel"], - "block-geo-red-options": module.params["sip_block_geo_red_options"], - "block-info": module.params["sip_block_info"], - "block-invite": module.params["sip_block_invite"], - "block-long-lines": module.params["sip_block_long_lines"], - "block-message": module.params["sip_block_message"], - "block-notify": module.params["sip_block_notify"], - "block-options": module.params["sip_block_options"], - "block-prack": module.params["sip_block_prack"], - "block-publish": module.params["sip_block_publish"], - "block-refer": module.params["sip_block_refer"], - "block-register": module.params["sip_block_register"], - "block-subscribe": module.params["sip_block_subscribe"], - "block-unknown": module.params["sip_block_unknown"], - "block-update": module.params["sip_block_update"], - "bye-rate": module.params["sip_bye_rate"], - "call-keepalive": module.params["sip_call_keepalive"], - "cancel-rate": module.params["sip_cancel_rate"], - "contact-fixup": module.params["sip_contact_fixup"], - "hnt-restrict-source-ip": module.params["sip_hnt_restrict_source_ip"], - "hosted-nat-traversal": module.params["sip_hosted_nat_traversal"], - "info-rate": module.params["sip_info_rate"], - "invite-rate": module.params["sip_invite_rate"], - "ips-rtp": module.params["sip_ips_rtp"], - "log-call-summary": module.params["sip_log_call_summary"], - "log-violations": module.params["sip_log_violations"], - "malformed-header-allow": module.params["sip_malformed_header_allow"], - "malformed-header-call-id": module.params["sip_malformed_header_call_id"], - "malformed-header-contact": module.params["sip_malformed_header_contact"], - "malformed-header-content-length": module.params["sip_malformed_header_content_length"], - "malformed-header-content-type": module.params["sip_malformed_header_content_type"], - "malformed-header-cseq": module.params["sip_malformed_header_cseq"], - "malformed-header-expires": module.params["sip_malformed_header_expires"], - "malformed-header-from": module.params["sip_malformed_header_from"], - "malformed-header-max-forwards": module.params["sip_malformed_header_max_forwards"], - "malformed-header-p-asserted-identity": module.params["sip_malformed_header_p_asserted_identity"], - "malformed-header-rack": module.params["sip_malformed_header_rack"], - "malformed-header-record-route": module.params["sip_malformed_header_record_route"], - "malformed-header-route": module.params["sip_malformed_header_route"], - "malformed-header-rseq": module.params["sip_malformed_header_rseq"], - "malformed-header-sdp-a": module.params["sip_malformed_header_sdp_a"], - "malformed-header-sdp-b": module.params["sip_malformed_header_sdp_b"], - "malformed-header-sdp-c": module.params["sip_malformed_header_sdp_c"], - "malformed-header-sdp-i": module.params["sip_malformed_header_sdp_i"], - "malformed-header-sdp-k": module.params["sip_malformed_header_sdp_k"], - "malformed-header-sdp-m": module.params["sip_malformed_header_sdp_m"], - "malformed-header-sdp-o": module.params["sip_malformed_header_sdp_o"], - "malformed-header-sdp-r": module.params["sip_malformed_header_sdp_r"], - "malformed-header-sdp-s": module.params["sip_malformed_header_sdp_s"], - "malformed-header-sdp-t": module.params["sip_malformed_header_sdp_t"], - "malformed-header-sdp-v": module.params["sip_malformed_header_sdp_v"], - "malformed-header-sdp-z": module.params["sip_malformed_header_sdp_z"], - "malformed-header-to": module.params["sip_malformed_header_to"], - "malformed-header-via": module.params["sip_malformed_header_via"], - "malformed-request-line": module.params["sip_malformed_request_line"], - "max-body-length": module.params["sip_max_body_length"], - "max-dialogs": module.params["sip_max_dialogs"], - "max-idle-dialogs": module.params["sip_max_idle_dialogs"], - "max-line-length": module.params["sip_max_line_length"], - "message-rate": module.params["sip_message_rate"], - "nat-trace": module.params["sip_nat_trace"], - "no-sdp-fixup": module.params["sip_no_sdp_fixup"], - "notify-rate": module.params["sip_notify_rate"], - "open-contact-pinhole": module.params["sip_open_contact_pinhole"], - "open-record-route-pinhole": module.params["sip_open_record_route_pinhole"], - "open-register-pinhole": module.params["sip_open_register_pinhole"], - "open-via-pinhole": module.params["sip_open_via_pinhole"], - "options-rate": module.params["sip_options_rate"], - "prack-rate": module.params["sip_prack_rate"], - "preserve-override": module.params["sip_preserve_override"], - "provisional-invite-expiry-time": module.params["sip_provisional_invite_expiry_time"], - "publish-rate": module.params["sip_publish_rate"], - "refer-rate": module.params["sip_refer_rate"], - "register-contact-trace": module.params["sip_register_contact_trace"], - "register-rate": module.params["sip_register_rate"], - "rfc2543-branch": module.params["sip_rfc2543_branch"], - "rtp": module.params["sip_rtp"], - "ssl-algorithm": module.params["sip_ssl_algorithm"], - "ssl-auth-client": module.params["sip_ssl_auth_client"], - "ssl-auth-server": module.params["sip_ssl_auth_server"], - "ssl-client-certificate": module.params["sip_ssl_client_certificate"], - "ssl-client-renegotiation": module.params["sip_ssl_client_renegotiation"], - "ssl-max-version": module.params["sip_ssl_max_version"], - "ssl-min-version": module.params["sip_ssl_min_version"], - "ssl-mode": module.params["sip_ssl_mode"], - "ssl-pfs": module.params["sip_ssl_pfs"], - "ssl-send-empty-frags": module.params["sip_ssl_send_empty_frags"], - "ssl-server-certificate": module.params["sip_ssl_server_certificate"], - "status": module.params["sip_status"], - "strict-register": module.params["sip_strict_register"], - "subscribe-rate": module.params["sip_subscribe_rate"], - "unknown-header": module.params["sip_unknown_header"], - "update-rate": module.params["sip_update_rate"], - } - } - - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['sccp', 'sip'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - module.paramgram = paramgram - - results = DEFAULT_RESULT_OBJ - try: - - results = fmgr_voip_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_waf.py b/plugins/modules/network/fortimanager/fmgr_secprof_waf.py deleted file mode 100644 index 275176959f..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_waf.py +++ /dev/null @@ -1,1481 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_waf -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: FortiManager web application firewall security profile -description: - - Manage web application firewall security profiles for FGTs via FMG - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - name: - description: - - WAF Profile name. - required: false - - external: - description: - - Disable/Enable external HTTP Inspection. - - choice | disable | Disable external inspection. - - choice | enable | Enable external inspection. - required: false - choices: ["disable", "enable"] - - extended_log: - description: - - Enable/disable extended logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - comment: - description: - - Comment. - required: false - - address_list: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - address_list_blocked_address: - description: - - Blocked address. - required: false - - address_list_blocked_log: - description: - - Enable/disable logging on blocked addresses. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - address_list_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - address_list_status: - description: - - Status. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - address_list_trusted_address: - description: - - Trusted address. - required: false - - constraint: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - constraint_content_length_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_content_length_length: - description: - - Length of HTTP content in bytes (0 to 2147483647). - required: false - - constraint_content_length_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_content_length_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_content_length_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_address: - description: - - Host address. - required: false - - constraint_exception_content_length: - description: - - HTTP content length in request. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_header_length: - description: - - HTTP header length in request. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_hostname: - description: - - Enable/disable hostname check. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_line_length: - description: - - HTTP line length in request. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_malformed: - description: - - Enable/disable malformed HTTP request check. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_max_cookie: - description: - - Maximum number of cookies in HTTP request. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_max_header_line: - description: - - Maximum number of HTTP header line. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_max_range_segment: - description: - - Maximum number of range segments in HTTP range line. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_max_url_param: - description: - - Maximum number of parameters in URL. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_method: - description: - - Enable/disable HTTP method check. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_param_length: - description: - - Maximum length of parameter in URL, HTTP POST request or HTTP body. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_pattern: - description: - - URL pattern. - required: false - - constraint_exception_regex: - description: - - Enable/disable regular expression based pattern match. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_url_param_length: - description: - - Maximum length of parameter in URL. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_exception_version: - description: - - Enable/disable HTTP version check. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_header_length_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_header_length_length: - description: - - Length of HTTP header in bytes (0 to 2147483647). - required: false - - constraint_header_length_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_header_length_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_header_length_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_hostname_action: - description: - - Action for a hostname constraint. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_hostname_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_hostname_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_hostname_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_line_length_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_line_length_length: - description: - - Length of HTTP line in bytes (0 to 2147483647). - required: false - - constraint_line_length_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_line_length_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_line_length_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_malformed_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_malformed_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_malformed_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_malformed_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_max_cookie_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_max_cookie_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_max_cookie_max_cookie: - description: - - Maximum number of cookies in HTTP request (0 to 2147483647). - required: false - - constraint_max_cookie_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_max_cookie_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_max_header_line_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_max_header_line_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_max_header_line_max_header_line: - description: - - Maximum number HTTP header lines (0 to 2147483647). - required: false - - constraint_max_header_line_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_max_header_line_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_max_range_segment_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_max_range_segment_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_max_range_segment_max_range_segment: - description: - - Maximum number of range segments in HTTP range line (0 to 2147483647). - required: false - - constraint_max_range_segment_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_max_range_segment_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_max_url_param_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_max_url_param_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_max_url_param_max_url_param: - description: - - Maximum number of parameters in URL (0 to 2147483647). - required: false - - constraint_max_url_param_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_max_url_param_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_method_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_method_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_method_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_method_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_param_length_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_param_length_length: - description: - - Maximum length of parameter in URL, HTTP POST request or HTTP body in bytes (0 to 2147483647). - required: false - - constraint_param_length_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_param_length_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_param_length_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_url_param_length_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_url_param_length_length: - description: - - Maximum length of URL parameter in bytes (0 to 2147483647). - required: false - - constraint_url_param_length_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_url_param_length_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_url_param_length_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_version_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - required: false - choices: ["allow", "block"] - - constraint_version_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - constraint_version_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - constraint_version_status: - description: - - Enable/disable the constraint. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - method: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - method_default_allowed_methods: - description: - - Methods. - - FLAG Based Options. Specify multiple in list form. - - flag | delete | HTTP DELETE method. - - flag | get | HTTP GET method. - - flag | head | HTTP HEAD method. - - flag | options | HTTP OPTIONS method. - - flag | post | HTTP POST method. - - flag | put | HTTP PUT method. - - flag | trace | HTTP TRACE method. - - flag | others | Other HTTP methods. - - flag | connect | HTTP CONNECT method. - required: false - choices: ["delete", "get", "head", "options", "post", "put", "trace", "others", "connect"] - - method_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - method_severity: - description: - - Severity. - - choice | low | low severity - - choice | medium | medium severity - - choice | high | High severity - required: false - choices: ["low", "medium", "high"] - - method_status: - description: - - Status. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - method_method_policy_address: - description: - - Host address. - required: false - - method_method_policy_allowed_methods: - description: - - Allowed Methods. - - FLAG Based Options. Specify multiple in list form. - - flag | delete | HTTP DELETE method. - - flag | get | HTTP GET method. - - flag | head | HTTP HEAD method. - - flag | options | HTTP OPTIONS method. - - flag | post | HTTP POST method. - - flag | put | HTTP PUT method. - - flag | trace | HTTP TRACE method. - - flag | others | Other HTTP methods. - - flag | connect | HTTP CONNECT method. - required: false - choices: ["delete", "get", "head", "options", "post", "put", "trace", "others", "connect"] - - method_method_policy_pattern: - description: - - URL pattern. - required: false - - method_method_policy_regex: - description: - - Enable/disable regular expression based pattern match. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - signature: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - signature_credit_card_detection_threshold: - description: - - The minimum number of Credit cards to detect violation. - required: false - - signature_disabled_signature: - description: - - Disabled signatures - required: false - - signature_disabled_sub_class: - description: - - Disabled signature subclasses. - required: false - - signature_custom_signature_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - - choice | erase | Erase credit card numbers. - required: false - choices: ["allow", "block", "erase"] - - signature_custom_signature_case_sensitivity: - description: - - Case sensitivity in pattern. - - choice | disable | Case insensitive in pattern. - - choice | enable | Case sensitive in pattern. - required: false - choices: ["disable", "enable"] - - signature_custom_signature_direction: - description: - - Traffic direction. - - choice | request | Match HTTP request. - - choice | response | Match HTTP response. - required: false - choices: ["request", "response"] - - signature_custom_signature_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - signature_custom_signature_name: - description: - - Signature name. - required: false - - signature_custom_signature_pattern: - description: - - Match pattern. - required: false - - signature_custom_signature_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - signature_custom_signature_status: - description: - - Status. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - signature_custom_signature_target: - description: - - Match HTTP target. - - FLAG Based Options. Specify multiple in list form. - - flag | arg | HTTP arguments. - - flag | arg-name | Names of HTTP arguments. - - flag | req-body | HTTP request body. - - flag | req-cookie | HTTP request cookies. - - flag | req-cookie-name | HTTP request cookie names. - - flag | req-filename | HTTP request file name. - - flag | req-header | HTTP request headers. - - flag | req-header-name | HTTP request header names. - - flag | req-raw-uri | Raw URI of HTTP request. - - flag | req-uri | URI of HTTP request. - - flag | resp-body | HTTP response body. - - flag | resp-hdr | HTTP response headers. - - flag | resp-status | HTTP response status. - required: false - choices: ["arg","arg-name","req-body","req-cookie","req-cookie-name","req-filename","req-header","req-header-name", - "req-raw-uri","req-uri","resp-body","resp-hdr","resp-status"] - - signature_main_class_action: - description: - - Action. - - choice | allow | Allow. - - choice | block | Block. - - choice | erase | Erase credit card numbers. - required: false - choices: ["allow", "block", "erase"] - - signature_main_class_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - signature_main_class_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - signature_main_class_status: - description: - - Status. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - url_access: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - url_access_action: - description: - - Action. - - choice | bypass | Allow the HTTP request, also bypass further WAF scanning. - - choice | permit | Allow the HTTP request, and continue further WAF scanning. - - choice | block | Block HTTP request. - required: false - choices: ["bypass", "permit", "block"] - - url_access_address: - description: - - Host address. - required: false - - url_access_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - url_access_severity: - description: - - Severity. - - choice | low | Low severity. - - choice | medium | Medium severity. - - choice | high | High severity. - required: false - choices: ["low", "medium", "high"] - - url_access_access_pattern_negate: - description: - - Enable/disable match negation. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - url_access_access_pattern_pattern: - description: - - URL pattern. - required: false - - url_access_access_pattern_regex: - description: - - Enable/disable regular expression based pattern match. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - url_access_access_pattern_srcaddr: - description: - - Source address. - required: false - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_waf: - name: "Ansible_WAF_Profile" - comment: "Created by Ansible Module TEST" - mode: "delete" - - - name: CREATE Profile - fmgr_secprof_waf: - name: "Ansible_WAF_Profile" - comment: "Created by Ansible Module TEST" - mode: "set" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -############### -# START METHODS -############### - - -def fmgr_waf_profile_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - mode = paramgram["mode"] - adom = paramgram["adom"] - # INIT A BASIC OBJECTS - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/waf/profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/waf/profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - name=dict(required=False, type="str"), - external=dict(required=False, type="str", choices=["disable", "enable"]), - extended_log=dict(required=False, type="str", choices=["disable", "enable"]), - comment=dict(required=False, type="str"), - address_list=dict(required=False, type="list"), - address_list_blocked_address=dict(required=False, type="str"), - address_list_blocked_log=dict(required=False, type="str", choices=["disable", "enable"]), - address_list_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - address_list_status=dict(required=False, type="str", choices=["disable", "enable"]), - address_list_trusted_address=dict(required=False, type="str"), - constraint=dict(required=False, type="list"), - - constraint_content_length_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_content_length_length=dict(required=False, type="int"), - constraint_content_length_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_content_length_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_content_length_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_exception_address=dict(required=False, type="str"), - constraint_exception_content_length=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_header_length=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_hostname=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_line_length=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_malformed=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_max_cookie=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_max_header_line=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_max_range_segment=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_max_url_param=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_method=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_param_length=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_pattern=dict(required=False, type="str"), - constraint_exception_regex=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_url_param_length=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_exception_version=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_header_length_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_header_length_length=dict(required=False, type="int"), - constraint_header_length_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_header_length_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_header_length_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_hostname_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_hostname_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_hostname_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_hostname_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_line_length_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_line_length_length=dict(required=False, type="int"), - constraint_line_length_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_line_length_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_line_length_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_malformed_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_malformed_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_malformed_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_malformed_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_max_cookie_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_max_cookie_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_max_cookie_max_cookie=dict(required=False, type="int"), - constraint_max_cookie_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_max_cookie_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_max_header_line_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_max_header_line_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_max_header_line_max_header_line=dict(required=False, type="int"), - constraint_max_header_line_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_max_header_line_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_max_range_segment_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_max_range_segment_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_max_range_segment_max_range_segment=dict(required=False, type="int"), - constraint_max_range_segment_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_max_range_segment_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_max_url_param_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_max_url_param_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_max_url_param_max_url_param=dict(required=False, type="int"), - constraint_max_url_param_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_max_url_param_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_method_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_method_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_method_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_method_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_param_length_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_param_length_length=dict(required=False, type="int"), - constraint_param_length_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_param_length_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_param_length_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_url_param_length_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_url_param_length_length=dict(required=False, type="int"), - constraint_url_param_length_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_url_param_length_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_url_param_length_status=dict(required=False, type="str", choices=["disable", "enable"]), - - constraint_version_action=dict(required=False, type="str", choices=["allow", "block"]), - constraint_version_log=dict(required=False, type="str", choices=["disable", "enable"]), - constraint_version_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - constraint_version_status=dict(required=False, type="str", choices=["disable", "enable"]), - method=dict(required=False, type="list"), - method_default_allowed_methods=dict(required=False, type="str", choices=["delete", - "get", - "head", - "options", - "post", - "put", - "trace", - "others", - "connect"]), - method_log=dict(required=False, type="str", choices=["disable", "enable"]), - method_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - method_status=dict(required=False, type="str", choices=["disable", "enable"]), - - method_method_policy_address=dict(required=False, type="str"), - method_method_policy_allowed_methods=dict(required=False, type="str", choices=["delete", - "get", - "head", - "options", - "post", - "put", - "trace", - "others", - "connect"]), - method_method_policy_pattern=dict(required=False, type="str"), - method_method_policy_regex=dict(required=False, type="str", choices=["disable", "enable"]), - signature=dict(required=False, type="list"), - signature_credit_card_detection_threshold=dict(required=False, type="int"), - signature_disabled_signature=dict(required=False, type="str"), - signature_disabled_sub_class=dict(required=False, type="str"), - - signature_custom_signature_action=dict(required=False, type="str", choices=["allow", "block", "erase"]), - signature_custom_signature_case_sensitivity=dict(required=False, type="str", choices=["disable", "enable"]), - signature_custom_signature_direction=dict(required=False, type="str", choices=["request", "response"]), - signature_custom_signature_log=dict(required=False, type="str", choices=["disable", "enable"]), - signature_custom_signature_name=dict(required=False, type="str"), - signature_custom_signature_pattern=dict(required=False, type="str"), - signature_custom_signature_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - signature_custom_signature_status=dict(required=False, type="str", choices=["disable", "enable"]), - signature_custom_signature_target=dict(required=False, type="str", choices=["arg", - "arg-name", - "req-body", - "req-cookie", - "req-cookie-name", - "req-filename", - "req-header", - "req-header-name", - "req-raw-uri", - "req-uri", - "resp-body", - "resp-hdr", - "resp-status"]), - - signature_main_class_action=dict(required=False, type="str", choices=["allow", "block", "erase"]), - signature_main_class_log=dict(required=False, type="str", choices=["disable", "enable"]), - signature_main_class_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - signature_main_class_status=dict(required=False, type="str", choices=["disable", "enable"]), - url_access=dict(required=False, type="list"), - url_access_action=dict(required=False, type="str", choices=["bypass", "permit", "block"]), - url_access_address=dict(required=False, type="str"), - url_access_log=dict(required=False, type="str", choices=["disable", "enable"]), - url_access_severity=dict(required=False, type="str", choices=["low", "medium", "high"]), - - url_access_access_pattern_negate=dict(required=False, type="str", choices=["disable", "enable"]), - url_access_access_pattern_pattern=dict(required=False, type="str"), - url_access_access_pattern_regex=dict(required=False, type="str", choices=["disable", "enable"]), - url_access_access_pattern_srcaddr=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "name": module.params["name"], - "external": module.params["external"], - "extended-log": module.params["extended_log"], - "comment": module.params["comment"], - "address-list": { - "blocked-address": module.params["address_list_blocked_address"], - "blocked-log": module.params["address_list_blocked_log"], - "severity": module.params["address_list_severity"], - "status": module.params["address_list_status"], - "trusted-address": module.params["address_list_trusted_address"], - }, - "constraint": { - "content-length": { - "action": module.params["constraint_content_length_action"], - "length": module.params["constraint_content_length_length"], - "log": module.params["constraint_content_length_log"], - "severity": module.params["constraint_content_length_severity"], - "status": module.params["constraint_content_length_status"], - }, - "exception": { - "address": module.params["constraint_exception_address"], - "content-length": module.params["constraint_exception_content_length"], - "header-length": module.params["constraint_exception_header_length"], - "hostname": module.params["constraint_exception_hostname"], - "line-length": module.params["constraint_exception_line_length"], - "malformed": module.params["constraint_exception_malformed"], - "max-cookie": module.params["constraint_exception_max_cookie"], - "max-header-line": module.params["constraint_exception_max_header_line"], - "max-range-segment": module.params["constraint_exception_max_range_segment"], - "max-url-param": module.params["constraint_exception_max_url_param"], - "method": module.params["constraint_exception_method"], - "param-length": module.params["constraint_exception_param_length"], - "pattern": module.params["constraint_exception_pattern"], - "regex": module.params["constraint_exception_regex"], - "url-param-length": module.params["constraint_exception_url_param_length"], - "version": module.params["constraint_exception_version"], - }, - "header-length": { - "action": module.params["constraint_header_length_action"], - "length": module.params["constraint_header_length_length"], - "log": module.params["constraint_header_length_log"], - "severity": module.params["constraint_header_length_severity"], - "status": module.params["constraint_header_length_status"], - }, - "hostname": { - "action": module.params["constraint_hostname_action"], - "log": module.params["constraint_hostname_log"], - "severity": module.params["constraint_hostname_severity"], - "status": module.params["constraint_hostname_status"], - }, - "line-length": { - "action": module.params["constraint_line_length_action"], - "length": module.params["constraint_line_length_length"], - "log": module.params["constraint_line_length_log"], - "severity": module.params["constraint_line_length_severity"], - "status": module.params["constraint_line_length_status"], - }, - "malformed": { - "action": module.params["constraint_malformed_action"], - "log": module.params["constraint_malformed_log"], - "severity": module.params["constraint_malformed_severity"], - "status": module.params["constraint_malformed_status"], - }, - "max-cookie": { - "action": module.params["constraint_max_cookie_action"], - "log": module.params["constraint_max_cookie_log"], - "max-cookie": module.params["constraint_max_cookie_max_cookie"], - "severity": module.params["constraint_max_cookie_severity"], - "status": module.params["constraint_max_cookie_status"], - }, - "max-header-line": { - "action": module.params["constraint_max_header_line_action"], - "log": module.params["constraint_max_header_line_log"], - "max-header-line": module.params["constraint_max_header_line_max_header_line"], - "severity": module.params["constraint_max_header_line_severity"], - "status": module.params["constraint_max_header_line_status"], - }, - "max-range-segment": { - "action": module.params["constraint_max_range_segment_action"], - "log": module.params["constraint_max_range_segment_log"], - "max-range-segment": module.params["constraint_max_range_segment_max_range_segment"], - "severity": module.params["constraint_max_range_segment_severity"], - "status": module.params["constraint_max_range_segment_status"], - }, - "max-url-param": { - "action": module.params["constraint_max_url_param_action"], - "log": module.params["constraint_max_url_param_log"], - "max-url-param": module.params["constraint_max_url_param_max_url_param"], - "severity": module.params["constraint_max_url_param_severity"], - "status": module.params["constraint_max_url_param_status"], - }, - "method": { - "action": module.params["constraint_method_action"], - "log": module.params["constraint_method_log"], - "severity": module.params["constraint_method_severity"], - "status": module.params["constraint_method_status"], - }, - "param-length": { - "action": module.params["constraint_param_length_action"], - "length": module.params["constraint_param_length_length"], - "log": module.params["constraint_param_length_log"], - "severity": module.params["constraint_param_length_severity"], - "status": module.params["constraint_param_length_status"], - }, - "url-param-length": { - "action": module.params["constraint_url_param_length_action"], - "length": module.params["constraint_url_param_length_length"], - "log": module.params["constraint_url_param_length_log"], - "severity": module.params["constraint_url_param_length_severity"], - "status": module.params["constraint_url_param_length_status"], - }, - "version": { - "action": module.params["constraint_version_action"], - "log": module.params["constraint_version_log"], - "severity": module.params["constraint_version_severity"], - "status": module.params["constraint_version_status"], - }, - }, - "method": { - "default-allowed-methods": module.params["method_default_allowed_methods"], - "log": module.params["method_log"], - "severity": module.params["method_severity"], - "status": module.params["method_status"], - "method-policy": { - "address": module.params["method_method_policy_address"], - "allowed-methods": module.params["method_method_policy_allowed_methods"], - "pattern": module.params["method_method_policy_pattern"], - "regex": module.params["method_method_policy_regex"], - }, - }, - "signature": { - "credit-card-detection-threshold": module.params["signature_credit_card_detection_threshold"], - "disabled-signature": module.params["signature_disabled_signature"], - "disabled-sub-class": module.params["signature_disabled_sub_class"], - "custom-signature": { - "action": module.params["signature_custom_signature_action"], - "case-sensitivity": module.params["signature_custom_signature_case_sensitivity"], - "direction": module.params["signature_custom_signature_direction"], - "log": module.params["signature_custom_signature_log"], - "name": module.params["signature_custom_signature_name"], - "pattern": module.params["signature_custom_signature_pattern"], - "severity": module.params["signature_custom_signature_severity"], - "status": module.params["signature_custom_signature_status"], - "target": module.params["signature_custom_signature_target"], - }, - "main-class": { - "action": module.params["signature_main_class_action"], - "log": module.params["signature_main_class_log"], - "severity": module.params["signature_main_class_severity"], - "status": module.params["signature_main_class_status"], - }, - }, - "url-access": { - "action": module.params["url_access_action"], - "address": module.params["url_access_address"], - "log": module.params["url_access_log"], - "severity": module.params["url_access_severity"], - "access-pattern": { - "negate": module.params["url_access_access_pattern_negate"], - "pattern": module.params["url_access_access_pattern_pattern"], - "regex": module.params["url_access_access_pattern_regex"], - "srcaddr": module.params["url_access_access_pattern_srcaddr"], - } - } - } - - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['address-list', 'constraint', 'method', 'signature', 'url-access'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - - try: - results = fmgr_waf_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_wanopt.py b/plugins/modules/network/fortimanager/fmgr_secprof_wanopt.py deleted file mode 100644 index 0b03e06a6c..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_wanopt.py +++ /dev/null @@ -1,689 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_wanopt -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: WAN optimization -description: - - Manage WanOpt security profiles in FortiManager via API - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - transparent: - description: - - Enable/disable transparent mode. - required: false - choices: - - disable - - enable - - name: - description: - - Profile name. - required: false - - comments: - description: - - Comment. - required: false - - auth_group: - description: - - Optionally add an authentication group to restrict access to the WAN Optimization tunnel to - peers in the authentication group. - required: false - - cifs: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - cifs_byte_caching: - description: - - Enable/disable byte-caching for HTTP. Byte caching reduces the amount of traffic by caching - file data sent across the WAN and in future serving if from the cache. - required: false - choices: - - disable - - enable - - cifs_log_traffic: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - cifs_port: - description: - - Single port number or port number range for CIFS. Only packets with a destination port number - that matches this port number or range are accepted by this profile. - required: false - - cifs_prefer_chunking: - description: - - Select dynamic or fixed-size data chunking for HTTP WAN Optimization. - required: false - choices: - - dynamic - - fix - - cifs_secure_tunnel: - description: - - Enable/disable securing the WAN Opt tunnel using SSL. Secure and non-secure tunnels use the - same TCP port (7810). - required: false - choices: - - disable - - enable - - cifs_status: - description: - - Enable/disable HTTP WAN Optimization. - required: false - choices: - - disable - - enable - - cifs_tunnel_sharing: - description: - - Tunnel sharing mode for aggressive/non-aggressive and/or interactive/non-interactive protocols. - required: false - choices: - - private - - shared - - express-shared - - ftp: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ftp_byte_caching: - description: - - Enable/disable byte-caching for HTTP. Byte caching reduces the amount of traffic by caching - file data sent across the WAN and in future serving if from the cache. - required: false - choices: - - disable - - enable - - ftp_log_traffic: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - ftp_port: - description: - - Single port number or port number range for FTP. Only packets with a destination port number - that matches this port number or range are accepted by this profile. - required: false - - ftp_prefer_chunking: - description: - - Select dynamic or fixed-size data chunking for HTTP WAN Optimization. - required: false - choices: - - dynamic - - fix - - ftp_secure_tunnel: - description: - - Enable/disable securing the WAN Opt tunnel using SSL. Secure and non-secure tunnels use the - same TCP port (7810). - required: false - choices: - - disable - - enable - - ftp_status: - description: - - Enable/disable HTTP WAN Optimization. - required: false - choices: - - disable - - enable - - ftp_tunnel_sharing: - description: - - Tunnel sharing mode for aggressive/non-aggressive and/or interactive/non-interactive protocols. - required: false - choices: - - private - - shared - - express-shared - - http: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - http_byte_caching: - description: - - Enable/disable byte-caching for HTTP. Byte caching reduces the amount of traffic by caching - file data sent across the WAN and in future serving if from the cache. - required: false - choices: - - disable - - enable - - http_log_traffic: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - http_port: - description: - - Single port number or port number range for HTTP. Only packets with a destination port number - that matches this port number or range are accepted by this profile. - required: false - - http_prefer_chunking: - description: - - Select dynamic or fixed-size data chunking for HTTP WAN Optimization. - required: false - choices: - - dynamic - - fix - - http_secure_tunnel: - description: - - Enable/disable securing the WAN Opt tunnel using SSL. Secure and non-secure tunnels use the - same TCP port (7810). - required: false - choices: - - disable - - enable - - http_ssl: - description: - - Enable/disable SSL/TLS offloading (hardware acceleration) for HTTPS traffic in this tunnel. - required: false - choices: - - disable - - enable - - http_ssl_port: - description: - - Port on which to expect HTTPS traffic for SSL/TLS offloading. - required: false - - http_status: - description: - - Enable/disable HTTP WAN Optimization. - required: false - choices: - - disable - - enable - - http_tunnel_non_http: - description: - - Configure how to process non-HTTP traffic when a profile configured for HTTP traffic accepts - a non-HTTP session. Can occur if an application sends non-HTTP traffic using an HTTP destination port. - required: false - choices: - - disable - - enable - - http_tunnel_sharing: - description: - - Tunnel sharing mode for aggressive/non-aggressive and/or interactive/non-interactive protocols. - required: false - choices: - - private - - shared - - express-shared - - http_unknown_http_version: - description: - - How to handle HTTP sessions that do not comply with HTTP 0.9, 1.0, or 1.1. - required: false - choices: - - best-effort - - reject - - tunnel - - mapi: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - mapi_byte_caching: - description: - - Enable/disable byte-caching for HTTP. Byte caching reduces the amount of traffic by caching - file data sent across the WAN and in future serving if from the cache. - required: false - choices: - - disable - - enable - - mapi_log_traffic: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - mapi_port: - description: - - Single port number or port number range for MAPI. Only packets with a destination port number - that matches this port number or range are accepted by this profile. - required: false - - mapi_secure_tunnel: - description: - - Enable/disable securing the WAN Opt tunnel using SSL. Secure and non-secure tunnels use the - same TCP port (7810). - required: false - choices: - - disable - - enable - - mapi_status: - description: - - Enable/disable HTTP WAN Optimization. - required: false - choices: - - disable - - enable - - mapi_tunnel_sharing: - description: - - Tunnel sharing mode for aggressive/non-aggressive and/or interactive/non-interactive protocols. - required: false - choices: - - private - - shared - - express-shared - - tcp: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - tcp_byte_caching: - description: - - Enable/disable byte-caching for HTTP. Byte caching reduces the amount of traffic by caching - file data sent across the WAN and in future serving if from the cache. - required: false - choices: - - disable - - enable - - tcp_byte_caching_opt: - description: - - Select whether TCP byte-caching uses system memory only or both memory and disk space. - required: false - choices: - - mem-only - - mem-disk - - tcp_log_traffic: - description: - - Enable/disable logging. - required: false - choices: - - disable - - enable - - tcp_port: - description: - - Single port number or port number range for TCP. Only packets with a destination port number - that matches this port number or range are accepted by this profile. - required: false - - tcp_secure_tunnel: - description: - - Enable/disable securing the WAN Opt tunnel using SSL. Secure and non-secure tunnels use the - same TCP port (7810). - required: false - choices: - - disable - - enable - - tcp_ssl: - description: - - Enable/disable SSL/TLS offloading. - required: false - choices: - - disable - - enable - - tcp_ssl_port: - description: - - Port on which to expect HTTPS traffic for SSL/TLS offloading. - required: false - - tcp_status: - description: - - Enable/disable HTTP WAN Optimization. - required: false - choices: - - disable - - enable - - tcp_tunnel_sharing: - description: - - Tunnel sharing mode for aggressive/non-aggressive and/or interactive/non-interactive protocols. - required: false - choices: - - private - - shared - - express-shared - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_wanopt: - name: "Ansible_WanOpt_Profile" - mode: "delete" - - - name: Create FMGR_WANOPT_PROFILE - fmgr_secprof_wanopt: - mode: "set" - adom: "root" - transparent: "enable" - name: "Ansible_WanOpt_Profile" - comments: "Created by Ansible" - cifs: {byte-caching: "enable", - log-traffic: "enable", - port: 80, - prefer-chunking: "dynamic", - status: "enable", - tunnel-sharing: "private"} - ftp: {byte-caching: "enable", - log-traffic: "enable", - port: 80, - prefer-chunking: "dynamic", - secure-tunnel: "disable", - status: "enable", - tunnel-sharing: "private"} -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -############### -# START METHODS -############### - - -def fmgr_wanopt_profile_modify(fmgr, paramgram): - """ - :param fmgr: The fmgr object instance from fortimanager.py - :type fmgr: class object - :param paramgram: The formatted dictionary of options to process - :type paramgram: dict - :return: The response from the FortiManager - :rtype: dict - """ - - mode = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/wanopt/profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/wanopt/profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - transparent=dict(required=False, type="str", choices=["disable", "enable"]), - name=dict(required=False, type="str"), - comments=dict(required=False, type="str"), - auth_group=dict(required=False, type="str"), - cifs=dict(required=False, type="dict"), - cifs_byte_caching=dict(required=False, type="str", choices=["disable", "enable"]), - cifs_log_traffic=dict(required=False, type="str", choices=["disable", "enable"]), - cifs_port=dict(required=False, type="str"), - cifs_prefer_chunking=dict(required=False, type="str", choices=["dynamic", "fix"]), - cifs_secure_tunnel=dict(required=False, type="str", choices=["disable", "enable"]), - cifs_status=dict(required=False, type="str", choices=["disable", "enable"]), - cifs_tunnel_sharing=dict(required=False, type="str", choices=["private", "shared", "express-shared"]), - ftp=dict(required=False, type="dict"), - ftp_byte_caching=dict(required=False, type="str", choices=["disable", "enable"]), - ftp_log_traffic=dict(required=False, type="str", choices=["disable", "enable"]), - ftp_port=dict(required=False, type="str"), - ftp_prefer_chunking=dict(required=False, type="str", choices=["dynamic", "fix"]), - ftp_secure_tunnel=dict(required=False, type="str", choices=["disable", "enable"]), - ftp_status=dict(required=False, type="str", choices=["disable", "enable"]), - ftp_tunnel_sharing=dict(required=False, type="str", choices=["private", "shared", "express-shared"]), - http=dict(required=False, type="dict"), - http_byte_caching=dict(required=False, type="str", choices=["disable", "enable"]), - http_log_traffic=dict(required=False, type="str", choices=["disable", "enable"]), - http_port=dict(required=False, type="str"), - http_prefer_chunking=dict(required=False, type="str", choices=["dynamic", "fix"]), - http_secure_tunnel=dict(required=False, type="str", choices=["disable", "enable"]), - http_ssl=dict(required=False, type="str", choices=["disable", "enable"]), - http_ssl_port=dict(required=False, type="str"), - http_status=dict(required=False, type="str", choices=["disable", "enable"]), - http_tunnel_non_http=dict(required=False, type="str", choices=["disable", "enable"]), - http_tunnel_sharing=dict(required=False, type="str", choices=["private", "shared", "express-shared"]), - http_unknown_http_version=dict(required=False, type="str", choices=["best-effort", "reject", "tunnel"]), - mapi=dict(required=False, type="dict"), - mapi_byte_caching=dict(required=False, type="str", choices=["disable", "enable"]), - mapi_log_traffic=dict(required=False, type="str", choices=["disable", "enable"]), - mapi_port=dict(required=False, type="str"), - mapi_secure_tunnel=dict(required=False, type="str", choices=["disable", "enable"]), - mapi_status=dict(required=False, type="str", choices=["disable", "enable"]), - mapi_tunnel_sharing=dict(required=False, type="str", choices=["private", "shared", "express-shared"]), - tcp=dict(required=False, type="dict"), - tcp_byte_caching=dict(required=False, type="str", choices=["disable", "enable"]), - tcp_byte_caching_opt=dict(required=False, type="str", choices=["mem-only", "mem-disk"]), - tcp_log_traffic=dict(required=False, type="str", choices=["disable", "enable"]), - tcp_port=dict(required=False, type="str"), - tcp_secure_tunnel=dict(required=False, type="str", choices=["disable", "enable"]), - tcp_ssl=dict(required=False, type="str", choices=["disable", "enable"]), - tcp_ssl_port=dict(required=False, type="str"), - tcp_status=dict(required=False, type="str", choices=["disable", "enable"]), - tcp_tunnel_sharing=dict(required=False, type="str", choices=["private", "shared", "express-shared"]), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "transparent": module.params["transparent"], - "name": module.params["name"], - "comments": module.params["comments"], - "auth-group": module.params["auth_group"], - "cifs": { - "byte-caching": module.params["cifs_byte_caching"], - "log-traffic": module.params["cifs_log_traffic"], - "port": module.params["cifs_port"], - "prefer-chunking": module.params["cifs_prefer_chunking"], - "secure-tunnel": module.params["cifs_secure_tunnel"], - "status": module.params["cifs_status"], - "tunnel-sharing": module.params["cifs_tunnel_sharing"], - }, - "ftp": { - "byte-caching": module.params["ftp_byte_caching"], - "log-traffic": module.params["ftp_log_traffic"], - "port": module.params["ftp_port"], - "prefer-chunking": module.params["ftp_prefer_chunking"], - "secure-tunnel": module.params["ftp_secure_tunnel"], - "status": module.params["ftp_status"], - "tunnel-sharing": module.params["ftp_tunnel_sharing"], - }, - "http": { - "byte-caching": module.params["http_byte_caching"], - "log-traffic": module.params["http_log_traffic"], - "port": module.params["http_port"], - "prefer-chunking": module.params["http_prefer_chunking"], - "secure-tunnel": module.params["http_secure_tunnel"], - "ssl": module.params["http_ssl"], - "ssl-port": module.params["http_ssl_port"], - "status": module.params["http_status"], - "tunnel-non-http": module.params["http_tunnel_non_http"], - "tunnel-sharing": module.params["http_tunnel_sharing"], - "unknown-http-version": module.params["http_unknown_http_version"], - }, - "mapi": { - "byte-caching": module.params["mapi_byte_caching"], - "log-traffic": module.params["mapi_log_traffic"], - "port": module.params["mapi_port"], - "secure-tunnel": module.params["mapi_secure_tunnel"], - "status": module.params["mapi_status"], - "tunnel-sharing": module.params["mapi_tunnel_sharing"], - }, - "tcp": { - "byte-caching": module.params["tcp_byte_caching"], - "byte-caching-opt": module.params["tcp_byte_caching_opt"], - "log-traffic": module.params["tcp_log_traffic"], - "port": module.params["tcp_port"], - "secure-tunnel": module.params["tcp_secure_tunnel"], - "ssl": module.params["tcp_ssl"], - "ssl-port": module.params["tcp_ssl_port"], - "status": module.params["tcp_status"], - "tunnel-sharing": module.params["tcp_tunnel_sharing"], - } - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['cifs', 'ftp', 'http', 'mapi', 'tcp'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - - try: - results = fmgr_wanopt_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/fortimanager/fmgr_secprof_web.py b/plugins/modules/network/fortimanager/fmgr_secprof_web.py deleted file mode 100644 index 7a3da4ff17..0000000000 --- a/plugins/modules/network/fortimanager/fmgr_secprof_web.py +++ /dev/null @@ -1,1085 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: fmgr_secprof_web -notes: - - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). -author: - - Luke Weighall (@lweighall) - - Andrew Welsh (@Ghilli3) - - Jim Huber (@p4r4n0y1ng) -short_description: Manage web filter security profiles in FortiManager -description: - - Manage web filter security profiles in FortiManager through playbooks using the FMG API - -options: - adom: - description: - - The ADOM the configuration should belong to. - required: false - default: root - - mode: - description: - - Sets one of three modes for managing the object. - - Allows use of soft-adds instead of overwriting existing values - choices: ['add', 'set', 'delete', 'update'] - required: false - default: add - - youtube_channel_status: - description: - - YouTube channel filter status. - - choice | disable | Disable YouTube channel filter. - - choice | blacklist | Block matches. - - choice | whitelist | Allow matches. - required: false - choices: ["disable", "blacklist", "whitelist"] - - wisp_servers: - description: - - WISP servers. - required: false - - wisp_algorithm: - description: - - WISP server selection algorithm. - - choice | auto-learning | Select the lightest loading healthy server. - - choice | primary-secondary | Select the first healthy server in order. - - choice | round-robin | Select the next healthy server. - required: false - choices: ["auto-learning", "primary-secondary", "round-robin"] - - wisp: - description: - - Enable/disable web proxy WISP. - - choice | disable | Disable web proxy WISP. - - choice | enable | Enable web proxy WISP. - required: false - choices: ["disable", "enable"] - - web_url_log: - description: - - Enable/disable logging URL filtering. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_invalid_domain_log: - description: - - Enable/disable logging invalid domain names. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_ftgd_quota_usage: - description: - - Enable/disable logging daily quota usage. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_ftgd_err_log: - description: - - Enable/disable logging rating errors. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_vbs_log: - description: - - Enable/disable logging VBS scripts. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_unknown_log: - description: - - Enable/disable logging unknown scripts. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_referer_log: - description: - - Enable/disable logging referrers. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_jscript_log: - description: - - Enable/disable logging JScripts. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_js_log: - description: - - Enable/disable logging Java scripts. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_cookie_removal_log: - description: - - Enable/disable logging blocked cookies. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_cookie_log: - description: - - Enable/disable logging cookie filtering. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_command_block_log: - description: - - Enable/disable logging blocked commands. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_applet_log: - description: - - Enable/disable logging Java applets. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_filter_activex_log: - description: - - Enable/disable logging ActiveX. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_extended_all_action_log: - description: - - Enable/disable extended any filter action logging for web filtering. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_content_log: - description: - - Enable/disable logging logging blocked web content. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - replacemsg_group: - description: - - Replacement message group. - required: false - - post_action: - description: - - Action taken for HTTP POST traffic. - - choice | normal | Normal, POST requests are allowed. - - choice | block | POST requests are blocked. - required: false - choices: ["normal", "block"] - - ovrd_perm: - description: - - FLAG Based Options. Specify multiple in list form. - - flag | bannedword-override | Banned word override. - - flag | urlfilter-override | URL filter override. - - flag | fortiguard-wf-override | FortiGuard Web Filter override. - - flag | contenttype-check-override | Content-type header override. - required: false - choices: - - bannedword-override - - urlfilter-override - - fortiguard-wf-override - - contenttype-check-override - - options: - description: - - FLAG Based Options. Specify multiple in list form. - - flag | block-invalid-url | Block sessions contained an invalid domain name. - - flag | jscript | Javascript block. - - flag | js | JS block. - - flag | vbs | VB script block. - - flag | unknown | Unknown script block. - - flag | wf-referer | Referring block. - - flag | intrinsic | Intrinsic script block. - - flag | wf-cookie | Cookie block. - - flag | per-user-bwl | Per-user black/white list filter - - flag | activexfilter | ActiveX filter. - - flag | cookiefilter | Cookie filter. - - flag | javafilter | Java applet filter. - required: false - choices: - - block-invalid-url - - jscript - - js - - vbs - - unknown - - wf-referer - - intrinsic - - wf-cookie - - per-user-bwl - - activexfilter - - cookiefilter - - javafilter - - name: - description: - - Profile name. - required: false - - log_all_url: - description: - - Enable/disable logging all URLs visited. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - inspection_mode: - description: - - Web filtering inspection mode. - - choice | proxy | Proxy. - - choice | flow-based | Flow based. - required: false - choices: ["proxy", "flow-based"] - - https_replacemsg: - description: - - Enable replacement messages for HTTPS. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - extended_log: - description: - - Enable/disable extended logging for web filtering. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - comment: - description: - - Optional comments. - required: false - - ftgd_wf: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - ftgd_wf_exempt_quota: - description: - - Do not stop quota for these categories. - required: false - - ftgd_wf_max_quota_timeout: - description: - - Maximum FortiGuard quota used by single page view in seconds (excludes streams). - required: false - - ftgd_wf_options: - description: - - Options for FortiGuard Web Filter. - - FLAG Based Options. Specify multiple in list form. - - flag | error-allow | Allow web pages with a rating error to pass through. - - flag | rate-server-ip | Rate the server IP in addition to the domain name. - - flag | connect-request-bypass | Bypass connection which has CONNECT request. - - flag | ftgd-disable | Disable FortiGuard scanning. - required: false - choices: ["error-allow", "rate-server-ip", "connect-request-bypass", "ftgd-disable"] - - ftgd_wf_ovrd: - description: - - Allow web filter profile overrides. - required: false - - ftgd_wf_rate_crl_urls: - description: - - Enable/disable rating CRL by URL. - - choice | disable | Disable rating CRL by URL. - - choice | enable | Enable rating CRL by URL. - required: false - choices: ["disable", "enable"] - - ftgd_wf_rate_css_urls: - description: - - Enable/disable rating CSS by URL. - - choice | disable | Disable rating CSS by URL. - - choice | enable | Enable rating CSS by URL. - required: false - choices: ["disable", "enable"] - - ftgd_wf_rate_image_urls: - description: - - Enable/disable rating images by URL. - - choice | disable | Disable rating images by URL (blocked images are replaced with blanks). - - choice | enable | Enable rating images by URL (blocked images are replaced with blanks). - required: false - choices: ["disable", "enable"] - - ftgd_wf_rate_javascript_urls: - description: - - Enable/disable rating JavaScript by URL. - - choice | disable | Disable rating JavaScript by URL. - - choice | enable | Enable rating JavaScript by URL. - required: false - choices: ["disable", "enable"] - - ftgd_wf_filters_action: - description: - - Action to take for matches. - - choice | block | Block access. - - choice | monitor | Allow access while logging the action. - - choice | warning | Allow access after warning the user. - - choice | authenticate | Authenticate user before allowing access. - required: false - choices: ["block", "monitor", "warning", "authenticate"] - - ftgd_wf_filters_auth_usr_grp: - description: - - Groups with permission to authenticate. - required: false - - ftgd_wf_filters_category: - description: - - Categories and groups the filter examines. - required: false - - ftgd_wf_filters_log: - description: - - Enable/disable logging. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - ftgd_wf_filters_override_replacemsg: - description: - - Override replacement message. - required: false - - ftgd_wf_filters_warn_duration: - description: - - Duration of warnings. - required: false - - ftgd_wf_filters_warning_duration_type: - description: - - Re-display warning after closing browser or after a timeout. - - choice | session | After session ends. - - choice | timeout | After timeout occurs. - required: false - choices: ["session", "timeout"] - - ftgd_wf_filters_warning_prompt: - description: - - Warning prompts in each category or each domain. - - choice | per-domain | Per-domain warnings. - - choice | per-category | Per-category warnings. - required: false - choices: ["per-domain", "per-category"] - - ftgd_wf_quota_category: - description: - - FortiGuard categories to apply quota to (category action must be set to monitor). - required: false - - ftgd_wf_quota_duration: - description: - - Duration of quota. - required: false - - ftgd_wf_quota_override_replacemsg: - description: - - Override replacement message. - required: false - - ftgd_wf_quota_type: - description: - - Quota type. - - choice | time | Use a time-based quota. - - choice | traffic | Use a traffic-based quota. - required: false - choices: ["time", "traffic"] - - ftgd_wf_quota_unit: - description: - - Traffic quota unit of measurement. - - choice | B | Quota in bytes. - - choice | KB | Quota in kilobytes. - - choice | MB | Quota in megabytes. - - choice | GB | Quota in gigabytes. - required: false - choices: ["B", "KB", "MB", "GB"] - - ftgd_wf_quota_value: - description: - - Traffic quota value. - required: false - - override: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - override_ovrd_cookie: - description: - - Allow/deny browser-based (cookie) overrides. - - choice | deny | Deny browser-based (cookie) override. - - choice | allow | Allow browser-based (cookie) override. - required: false - choices: ["deny", "allow"] - - override_ovrd_dur: - description: - - Override duration. - required: false - - override_ovrd_dur_mode: - description: - - Override duration mode. - - choice | constant | Constant mode. - - choice | ask | Prompt for duration when initiating an override. - required: false - choices: ["constant", "ask"] - - override_ovrd_scope: - description: - - Override scope. - - choice | user | Override for the user. - - choice | user-group | Override for the user's group. - - choice | ip | Override for the initiating IP. - - choice | ask | Prompt for scope when initiating an override. - - choice | browser | Create browser-based (cookie) override. - required: false - choices: ["user", "user-group", "ip", "ask", "browser"] - - override_ovrd_user_group: - description: - - User groups with permission to use the override. - required: false - - override_profile: - description: - - Web filter profile with permission to create overrides. - required: false - - override_profile_attribute: - description: - - Profile attribute to retrieve from the RADIUS server. - - choice | User-Name | Use this attribute. - - choice | NAS-IP-Address | Use this attribute. - - choice | Framed-IP-Address | Use this attribute. - - choice | Framed-IP-Netmask | Use this attribute. - - choice | Filter-Id | Use this attribute. - - choice | Login-IP-Host | Use this attribute. - - choice | Reply-Message | Use this attribute. - - choice | Callback-Number | Use this attribute. - - choice | Callback-Id | Use this attribute. - - choice | Framed-Route | Use this attribute. - - choice | Framed-IPX-Network | Use this attribute. - - choice | Class | Use this attribute. - - choice | Called-Station-Id | Use this attribute. - - choice | Calling-Station-Id | Use this attribute. - - choice | NAS-Identifier | Use this attribute. - - choice | Proxy-State | Use this attribute. - - choice | Login-LAT-Service | Use this attribute. - - choice | Login-LAT-Node | Use this attribute. - - choice | Login-LAT-Group | Use this attribute. - - choice | Framed-AppleTalk-Zone | Use this attribute. - - choice | Acct-Session-Id | Use this attribute. - - choice | Acct-Multi-Session-Id | Use this attribute. - required: false - choices: - - User-Name - - NAS-IP-Address - - Framed-IP-Address - - Framed-IP-Netmask - - Filter-Id - - Login-IP-Host - - Reply-Message - - Callback-Number - - Callback-Id - - Framed-Route - - Framed-IPX-Network - - Class - - Called-Station-Id - - Calling-Station-Id - - NAS-Identifier - - Proxy-State - - Login-LAT-Service - - Login-LAT-Node - - Login-LAT-Group - - Framed-AppleTalk-Zone - - Acct-Session-Id - - Acct-Multi-Session-Id - - override_profile_type: - description: - - Override profile type. - - choice | list | Profile chosen from list. - - choice | radius | Profile determined by RADIUS server. - required: false - choices: ["list", "radius"] - - url_extraction: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - url_extraction_redirect_header: - description: - - HTTP header name to use for client redirect on blocked requests - required: false - - url_extraction_redirect_no_content: - description: - - Enable / Disable empty message-body entity in HTTP response - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - url_extraction_redirect_url: - description: - - HTTP header value to use for client redirect on blocked requests - required: false - - url_extraction_server_fqdn: - description: - - URL extraction server FQDN (fully qualified domain name) - required: false - - url_extraction_status: - description: - - Enable URL Extraction - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - web_blacklist: - description: - - Enable/disable automatic addition of URLs detected by FortiSandbox to blacklist. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_bword_table: - description: - - Banned word table ID. - required: false - - web_bword_threshold: - description: - - Banned word score threshold. - required: false - - web_content_header_list: - description: - - Content header list. - required: false - - web_keyword_match: - description: - - Search keywords to log when match is found. - required: false - - web_log_search: - description: - - Enable/disable logging all search phrases. - - choice | disable | Disable setting. - - choice | enable | Enable setting. - required: false - choices: ["disable", "enable"] - - web_safe_search: - description: - - Safe search type. - - FLAG Based Options. Specify multiple in list form. - - flag | url | Insert safe search string into URL. - - flag | header | Insert safe search header. - required: false - choices: ["url", "header"] - - web_urlfilter_table: - description: - - URL filter table ID. - required: false - - web_whitelist: - description: - - FortiGuard whitelist settings. - - FLAG Based Options. Specify multiple in list form. - - flag | exempt-av | Exempt antivirus. - - flag | exempt-webcontent | Exempt web content. - - flag | exempt-activex-java-cookie | Exempt ActiveX-JAVA-Cookie. - - flag | exempt-dlp | Exempt DLP. - - flag | exempt-rangeblock | Exempt RangeBlock. - - flag | extended-log-others | Support extended log. - required: false - choices: - - exempt-av - - exempt-webcontent - - exempt-activex-java-cookie - - exempt-dlp - - exempt-rangeblock - - extended-log-others - - web_youtube_restrict: - description: - - YouTube EDU filter level. - - choice | strict | Strict access for YouTube. - - choice | none | Full access for YouTube. - - choice | moderate | Moderate access for YouTube. - required: false - choices: ["strict", "none", "moderate"] - - youtube_channel_filter: - description: - - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! - - List of multiple child objects to be added. Expects a list of dictionaries. - - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. - - If submitted, all other prefixed sub-parameters ARE IGNORED. - - This object is MUTUALLY EXCLUSIVE with its options. - - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. - - WHEN IN DOUBT, USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS - required: false - - youtube_channel_filter_channel_id: - description: - - YouTube channel ID to be filtered. - required: false - - youtube_channel_filter_comment: - description: - - Comment. - required: false - - -''' - -EXAMPLES = ''' - - name: DELETE Profile - fmgr_secprof_web: - name: "Ansible_Web_Filter_Profile" - mode: "delete" - - - name: CREATE Profile - fmgr_secprof_web: - name: "Ansible_Web_Filter_Profile" - comment: "Created by Ansible Module TEST" - mode: "set" - extended_log: "enable" - inspection_mode: "proxy" - log_all_url: "enable" - options: "js" - ovrd_perm: "bannedword-override" - post_action: "block" - web_content_log: "enable" - web_extended_all_action_log: "enable" - web_filter_activex_log: "enable" - web_filter_applet_log: "enable" - web_filter_command_block_log: "enable" - web_filter_cookie_log: "enable" - web_filter_cookie_removal_log: "enable" - web_filter_js_log: "enable" - web_filter_jscript_log: "enable" - web_filter_referer_log: "enable" - web_filter_unknown_log: "enable" - web_filter_vbs_log: "enable" - web_ftgd_err_log: "enable" - web_ftgd_quota_usage: "enable" - web_invalid_domain_log: "enable" - web_url_log: "enable" - wisp: "enable" - wisp_algorithm: "auto-learning" - youtube_channel_status: "blacklist" -''' - -RETURN = """ -api_result: - description: full API response, includes status code and message - returned: always - type: str -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.fortimanager import FortiManagerHandler -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGBaseException -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRCommon -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FMGRMethods -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import DEFAULT_RESULT_OBJ -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import prepare_dict -from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import scrub_dict - - -def fmgr_webfilter_profile_modify(fmgr, paramgram): - - mode = paramgram["mode"] - adom = paramgram["adom"] - - response = DEFAULT_RESULT_OBJ - url = "" - datagram = {} - - # EVAL THE MODE PARAMETER FOR SET OR ADD - if mode in ['set', 'add', 'update']: - url = '/pm/config/adom/{adom}/obj/webfilter/profile'.format(adom=adom) - datagram = scrub_dict(prepare_dict(paramgram)) - - # EVAL THE MODE PARAMETER FOR DELETE - elif mode == "delete": - # SET THE CORRECT URL FOR DELETE - url = '/pm/config/adom/{adom}/obj/webfilter/profile/{name}'.format(adom=adom, name=paramgram["name"]) - datagram = {} - - response = fmgr.process_request(url, datagram, paramgram["mode"]) - - return response - - -############# -# END METHODS -############# - - -def main(): - argument_spec = dict( - adom=dict(type="str", default="root"), - mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), - - youtube_channel_status=dict(required=False, type="str", choices=["disable", "blacklist", "whitelist"]), - wisp_servers=dict(required=False, type="str"), - wisp_algorithm=dict(required=False, type="str", choices=["auto-learning", "primary-secondary", "round-robin"]), - wisp=dict(required=False, type="str", choices=["disable", "enable"]), - web_url_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_invalid_domain_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_ftgd_quota_usage=dict(required=False, type="str", choices=["disable", "enable"]), - web_ftgd_err_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_vbs_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_unknown_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_referer_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_jscript_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_js_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_cookie_removal_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_cookie_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_command_block_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_applet_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_filter_activex_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_extended_all_action_log=dict(required=False, type="str", choices=["disable", "enable"]), - web_content_log=dict(required=False, type="str", choices=["disable", "enable"]), - replacemsg_group=dict(required=False, type="str"), - post_action=dict(required=False, type="str", choices=["normal", "block"]), - ovrd_perm=dict(required=False, type="list", choices=["bannedword-override", - "urlfilter-override", - "fortiguard-wf-override", - "contenttype-check-override"]), - options=dict(required=False, type="list", choices=["block-invalid-url", - "jscript", - "js", - "vbs", - "unknown", - "wf-referer", - "intrinsic", - "wf-cookie", - "per-user-bwl", - "activexfilter", - "cookiefilter", - "javafilter"]), - name=dict(required=False, type="str"), - log_all_url=dict(required=False, type="str", choices=["disable", "enable"]), - inspection_mode=dict(required=False, type="str", choices=["proxy", "flow-based"]), - https_replacemsg=dict(required=False, type="str", choices=["disable", "enable"]), - extended_log=dict(required=False, type="str", choices=["disable", "enable"]), - comment=dict(required=False, type="str"), - ftgd_wf=dict(required=False, type="list"), - ftgd_wf_exempt_quota=dict(required=False, type="str"), - ftgd_wf_max_quota_timeout=dict(required=False, type="int"), - ftgd_wf_options=dict(required=False, type="str", choices=["error-allow", "rate-server-ip", - "connect-request-bypass", "ftgd-disable"]), - ftgd_wf_ovrd=dict(required=False, type="str"), - ftgd_wf_rate_crl_urls=dict(required=False, type="str", choices=["disable", "enable"]), - ftgd_wf_rate_css_urls=dict(required=False, type="str", choices=["disable", "enable"]), - ftgd_wf_rate_image_urls=dict(required=False, type="str", choices=["disable", "enable"]), - ftgd_wf_rate_javascript_urls=dict(required=False, type="str", choices=["disable", "enable"]), - - ftgd_wf_filters_action=dict(required=False, type="str", choices=["block", "monitor", - "warning", "authenticate"]), - ftgd_wf_filters_auth_usr_grp=dict(required=False, type="str"), - ftgd_wf_filters_category=dict(required=False, type="str"), - ftgd_wf_filters_log=dict(required=False, type="str", choices=["disable", "enable"]), - ftgd_wf_filters_override_replacemsg=dict(required=False, type="str"), - ftgd_wf_filters_warn_duration=dict(required=False, type="str"), - ftgd_wf_filters_warning_duration_type=dict(required=False, type="str", choices=["session", "timeout"]), - ftgd_wf_filters_warning_prompt=dict(required=False, type="str", choices=["per-domain", "per-category"]), - - ftgd_wf_quota_category=dict(required=False, type="str"), - ftgd_wf_quota_duration=dict(required=False, type="str"), - ftgd_wf_quota_override_replacemsg=dict(required=False, type="str"), - ftgd_wf_quota_type=dict(required=False, type="str", choices=["time", "traffic"]), - ftgd_wf_quota_unit=dict(required=False, type="str", choices=["B", "KB", "MB", "GB"]), - ftgd_wf_quota_value=dict(required=False, type="int"), - override=dict(required=False, type="list"), - override_ovrd_cookie=dict(required=False, type="str", choices=["deny", "allow"]), - override_ovrd_dur=dict(required=False, type="str"), - override_ovrd_dur_mode=dict(required=False, type="str", choices=["constant", "ask"]), - override_ovrd_scope=dict(required=False, type="str", choices=["user", "user-group", "ip", "ask", "browser"]), - override_ovrd_user_group=dict(required=False, type="str"), - override_profile=dict(required=False, type="str"), - override_profile_attribute=dict(required=False, type="list", choices=["User-Name", - "NAS-IP-Address", - "Framed-IP-Address", - "Framed-IP-Netmask", - "Filter-Id", - "Login-IP-Host", - "Reply-Message", - "Callback-Number", - "Callback-Id", - "Framed-Route", - "Framed-IPX-Network", - "Class", - "Called-Station-Id", - "Calling-Station-Id", - "NAS-Identifier", - "Proxy-State", - "Login-LAT-Service", - "Login-LAT-Node", - "Login-LAT-Group", - "Framed-AppleTalk-Zone", - "Acct-Session-Id", - "Acct-Multi-Session-Id"]), - override_profile_type=dict(required=False, type="str", choices=["list", "radius"]), - url_extraction=dict(required=False, type="list"), - url_extraction_redirect_header=dict(required=False, type="str"), - url_extraction_redirect_no_content=dict(required=False, type="str", choices=["disable", "enable"]), - url_extraction_redirect_url=dict(required=False, type="str"), - url_extraction_server_fqdn=dict(required=False, type="str"), - url_extraction_status=dict(required=False, type="str", choices=["disable", "enable"]), - web=dict(required=False, type="list"), - web_blacklist=dict(required=False, type="str", choices=["disable", "enable"]), - web_bword_table=dict(required=False, type="str"), - web_bword_threshold=dict(required=False, type="int"), - web_content_header_list=dict(required=False, type="str"), - web_keyword_match=dict(required=False, type="str"), - web_log_search=dict(required=False, type="str", choices=["disable", "enable"]), - web_safe_search=dict(required=False, type="str", choices=["url", "header"]), - web_urlfilter_table=dict(required=False, type="str"), - web_whitelist=dict(required=False, type="list", choices=["exempt-av", - "exempt-webcontent", - "exempt-activex-java-cookie", - "exempt-dlp", - "exempt-rangeblock", - "extended-log-others"]), - web_youtube_restrict=dict(required=False, type="str", choices=["strict", "none", "moderate"]), - youtube_channel_filter=dict(required=False, type="list"), - youtube_channel_filter_channel_id=dict(required=False, type="str"), - youtube_channel_filter_comment=dict(required=False, type="str"), - - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) - # MODULE PARAMGRAM - paramgram = { - "mode": module.params["mode"], - "adom": module.params["adom"], - "youtube-channel-status": module.params["youtube_channel_status"], - "wisp-servers": module.params["wisp_servers"], - "wisp-algorithm": module.params["wisp_algorithm"], - "wisp": module.params["wisp"], - "web-url-log": module.params["web_url_log"], - "web-invalid-domain-log": module.params["web_invalid_domain_log"], - "web-ftgd-quota-usage": module.params["web_ftgd_quota_usage"], - "web-ftgd-err-log": module.params["web_ftgd_err_log"], - "web-filter-vbs-log": module.params["web_filter_vbs_log"], - "web-filter-unknown-log": module.params["web_filter_unknown_log"], - "web-filter-referer-log": module.params["web_filter_referer_log"], - "web-filter-jscript-log": module.params["web_filter_jscript_log"], - "web-filter-js-log": module.params["web_filter_js_log"], - "web-filter-cookie-removal-log": module.params["web_filter_cookie_removal_log"], - "web-filter-cookie-log": module.params["web_filter_cookie_log"], - "web-filter-command-block-log": module.params["web_filter_command_block_log"], - "web-filter-applet-log": module.params["web_filter_applet_log"], - "web-filter-activex-log": module.params["web_filter_activex_log"], - "web-extended-all-action-log": module.params["web_extended_all_action_log"], - "web-content-log": module.params["web_content_log"], - "replacemsg-group": module.params["replacemsg_group"], - "post-action": module.params["post_action"], - "ovrd-perm": module.params["ovrd_perm"], - "options": module.params["options"], - "name": module.params["name"], - "log-all-url": module.params["log_all_url"], - "inspection-mode": module.params["inspection_mode"], - "https-replacemsg": module.params["https_replacemsg"], - "extended-log": module.params["extended_log"], - "comment": module.params["comment"], - "ftgd-wf": { - "exempt-quota": module.params["ftgd_wf_exempt_quota"], - "max-quota-timeout": module.params["ftgd_wf_max_quota_timeout"], - "options": module.params["ftgd_wf_options"], - "ovrd": module.params["ftgd_wf_ovrd"], - "rate-crl-urls": module.params["ftgd_wf_rate_crl_urls"], - "rate-css-urls": module.params["ftgd_wf_rate_css_urls"], - "rate-image-urls": module.params["ftgd_wf_rate_image_urls"], - "rate-javascript-urls": module.params["ftgd_wf_rate_javascript_urls"], - "filters": { - "action": module.params["ftgd_wf_filters_action"], - "auth-usr-grp": module.params["ftgd_wf_filters_auth_usr_grp"], - "category": module.params["ftgd_wf_filters_category"], - "log": module.params["ftgd_wf_filters_log"], - "override-replacemsg": module.params["ftgd_wf_filters_override_replacemsg"], - "warn-duration": module.params["ftgd_wf_filters_warn_duration"], - "warning-duration-type": module.params["ftgd_wf_filters_warning_duration_type"], - "warning-prompt": module.params["ftgd_wf_filters_warning_prompt"], - }, - "quota": { - "category": module.params["ftgd_wf_quota_category"], - "duration": module.params["ftgd_wf_quota_duration"], - "override-replacemsg": module.params["ftgd_wf_quota_override_replacemsg"], - "type": module.params["ftgd_wf_quota_type"], - "unit": module.params["ftgd_wf_quota_unit"], - "value": module.params["ftgd_wf_quota_value"], - }, - }, - "override": { - "ovrd-cookie": module.params["override_ovrd_cookie"], - "ovrd-dur": module.params["override_ovrd_dur"], - "ovrd-dur-mode": module.params["override_ovrd_dur_mode"], - "ovrd-scope": module.params["override_ovrd_scope"], - "ovrd-user-group": module.params["override_ovrd_user_group"], - "profile": module.params["override_profile"], - "profile-attribute": module.params["override_profile_attribute"], - "profile-type": module.params["override_profile_type"], - }, - "url-extraction": { - "redirect-header": module.params["url_extraction_redirect_header"], - "redirect-no-content": module.params["url_extraction_redirect_no_content"], - "redirect-url": module.params["url_extraction_redirect_url"], - "server-fqdn": module.params["url_extraction_server_fqdn"], - "status": module.params["url_extraction_status"], - }, - "web": { - "blacklist": module.params["web_blacklist"], - "bword-table": module.params["web_bword_table"], - "bword-threshold": module.params["web_bword_threshold"], - "content-header-list": module.params["web_content_header_list"], - "keyword-match": module.params["web_keyword_match"], - "log-search": module.params["web_log_search"], - "safe-search": module.params["web_safe_search"], - "urlfilter-table": module.params["web_urlfilter_table"], - "whitelist": module.params["web_whitelist"], - "youtube-restrict": module.params["web_youtube_restrict"], - }, - "youtube-channel-filter": { - "channel-id": module.params["youtube_channel_filter_channel_id"], - "comment": module.params["youtube_channel_filter_comment"], - } - } - module.paramgram = paramgram - fmgr = None - if module._socket_path: - connection = Connection(module._socket_path) - fmgr = FortiManagerHandler(connection, module) - fmgr.tools = FMGRCommon() - else: - module.fail_json(**FAIL_SOCKET_MSG) - - list_overrides = ['ftgd-wf', 'override', 'url-extraction', 'web', 'youtube-channel-filter'] - paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, - paramgram=paramgram, module=module) - - results = DEFAULT_RESULT_OBJ - - try: - - results = fmgr_webfilter_profile_modify(fmgr, paramgram) - fmgr.govern_response(module=module, results=results, - ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) - - except Exception as err: - raise FMGBaseException(err) - - return module.exit_json(**results[1]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/ftd/ftd_configuration.py b/plugins/modules/network/ftd/ftd_configuration.py deleted file mode 100644 index 41cded30d8..0000000000 --- a/plugins/modules/network/ftd/ftd_configuration.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2018 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ftd_configuration -short_description: Manages configuration on Cisco FTD devices over REST API -description: - - Manages configuration on Cisco FTD devices including creating, updating, removing configuration objects, - scheduling and staring jobs, deploying pending changes, etc. All operations are performed over REST API. -author: "Cisco Systems, Inc. (@annikulin)" -options: - operation: - description: - - The name of the operation to execute. Commonly, the operation starts with 'add', 'edit', 'get', 'upsert' - or 'delete' verbs, but can have an arbitrary name too. - required: true - type: str - data: - description: - - Key-value pairs that should be sent as body parameters in a REST API call - type: dict - query_params: - description: - - Key-value pairs that should be sent as query parameters in a REST API call. - type: dict - path_params: - description: - - Key-value pairs that should be sent as path parameters in a REST API call. - type: dict - register_as: - description: - - Specifies Ansible fact name that is used to register received response from the FTD device. - type: str - filters: - description: - - Key-value dict that represents equality filters. Every key is a property name and value is its desired value. - If multiple filters are present, they are combined with logical operator AND. - type: dict -''' - -EXAMPLES = """ -- name: Create a network object - ftd_configuration: - operation: "addNetworkObject" - data: - name: "Ansible-network-host" - description: "From Ansible with love" - subType: "HOST" - value: "192.168.2.0" - dnsResolution: "IPV4_AND_IPV6" - type: "networkobject" - isSystemDefined: false - register_as: "hostNetwork" - -- name: Delete the network object - ftd_configuration: - operation: "deleteNetworkObject" - path_params: - objId: "{{ hostNetwork['id'] }}" -""" - -RETURN = """ -response: - description: HTTP response returned from the API call. - returned: success - type: dict -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.community.general.plugins.module_utils.network.ftd.configuration import BaseConfigurationResource, CheckModeException, \ - FtdInvalidOperationNameError -from ansible_collections.community.general.plugins.module_utils.network.ftd.fdm_swagger_client import ValidationError -from ansible_collections.community.general.plugins.module_utils.network.ftd.common import construct_ansible_facts, FtdConfigurationError, \ - FtdServerError, FtdUnexpectedResponse - - -def main(): - fields = dict( - operation=dict(type='str', required=True), - data=dict(type='dict'), - query_params=dict(type='dict'), - path_params=dict(type='dict'), - register_as=dict(type='str'), - filters=dict(type='dict') - ) - module = AnsibleModule(argument_spec=fields, - supports_check_mode=True) - params = module.params - - connection = Connection(module._socket_path) - resource = BaseConfigurationResource(connection, module.check_mode) - op_name = params['operation'] - try: - resp = resource.execute_operation(op_name, params) - module.exit_json(changed=resource.config_changed, response=resp, - ansible_facts=construct_ansible_facts(resp, module.params)) - except FtdInvalidOperationNameError as e: - module.fail_json(msg='Invalid operation name provided: %s' % e.operation_name) - except FtdConfigurationError as e: - module.fail_json(msg='Failed to execute %s operation because of the configuration error: %s' % (op_name, e.msg)) - except FtdServerError as e: - module.fail_json(msg='Server returned an error trying to execute %s operation. Status code: %s. ' - 'Server response: %s' % (op_name, e.code, e.response)) - except FtdUnexpectedResponse as e: - module.fail_json(msg=e.args[0]) - except ValidationError as e: - module.fail_json(msg=e.args[0]) - except CheckModeException: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ftd/ftd_file_download.py b/plugins/modules/network/ftd/ftd_file_download.py deleted file mode 100644 index 18c8b15633..0000000000 --- a/plugins/modules/network/ftd/ftd_file_download.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2018 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ftd_file_download -short_description: Downloads files from Cisco FTD devices over HTTP(S) -description: - - Downloads files from Cisco FTD devices including pending changes, disk files, certificates, - troubleshoot reports, and backups. -author: "Cisco Systems, Inc. (@annikulin)" -options: - operation: - description: - - The name of the operation to execute. - - Only operations that return a file can be used in this module. - required: true - type: str - path_params: - description: - - Key-value pairs that should be sent as path parameters in a REST API call. - type: dict - destination: - description: - - Absolute path of where to download the file to. - - If destination is a directory, the module uses a filename from 'Content-Disposition' header specified by - the server. - required: true - type: path -''' - -EXAMPLES = """ -- name: Download pending changes - ftd_file_download: - operation: 'getdownload' - path_params: - objId: 'default' - destination: /tmp/ -""" - -RETURN = """ -msg: - description: The error message describing why the module failed. - returned: error - type: str -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.community.general.plugins.module_utils.network.ftd.common import FtdServerError, HTTPMethod -from ansible_collections.community.general.plugins.module_utils.network.ftd.fdm_swagger_client import OperationField, ValidationError, FILE_MODEL_NAME - - -def is_download_operation(op_spec): - return op_spec[OperationField.METHOD] == HTTPMethod.GET and op_spec[OperationField.MODEL_NAME] == FILE_MODEL_NAME - - -def validate_params(connection, op_name, path_params): - field_name = 'Invalid path_params provided' - try: - is_valid, validation_report = connection.validate_path_params(op_name, path_params) - if not is_valid: - raise ValidationError({ - field_name: validation_report - }) - except Exception as e: - raise ValidationError({ - field_name: str(e) - }) - - -def main(): - fields = dict( - operation=dict(type='str', required=True), - path_params=dict(type='dict'), - destination=dict(type='path', required=True) - ) - module = AnsibleModule(argument_spec=fields, - supports_check_mode=True) - params = module.params - connection = Connection(module._socket_path) - - op_name = params['operation'] - op_spec = connection.get_operation_spec(op_name) - if op_spec is None: - module.fail_json(msg='Operation with specified name is not found: %s' % op_name) - if not is_download_operation(op_spec): - module.fail_json( - msg='Invalid download operation: %s. The operation must make GET request and return a file.' % - op_name) - - try: - path_params = params['path_params'] - validate_params(connection, op_name, path_params) - if module.check_mode: - module.exit_json(changed=False) - connection.download_file(op_spec[OperationField.URL], params['destination'], path_params) - module.exit_json(changed=False) - except FtdServerError as e: - module.fail_json(msg='Download request for %s operation failed. Status code: %s. ' - 'Server response: %s' % (op_name, e.code, e.response)) - except ValidationError as e: - module.fail_json(msg=e.args[0]) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ftd/ftd_file_upload.py b/plugins/modules/network/ftd/ftd_file_upload.py deleted file mode 100644 index 50062d8c1f..0000000000 --- a/plugins/modules/network/ftd/ftd_file_upload.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2018 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ftd_file_upload -short_description: Uploads files to Cisco FTD devices over HTTP(S) -description: - - Uploads files to Cisco FTD devices including disk files, backups, and upgrades. -author: "Cisco Systems, Inc. (@annikulin)" -options: - operation: - description: - - The name of the operation to execute. - - Only operations that upload file can be used in this module. - required: true - type: str - file_to_upload: - description: - - Absolute path to the file that should be uploaded. - required: true - type: path - register_as: - description: - - Specifies Ansible fact name that is used to register received response from the FTD device. - type: str -''' - -EXAMPLES = """ -- name: Upload disk file - ftd_file_upload: - operation: 'postuploaddiskfile' - file_to_upload: /tmp/test1.txt -""" - -RETURN = """ -msg: - description: The error message describing why the module failed. - returned: error - type: str -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible_collections.community.general.plugins.module_utils.network.ftd.common import construct_ansible_facts, FtdServerError, HTTPMethod -from ansible_collections.community.general.plugins.module_utils.network.ftd.fdm_swagger_client import OperationField - - -def is_upload_operation(op_spec): - return op_spec[OperationField.METHOD] == HTTPMethod.POST or 'UploadStatus' in op_spec[OperationField.MODEL_NAME] - - -def main(): - fields = dict( - operation=dict(type='str', required=True), - file_to_upload=dict(type='path', required=True), - register_as=dict(type='str'), - ) - module = AnsibleModule(argument_spec=fields, - supports_check_mode=True) - params = module.params - connection = Connection(module._socket_path) - - op_spec = connection.get_operation_spec(params['operation']) - if op_spec is None: - module.fail_json(msg='Operation with specified name is not found: %s' % params['operation']) - if not is_upload_operation(op_spec): - module.fail_json( - msg='Invalid upload operation: %s. The operation must make POST request and return UploadStatus model.' % - params['operation']) - - try: - if module.check_mode: - module.exit_json() - resp = connection.upload_file(params['file_to_upload'], op_spec[OperationField.URL]) - module.exit_json(changed=True, response=resp, ansible_facts=construct_ansible_facts(resp, module.params)) - except FtdServerError as e: - module.fail_json(msg='Upload request for %s operation failed. Status code: %s. ' - 'Server response: %s' % (params['operation'], e.code, e.response)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ftd/ftd_install.py b/plugins/modules/network/ftd/ftd_install.py deleted file mode 100644 index 025bac5e3b..0000000000 --- a/plugins/modules/network/ftd/ftd_install.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2019 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: ftd_install -short_description: Installs FTD pkg image on the firewall -description: - - Provisioning module for FTD devices that installs ROMMON image (if needed) and - FTD pkg image on the firewall. - - Can be used with `httpapi` and `local` connection types. The `httpapi` is preferred, - the `local` connection should be used only when the device cannot be accessed via - REST API. -requirements: [ "python >= 3.5", "firepower-kickstart" ] -notes: - - Requires `firepower-kickstart` library that should be installed separately and requires Python >= 3.5. - - On localhost, Ansible can be still run with Python >= 2.7, but the interpreter for this particular module must be - Python >= 3.5. - - Python interpreter for the module can overwritten in `ansible_python_interpreter` variable. -author: "Cisco Systems, Inc. (@annikulin)" -options: - device_hostname: - description: - - Hostname of the device as appears in the prompt (e.g., 'firepower-5516'). - required: true - type: str - device_username: - description: - - Username to login on the device. - - Defaulted to 'admin' if not specified. - required: false - type: str - default: admin - device_password: - description: - - Password to login on the device. - required: true - type: str - device_sudo_password: - description: - - Root password for the device. If not specified, `device_password` is used. - required: false - type: str - device_new_password: - description: - - New device password to set after image installation. - - If not specified, current password from `device_password` property is reused. - - Not applicable for ASA5500-X series devices. - required: false - type: str - device_ip: - description: - - Device IP address of management interface. - - If not specified and connection is 'httpapi`, the module tries to fetch the existing value via REST API. - - For 'local' connection type, this parameter is mandatory. - required: false - type: str - device_gateway: - description: - - Device gateway of management interface. - - If not specified and connection is 'httpapi`, the module tries to fetch the existing value via REST API. - - For 'local' connection type, this parameter is mandatory. - required: false - type: str - device_netmask: - description: - - Device netmask of management interface. - - If not specified and connection is 'httpapi`, the module tries to fetch the existing value via REST API. - - For 'local' connection type, this parameter is mandatory. - required: false - type: str - device_model: - description: - - Platform model of the device (e.g., 'Cisco ASA5506-X Threat Defense'). - - If not specified and connection is 'httpapi`, the module tries to fetch the device model via REST API. - - For 'local' connection type, this parameter is mandatory. - required: false - type: str - choices: - - Cisco ASA5506-X Threat Defense - - Cisco ASA5508-X Threat Defense - - Cisco ASA5516-X Threat Defense - - Cisco Firepower 2110 Threat Defense - - Cisco Firepower 2120 Threat Defense - - Cisco Firepower 2130 Threat Defense - - Cisco Firepower 2140 Threat Defense - dns_server: - description: - - DNS IP address of management interface. - - If not specified and connection is 'httpapi`, the module tries to fetch the existing value via REST API. - - For 'local' connection type, this parameter is mandatory. - required: false - type: str - console_ip: - description: - - IP address of a terminal server. - - Used to set up an SSH connection with device's console port through the terminal server. - required: true - type: str - console_port: - description: - - Device's port on a terminal server. - required: true - type: str - console_username: - description: - - Username to login on a terminal server. - required: true - type: str - console_password: - description: - - Password to login on a terminal server. - required: true - type: str - rommon_file_location: - description: - - Path to the boot (ROMMON) image on TFTP server. - - Only TFTP is supported. - required: true - type: str - image_file_location: - description: - - Path to the FTD pkg image on the server to be downloaded. - - FTP, SCP, SFTP, TFTP, or HTTP protocols are usually supported, but may depend on the device model. - required: true - type: str - image_version: - description: - - Version of FTD image to be installed. - - Helps to compare target and current FTD versions to prevent unnecessary reinstalls. - required: true - type: str - force_install: - description: - - Forces the FTD image to be installed even when the same version is already installed on the firewall. - - By default, the module stops execution when the target version is installed in the device. - required: false - type: bool - default: false - search_domains: - description: - - Search domains delimited by comma. - - Defaulted to 'cisco.com' if not specified. - required: false - type: str - default: cisco.com -''' - -EXAMPLES = """ - - name: Install image v6.3.0 on FTD 5516 - ftd_install: - device_hostname: firepower - device_password: pass - device_ip: 192.168.0.1 - device_netmask: 255.255.255.0 - device_gateway: 192.168.0.254 - dns_server: 8.8.8.8 - - console_ip: 10.89.0.0 - console_port: 2004 - console_username: console_user - console_password: console_pass - - rommon_file_location: 'tftp://10.89.0.11/installers/ftd-boot-9.10.1.3.lfbff' - image_file_location: 'https://10.89.0.11/installers/ftd-6.3.0-83.pkg' - image_version: 6.3.0-83 -""" - -RETURN = """ -msg: - description: The message saying whether the image was installed or explaining why the installation failed. - returned: always - type: str -""" -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible.module_utils.six import iteritems - -from ansible_collections.community.general.plugins.module_utils.network.ftd.configuration import BaseConfigurationResource, ParamName -from ansible_collections.community.general.plugins.module_utils.network.ftd.device import assert_kick_is_installed, FtdPlatformFactory, FtdModel -from ansible_collections.community.general.plugins.module_utils.network.ftd.operation import FtdOperations, get_system_info - -REQUIRED_PARAMS_FOR_LOCAL_CONNECTION = ['device_ip', 'device_netmask', 'device_gateway', 'device_model', 'dns_server'] - - -def main(): - fields = dict( - device_hostname=dict(type='str', required=True), - device_username=dict(type='str', required=False, default='admin'), - device_password=dict(type='str', required=True, no_log=True), - device_sudo_password=dict(type='str', required=False, no_log=True), - device_new_password=dict(type='str', required=False, no_log=True), - device_ip=dict(type='str', required=False), - device_netmask=dict(type='str', required=False), - device_gateway=dict(type='str', required=False), - device_model=dict(type='str', required=False, choices=FtdModel.supported_models()), - dns_server=dict(type='str', required=False), - search_domains=dict(type='str', required=False, default='cisco.com'), - - console_ip=dict(type='str', required=True), - console_port=dict(type='str', required=True), - console_username=dict(type='str', required=True), - console_password=dict(type='str', required=True, no_log=True), - - rommon_file_location=dict(type='str', required=True), - image_file_location=dict(type='str', required=True), - image_version=dict(type='str', required=True), - force_install=dict(type='bool', required=False, default=False) - ) - module = AnsibleModule(argument_spec=fields) - assert_kick_is_installed(module) - - use_local_connection = module._socket_path is None - if use_local_connection: - check_required_params_for_local_connection(module, module.params) - platform_model = module.params['device_model'] - check_that_model_is_supported(module, platform_model) - else: - connection = Connection(module._socket_path) - resource = BaseConfigurationResource(connection, module.check_mode) - system_info = get_system_info(resource) - - platform_model = module.params['device_model'] or system_info['platformModel'] - check_that_model_is_supported(module, platform_model) - check_that_update_is_needed(module, system_info) - check_management_and_dns_params(resource, module.params) - - ftd_platform = FtdPlatformFactory.create(platform_model, module.params) - ftd_platform.install_ftd_image(module.params) - - module.exit_json(changed=True, - msg='Successfully installed FTD image %s on the firewall device.' % module.params["image_version"]) - - -def check_required_params_for_local_connection(module, params): - missing_params = [k for k, v in iteritems(params) if k in REQUIRED_PARAMS_FOR_LOCAL_CONNECTION and v is None] - if missing_params: - message = "The following parameters are mandatory when the module is used with 'local' connection: %s." % \ - ', '.join(sorted(missing_params)) - module.fail_json(msg=message) - - -def check_that_model_is_supported(module, platform_model): - if platform_model not in FtdModel.supported_models(): - module.fail_json(msg="Platform model '%s' is not supported by this module." % platform_model) - - -def check_that_update_is_needed(module, system_info): - target_ftd_version = module.params["image_version"] - if not module.params["force_install"] and target_ftd_version == system_info['softwareVersion']: - module.exit_json(changed=False, msg="FTD already has %s version of software installed." % target_ftd_version) - - -def check_management_and_dns_params(resource, params): - if not all([params['device_ip'], params['device_netmask'], params['device_gateway']]): - management_ip = resource.execute_operation(FtdOperations.GET_MANAGEMENT_IP_LIST, {})['items'][0] - params['device_ip'] = params['device_ip'] or management_ip['ipv4Address'] - params['device_netmask'] = params['device_netmask'] or management_ip['ipv4NetMask'] - params['device_gateway'] = params['device_gateway'] or management_ip['ipv4Gateway'] - if not params['dns_server']: - dns_setting = resource.execute_operation(FtdOperations.GET_DNS_SETTING_LIST, {})['items'][0] - dns_server_group_id = dns_setting['dnsServerGroup']['id'] - dns_server_group = resource.execute_operation(FtdOperations.GET_DNS_SERVER_GROUP, - {ParamName.PATH_PARAMS: {'objId': dns_server_group_id}}) - params['dns_server'] = dns_server_group['dnsServers'][0]['ipAddress'] - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_banner.py b/plugins/modules/network/icx/icx_banner.py deleted file mode 100644 index 7faadbe997..0000000000 --- a/plugins/modules/network/icx/icx_banner.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: icx_banner -author: "Ruckus Wireless (@Commscope)" -short_description: Manage multiline banners on Ruckus ICX 7000 series switches -description: - - This will configure both login and motd banners on remote - ruckus ICX 7000 series switches. It allows playbooks to add or remove - banner text from the active running configuration. -notes: - - Tested against ICX 10.1 -options: - banner: - description: - - Specifies which banner should be configured on the remote device. - type: str - required: true - choices: ['motd', 'exec', 'incoming'] - text: - description: - - The banner text that should be - present in the remote device running configuration. - This argument accepts a multiline string, with no empty lines. - type: str - state: - description: - - Specifies whether or not the configuration is - present in the current devices active running configuration. - type: str - default: present - choices: ['present', 'absent'] - enterkey: - description: - - Specifies whether or not the motd configuration should accept - the require-enter-key - type: bool - default: no - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, - by specifying it as module parameter. - type: bool - default: yes - -''' - -EXAMPLES = """ -- name: configure the motd banner - icx_banner: - banner: motd - text: | - this is my motd banner - that contains a multiline - string - state: present - -- name: remove the motd banner - icx_banner: - banner: motd - state: absent - -- name: configure require-enter-key for motd - icx_banner: - banner: motd - enterkey: True - -- name: remove require-enter-key for motd - icx_banner: - banner: motd - enterkey: False -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - banner motd - - this is my motd banner - - that contains a multiline - - string -""" - -import re -from ansible.module_utils._text import to_text -from ansible.module_utils.connection import exec_command -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import load_config, get_config -from ansible.module_utils.connection import Connection, ConnectionError - - -def map_obj_to_commands(updates, module): - commands = list() - state = module.params['state'] - want, have = updates - - if module.params['banner'] != 'motd' and module.params['enterkey']: - module.fail_json(msg=module.params['banner'] + " banner can have text only, got enterkey") - - if state == 'absent': - if 'text' in have.keys() and have['text']: - commands.append('no banner %s' % module.params['banner']) - if(module.params['enterkey'] is False): - commands.append('no banner %s require-enter-key' % module.params['banner']) - - elif state == 'present': - if module.params['text'] is None and module.params['enterkey'] is None: - module.fail_json(msg=module.params['banner'] + " one of the following is required: text, enterkey:only if motd") - - if module.params["banner"] == "motd" and want['enterkey'] != have['enterkey']: - if(module.params['enterkey']): - commands.append('banner %s require-enter-key' % module.params['banner']) - - if want['text'] and (want['text'] != have.get('text')): - module.params["enterkey"] = None - banner_cmd = 'banner %s' % module.params['banner'] - banner_cmd += ' $\n' - banner_cmd += module.params['text'].strip() - banner_cmd += '\n$' - commands.append(banner_cmd) - return commands - - -def map_config_to_obj(module): - compare = module.params.get('check_running_config') - obj = {'banner': module.params['banner'], 'state': 'absent', 'enterkey': False} - exec_command(module, 'skip') - output_text = '' - output_re = '' - out = get_config(module, flags=['| begin banner %s' - % module.params['banner']], compare=module.params['check_running_config']) - if out: - try: - output_re = re.search(r'banner %s( require-enter-key)' % module.params['banner'], out, re.S).group(0) - obj['enterkey'] = True - except BaseException: - pass - try: - output_text = re.search(r'banner %s (\$([^\$])+\$){1}' % module.params['banner'], out, re.S).group(1).strip('$\n') - except BaseException: - pass - - else: - output_text = None - if output_text: - obj['text'] = output_text - obj['state'] = 'present' - if module.params['check_running_config'] is False: - obj = {'banner': module.params['banner'], 'state': 'absent', 'enterkey': False, 'text': 'JUNK'} - return obj - - -def map_params_to_obj(module): - text = module.params['text'] - if text: - text = str(text).strip() - - return { - 'banner': module.params['banner'], - 'text': text, - 'state': module.params['state'], - 'enterkey': module.params['enterkey'] - } - - -def main(): - """entry point for module execution - """ - argument_spec = dict( - banner=dict(required=True, choices=['motd', 'exec', 'incoming']), - text=dict(), - enterkey=dict(type='bool'), - state=dict(default='present', choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - required_one_of = [['text', 'enterkey', 'state']] - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - supports_check_mode=True) - - warnings = list() - results = {'changed': False} - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - results['commands'] = commands - - if commands: - if not module.check_mode: - response = load_config(module, commands) - - results['changed'] = True - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_command.py b/plugins/modules/network/icx/icx_command.py deleted file mode 100644 index 871991743f..0000000000 --- a/plugins/modules/network/icx/icx_command.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = """ ---- -module: icx_command -author: "Ruckus Wireless (@Commscope)" -short_description: Run arbitrary commands on remote Ruckus ICX 7000 series switches -description: - - Sends arbitrary commands to an ICX node and returns the results - read from the device. This module includes an - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. -notes: - - Tested against ICX 10.1 -options: - commands: - description: - - List of commands to send to the remote ICX device over the - configured provider. The resulting output from the command - is returned. If the I(wait_for) argument is provided, the - module is not returned until the condition is satisfied or - the number of retries has expired. If a command sent to the - device requires answering a prompt, checkall and newline if - multiple prompts, it is possible to pass - a dict containing I(command), I(answer), I(prompt), I(check_all) - and I(newline).Common answers are 'y' or "\\r" (carriage return, - must be double quotes). See examples. - type: list - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - type: list - aliases: ['waitfor'] - match: - description: - - The I(match) argument is used in conjunction with the - I(wait_for) argument to specify the match policy. Valid - values are C(all) or C(any). If the value is set to C(all) - then all conditionals in the wait_for must be satisfied. If - the value is set to C(any) then only one of the values must be - satisfied. - type: str - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of times a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - type: int - default: 10 - interval: - description: - - Configures the interval in seconds to wait between retries - of the command. If the command does not pass the specified - conditions, the interval indicates how long to wait before - trying the command again. - type: int - default: 1 -""" - -EXAMPLES = """ -tasks: - - name: run show version on remote devices - icx_command: - commands: show version - - - name: run show version and check to see if output contains ICX - icx_command: - commands: show version - wait_for: result[0] contains ICX - - - name: run multiple commands on remote nodes - icx_command: - commands: - - show version - - show interfaces - - - name: run multiple commands and evaluate the output - icx_command: - commands: - - show version - - show interfaces - wait_for: - - result[0] contains ICX - - result[1] contains GigabitEthernet1/1/1 - - name: run commands that require answering a prompt - icx_command: - commands: - - command: 'service password-encryption sha1' - prompt: 'Warning: Moving to higher password-encryption type,.*' - answer: 'y' - - name: run commands that require answering multiple prompt - icx_command: - commands: - - command: 'username qqq password qqq' - prompt: - - 'User already exists. Do you want to modify:.*' - - 'To modify or remove user, enter current password:' - answer: - - 'y' - - 'qqq\\\r' - check_all: True - newline: False -""" - -RETURN = """ -stdout: - description: The set of responses from the commands - returned: always apart from low level errors - type: list - sample: ['...', '...'] -stdout_lines: - description: The value of stdout split into a list - returned: always apart from low level errors - type: list - sample: [['...', '...'], ['...'], ['...']] -failed_conditions: - description: The list of conditionals that have failed - returned: failed - type: list - sample: ['...', '...'] -""" - - -import re -import time -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList, to_lines -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional -from ansible.module_utils.six import string_types - - -def parse_commands(module, warnings): - command = ComplexList(dict( - command=dict(key=True), - prompt=dict(), - answer=dict(), - check_all=dict(type='bool', default='False'), - newline=dict(type='bool', default='True') - ), module) - commands = command(module.params['commands']) - for item in list(commands): - if module.check_mode: - if not item['command'].startswith('show'): - warnings.append( - 'Only show commands are supported when using check mode, not executing configure terminal') - commands.remove(item) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list', aliases=['waitfor']), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - run_commands(module, ['skip']) - commands = parse_commands(module, warnings) - result['warnings'] = warnings - - wait_for = module.params['wait_for'] or list() - conditionals = [Conditional(c) for c in wait_for] - retries = module.params['retries'] - interval = module.params['interval'] - match = module.params['match'] - - while retries > 0: - - responses = run_commands(module, commands) - - for item in list(conditionals): - - if item(responses): - if match == 'any': - conditionals = list() - break - conditionals.remove(item) - - if not conditionals: - break - - time.sleep(interval) - retries -= 1 - - if conditionals: - failed_conditions = [item.raw for item in conditionals] - msg = 'One or more conditional statements have not been satisfied' - module.fail_json(msg=msg, failed_conditions=failed_conditions) - - result.update({ - 'changed': False, - 'stdout': responses, - 'stdout_lines': list(to_lines(responses)) - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_config.py b/plugins/modules/network/icx/icx_config.py deleted file mode 100644 index 920a56d38a..0000000000 --- a/plugins/modules/network/icx/icx_config.py +++ /dev/null @@ -1,483 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: icx_config -author: "Ruckus Wireless (@Commscope)" -short_description: Manage configuration sections of Ruckus ICX 7000 series switches -description: - - Ruckus ICX configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with ICX configuration sections in - a deterministic way. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - lines: - description: - - The ordered set of commands that should be configured in the - section. The commands must be the exact same commands as found - in the device running-config. Be sure to note the configuration - command syntax as some commands are automatically modified by the - device config parser. - type: list - aliases: ['commands'] - parents: - description: - - The ordered set of parents that uniquely identify the section or hierarchy - the commands should be checked against. If the parents argument - is omitted, the commands are checked against the set of top - level or global commands. - type: list - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - type: str - before: - description: - - The ordered set of commands to push on to the command stack if - a change needs to be made. This allows the playbook designer - the opportunity to perform configuration commands prior to pushing - any changes without affecting how the set of commands are matched - against the system. - type: list - after: - description: - - The ordered set of commands to append to the end of the command - stack if a change needs to be made. Just like with I(before) this - allows the playbook designer to append a set of commands to be - executed after the command set. - type: list - match: - description: - - Instructs the module on the way to perform the matching of - the set of commands against the current device config. If - match is set to I(line), commands are matched line by line. If - match is set to I(strict), command lines are matched with respect - to position. If match is set to I(exact), command lines - must be an equal match. Finally, if match is set to I(none), the - module will not attempt to compare the source configuration with - the running configuration on the remote device. - type: str - choices: ['line', 'strict', 'exact', 'none'] - default: line - replace: - description: - - Instructs the module on the way to perform the configuration - on the device. If the replace argument is set to I(line) then - the modified lines are pushed to the device in configuration - mode. If the replace argument is set to I(block) then the entire - command block is pushed to the device in configuration mode if any - line is not correct. - type: str - default: line - choices: ['line', 'block'] - multiline_delimiter: - description: - - This argument is used when pushing a multiline configuration - element to the ICX device. It specifies the character to use - as the delimiting character. This only applies to the - configuration action. - type: str - default: "@" - backup: - description: - - This argument will cause the module to create a full backup of - the current C(running-config) from the remote device before any - changes are made. The backup file is written to the C(backup) - folder in the playbook root directory or role root directory, if - playbook is part of an ansible role. If the directory does not exist, - it is created. - type: bool - default: 'no' - defaults: - description: - - This argument specifies whether or not to collect all defaults - when getting the remote device running config. When enabled, - the module will get the current config by issuing the command - C(show running-config all). - type: bool - default: 'no' - running_config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(running_config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - type: str - aliases: ['config'] - save_when: - description: - - When changes are made to the device running-configuration, the - changes are not copied to non-volatile storage by default. Using - this argument will change that before. If the argument is set to - I(always), then the running-config will always be copied to the - start-up configuration and the I(modified) flag will always be set to - True. If the argument is set to I(modified), then the running-config - will only be copied to the start-up configuration if it has changed since - the last save to configuration. If the argument is set to - I(never), the running-config will never be copied to the - configuration. If the argument is set to I(changed), then the running-config - will only be copied to the configuration if the task has made a change. - type: str - default: never - choices: ['always', 'never', 'modified', 'changed'] - diff_against: - description: - - When using the C(ansible-playbook --diff) command line argument - the module can generate diffs against different sources. - - When this option is configure as I(startup), the module will return - the diff of the running-config against the configuration. - - When this option is configured as I(intended), the module will - return the diff of the running-config against the configuration - provided in the C(intended_config) argument. - - When this option is configured as I(running), the module will - return the before and after diff of the running-config with respect - to any changes made to the device configuration. - type: str - choices: ['running', 'startup', 'intended'] - diff_ignore_lines: - description: - - Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - type: list - intended_config: - description: - - The C(intended_config) provides the master configuration that - the node should conform to and is used to check the final - running-config against. This argument will not modify any settings - on the remote device and is strictly used to check the compliance - of the current device's configuration against. When specifying this - argument, the task should also modify the C(diff_against) value and - set it to I(intended). - type: str -''' - -EXAMPLES = """ -- name: configure top level configuration - icx_config: - lines: hostname {{ inventory_hostname }} - -- name: configure interface settings - icx_config: - lines: - - port-name test string - - ip address 172.31.1.1 255.255.255.0 - parents: interface ethernet 1/1/2 - -- name: configure ip helpers on multiple interfaces - icx_config: - lines: - - ip helper-address 172.26.1.10 - - ip helper-address 172.26.3.8 - parents: "{{ item }}" - with_items: - - interface ethernet 1/1/2 - - interface ethernet 1/1/3 - -- name: load new acl into device - icx_config: - lines: - - permit ip host 192.0.2.1 any log - - permit ip host 192.0.2.2 any log - - permit ip host 192.0.2.3 any log - - permit ip host 192.0.2.4 any log - parents: ip access-list extended test - before: no ip access-list extended test - match: exact - -- name: check the running-config against master config - icx_config: - diff_against: intended - intended_config: "{{ lookup('file', 'master.cfg') }}" - -- name: check the configuration against the running-config - icx_config: - diff_against: startup - diff_ignore_lines: - - ntp clock .* - -- name: for idempotency, use full-form commands - icx_config: - lines: - # - en - - enable - # parents: int eth1/0/11 - parents: interface ethernet 1/1/2 - -# Set boot image based on comparison to a group_var (version) and the version -# that is returned from the `icx_facts` module -- name: SETTING BOOT IMAGE - icx_config: - lines: - - no boot system - - boot system flash bootflash:{{new_image}} - host: "{{ inventory_hostname }}" - when: ansible_net_version != version - -- name: render template onto an ICX device - icx_config: - backup: yes - src: "{{ lookup('file', 'config.j2') }}" -""" - -RETURN = """ -updates: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1'] -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/icx_config.2016-07-16@22:28:34 -""" - -import json -from ansible.module_utils._text import to_text -from ansible.module_utils.connection import ConnectionError -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import run_commands, get_config -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import get_defaults_flag, get_connection -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import check_args as icx_check_args -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - - -def check_args(module, warnings): - icx_check_args(module, warnings) - if module.params['multiline_delimiter']: - if len(module.params['multiline_delimiter']) != 1: - module.fail_json(msg='multiline_delimiter value can only be a ' - 'single character') - - -def edit_config_or_macro(connection, commands): - if "macro name" in commands[0]: - connection.edit_macro(candidate=commands) - else: - if commands[0] != '': - connection.edit_config(candidate=commands) - - -def get_candidate_config(module): - candidate = '' - if module.params['src']: - candidate = module.params['src'] - - elif module.params['lines']: - candidate_obj = NetworkConfig(indent=1) - parents = module.params['parents'] or list() - candidate_obj.add(module.params['lines'], parents=parents) - candidate = dumps(candidate_obj, 'raw') - - return candidate - - -def get_running_config(module, current_config=None, flags=None): - running = module.params['running_config'] - if not running: - if not module.params['defaults'] and current_config: - running = current_config - else: - running = get_config(module, flags=flags) - - return running - - -def save_config(module, result): - result['changed'] = True - if not module.check_mode: - run_commands(module, 'write memory') - else: - module.warn('Skipping command `copy running-config start-up configuration` ' - 'due to check_mode. Configuration not copied to ' - 'non-volatile storage') - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - src=dict(), - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - multiline_delimiter=dict(default='@'), - - running_config=dict(aliases=['config']), - intended_config=dict(), - - defaults=dict(type='bool', default=False), - backup=dict(type='bool', default=False), - - save_when=dict(choices=['always', 'never', 'modified', 'changed'], default='never'), - - diff_against=dict(choices=['startup', 'intended', 'running']), - diff_ignore_lines=dict(type='list'), - - ) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('diff_against', 'intended', ['intended_config'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - check_args(module, warnings) - result['warnings'] = warnings - run_commands(module, 'skip') - diff_ignore_lines = module.params['diff_ignore_lines'] - config = None - contents = None - flags = None if module.params['defaults'] else [] - connection = get_connection(module) - - if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'): - contents = get_config(module, flags=flags) - config = NetworkConfig(indent=1, contents=contents) - if module.params['backup']: - result['__backup__'] = contents - - if any((module.params['lines'], module.params['src'])): - match = module.params['match'] - replace = module.params['replace'] - path = module.params['parents'] - - candidate = get_candidate_config(module) - running = get_running_config(module, contents, flags=flags) - try: - response = connection.get_diff(candidate=candidate, running=running, diff_match=match, diff_ignore_lines=diff_ignore_lines, path=path, - diff_replace=replace) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - config_diff = response['config_diff'] - banner_diff = response['banner_diff'] - - if config_diff or banner_diff: - commands = config_diff.split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - result['updates'] = commands - result['banners'] = banner_diff - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - if commands: - edit_config_or_macro(connection, commands) - if banner_diff: - connection.edit_banner(candidate=json.dumps(banner_diff), multiline_delimiter=module.params['multiline_delimiter']) - - result['changed'] = True - - running_config = module.params['running_config'] - startup_config = None - - if module.params['save_when'] == 'always': - save_config(module, result) - elif module.params['save_when'] == 'modified': - output = run_commands(module, ['show running-config', 'show configuration']) - - running_config = NetworkConfig(indent=1, contents=output[0], ignore_lines=diff_ignore_lines) - startup_config = NetworkConfig(indent=1, contents=output[1], ignore_lines=diff_ignore_lines) - - if running_config.sha1 != startup_config.sha1: - save_config(module, result) - elif module.params['save_when'] == 'changed' and result['changed']: - save_config(module, result) - - if module._diff: - if not running_config: - output = run_commands(module, 'show running-config') - contents = output[0] - else: - contents = running_config - - # recreate the object in order to process diff_ignore_lines - running_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if module.params['diff_against'] == 'running': - if module.check_mode: - module.warn("unable to perform diff against running-config due to check mode") - contents = None - else: - contents = config.config_text - - elif module.params['diff_against'] == 'startup': - if not startup_config: - output = run_commands(module, 'show configuration') - contents = output[0] - else: - contents = startup_config.config_text - - elif module.params['diff_against'] == 'intended': - contents = module.params['intended_config'] - - if contents is not None: - base_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if running_config.sha1 != base_config.sha1: - if module.params['diff_against'] == 'intended': - before = running_config - after = base_config - elif module.params['diff_against'] in ('startup', 'running'): - before = base_config - after = running_config - - result.update({ - 'changed': True, - 'diff': {'before': str(before), 'after': str(after)} - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_copy.py b/plugins/modules/network/icx/icx_copy.py deleted file mode 100644 index 2512870b04..0000000000 --- a/plugins/modules/network/icx/icx_copy.py +++ /dev/null @@ -1,372 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: icx_copy -author: "Ruckus Wireless (@Commscope)" -short_description: Transfer files from or to remote Ruckus ICX 7000 series switches -description: - - This module transfers files from or to remote devices running ICX. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - upload: - description: - - Name of the resource to be uploaded. Mutually exclusive with download. - type: str - choices: ['running-config', 'startup-config', 'flash_primary', 'flash_secondary'] - download: - description: - - Name of the resource to be downloaded. Mutually exclusive with upload. - type: str - choices: ['running-config', 'startup-config', 'flash_primary', 'flash_secondary', 'bootrom', 'fips-primary-sig', 'fips-secondary-sig', 'fips-bootrom-sig'] - protocol: - description: - - Data transfer protocol to be used - type: str - choices: ['scp', 'https'] - required: true - remote_server: - description: - - IP address of the remote server - type: str - required: true - remote_port: - description: - - The port number of the remote host. Default values will be selected based on protocol type. - Default scp:22, http:443 - type: str - remote_filename: - description: - - The name or path of the remote file/resource to be uploaded or downloaded. - type: str - required: true - remote_user: - description: - - remote username to be used for scp login. - type: str - remote_pass: - description: - - remote password to be used for scp login. - type: str - public_key: - description: - - public key type to be used to login to scp server - type: str - choices: ['rsa', 'dsa'] - -''' - -EXAMPLES = """ -- name: upload running-config to the remote scp server - icx_copy: - upload: running-config - protocol: scp - remote_server: 172.16.10.49 - remote_filename: running.conf - remote_user: user1 - remote_pass: pass123 - -- name: download running-config from the remote scp server - icx_copy: - download: running-config - protocol: scp - remote_server: 172.16.10.49 - remote_filename: running.conf - remote_user: user1 - remote_pass: pass123 - -- name: download running-config from the remote scp server using rsa public key - icx_copy: - download: running-config - protocol: scp - remote_server: 172.16.10.49 - remote_filename: running.conf - remote_user: user1 - remote_pass: pass123 - public_key: rsa - -- name: upload startup-config to the remote https server - icx_copy: - upload: startup-config - protocol: https - remote_server: 172.16.10.49 - remote_filename: config/running.conf - remote_user: user1 - remote_pass: pass123 - -- name: upload startup-config to the remote https server - icx_copy: - upload: startup-config - protocol: https - remote_server: 172.16.10.49 - remote_filename: config/running.conf - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the flash from remote scp ipv6 server - icx_copy: - download: startup-config - protocol: scp - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_filename: img.bin - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the secondary flash from remote scp ipv6 server - icx_copy: - Download: flash_secondary - protocol: scp - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_filename: img.bin - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the secondary flash from remote scp ipv6 server on port 5000 - icx_copy: - Download: flash_secondary - protocol: scp - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_port: 5000 - remote_filename: img.bin - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the primary flash from remote https ipv6 server - icx_copy: - Download: flash_primary - protocol: https - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_filename: images/img.bin - remote_user: user1 - remote_pass: pass123 - -- name: Download OS image into the primary flash from remote https ipv6 server on port 8080 - icx_copy: - Download: flash_primary - protocol: https - remote_server: ipv6 FE80:CD00:0000:0CDE:1257:0000:211E:729C - remote_port: 8080 - remote_filename: images/img.bin - remote_user: user1 - remote_pass: pass123 -""" - -RETURN = """ -changed: - description: true when downloaded any configuration or flash. false otherwise. - returned: always - type: bool -""" - - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import ConnectionError, exec_command -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import exec_scp, run_commands - - -def map_params_to_obj(module): - command = dict() - - if(module.params['protocol'] == 'scp'): - if(module.params['upload'] is not None): - module.params["upload"] = module.params["upload"].replace("flash_primary", "primary") - module.params["upload"] = module.params["upload"].replace("flash_secondary", "secondary") - if(module.params["upload"] == 'running-config' or module.params["upload"] == 'startup-config'): - command["command"] = "copy %s scp %s%s %s%s" % (module.params['upload'], - module.params["remote_server"], - " " + module.params["remote_port"] if module.params["remote_port"] else "", - module.params["remote_filename"], - "public-key " + module.params["public_key"] if module.params["public_key"] else "") - else: - command["command"] = "copy flash scp %s%s %s%s %s" % (module.params["remote_server"], - " " + module.params["remote_port"] if module.params["remote_port"] else "", - module.params["remote_filename"], - "public-key " + module.params["public_key"] if module.params["public_key"] else "", - module.params["upload"]) - command["scp_user"] = module.params["remote_user"] - command["scp_pass"] = module.params["remote_pass"] - if(module.params['download'] is not None): - module.params["download"] = module.params["download"].replace("flash_primary", "primary") - module.params["download"] = module.params["download"].replace("flash_secondary", "secondary") - if(module.params["download"] == 'running-config' or module.params["download"] == 'startup-config'): - command["command"] = "copy scp %s %s%s %s%s" % (module.params['download'], - module.params["remote_server"], - " " + module.params["remote_port"] if module.params["remote_port"] else "", - module.params["remote_filename"], - "public-key " + module.params["public_key"] if module.params["public_key"] else "") - else: - command["command"] = "copy scp flash %s%s %s%s %s" % (module.params["remote_server"], - " " + module.params["remote_port"] if module.params["remote_port"] else "", - module.params["remote_filename"], - "public-key " + module.params["public_key"] if module.params["public_key"] else "", - module.params["download"]) - command["scp_user"] = module.params["remote_user"] - command["scp_pass"] = module.params["remote_pass"] - if(module.params['protocol'] == 'https'): - if(module.params['upload'] is not None): - module.params["upload"] = module.params["upload"].replace("flash_primary", "primary") - module.params["upload"] = module.params["upload"].replace("flash_secondary", "secondary") - if(module.params["upload"] == 'running-config' or module.params["upload"] == 'startup-config'): - command["command"] = "copy %s https %s %s%s" % (module.params['upload'], - module.params["remote_server"], - module.params["remote_filename"], - " port " + module.params["remote_port"] if module.params["remote_port"] else "") - else: - command["command"] = "copy https flash %s %s %s%s" % (module.params["remote_server"], - module.params["remote_filename"], - module.params['upload'], - " port " + module.params["remote_port"] if module.params["remote_port"] else "") - if(module.params['download'] is not None): - module.params["download"] = module.params["download"].replace("flash_primary", "primary") - module.params["download"] = module.params["download"].replace("flash_secondary", "secondary") - if(module.params["download"] == 'running-config' or module.params["download"] == 'startup-config'): - command["command"] = "copy https %s %s %s%s" % (module.params['download'], - module.params["remote_server"], - module.params["remote_filename"], - " port " + module.params["remote_port"] if module.params["remote_port"] else "") - else: - command["command"] = "copy https flash %s %s %s%s" % (module.params["remote_server"], - module.params["remote_filename"], - module.params['download'], - " port " + module.params["remote_port"] if module.params["remote_port"] else "") - return command - - -def checkValidations(module): - validation = dict( - scp=dict( - upload=[ - 'running-config', - 'startup-config', - 'flash_primary', - 'flash_secondary'], - download=[ - 'running-config', - 'startup-config', - 'flash_primary', - 'flash_secondary', - 'bootrom', - 'fips-primary-sig', - 'fips-secondary-sig', - 'fips-bootrom-sig']), - https=dict( - upload=[ - 'running-config', - 'startup-config'], - download=[ - 'flash_primary', - 'flash_secondary', - 'startup-config'])) - protocol = module.params['protocol'] - upload = module.params['upload'] - download = module.params['download'] - - if(protocol == 'scp' and module.params['remote_user'] is None): - module.fail_json(msg="While using scp remote_user argument is required") - if(upload is None and download is None): - module.fail_json(msg="Upload or download params are required.") - if(upload is not None and download is not None): - module.fail_json(msg="Only upload or download can be used at a time.") - if(upload): - if(not (upload in validation.get(protocol).get("upload"))): - module.fail_json(msg="Specified resource '" + upload + "' can't be uploaded to '" + protocol + "'") - if(download): - if(not (download in validation.get(protocol).get("download"))): - module.fail_json(msg="Specified resource '" + download + "' can't be downloaded from '" + protocol + "'") - - -def main(): - """entry point for module execution - """ - argument_spec = dict( - upload=dict( - type='str', - required=False, - choices=[ - 'running-config', - 'flash_primary', - 'flash_secondary', - 'startup-config']), - download=dict( - type='str', - required=False, - choices=[ - 'running-config', - 'startup-config', - 'flash_primary', - 'flash_secondary', - 'bootrom', - 'fips-primary-sig', - 'fips-secondary-sig', - 'fips-bootrom-sig']), - protocol=dict( - type='str', - required=True, - choices=[ - 'https', - 'scp']), - remote_server=dict( - type='str', - required=True), - remote_port=dict( - type='str', - required=False), - remote_filename=dict( - type='str', - required=True), - remote_user=dict( - type='str', - required=False), - remote_pass=dict( - type='str', - required=False, - no_log=True), - public_key=dict( - type='str', - required=False, - choices=[ - 'rsa', - 'dsa'])) - mutually_exclusive = [['upload', 'download']] - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, mutually_exclusive=mutually_exclusive) - - checkValidations(module) - warnings = list() - result = {'changed': False, 'warnings': warnings} - exec_command(module, 'skip') - - response = '' - try: - command = map_params_to_obj(module) - result['commands'] = [command["command"]] - - if(module.params['protocol'] == 'scp'): - response = exec_scp(module, command) - else: - response = run_commands(module, command) - if('Response Code: 404' in response): - module.fail_json(msg=response) - else: - result['response'] = "in progress..." - if(module.params["download"] is not None): - result['changed'] = True - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_facts.py b/plugins/modules/network/icx/icx_facts.py deleted file mode 100644 index af45d0d14c..0000000000 --- a/plugins/modules/network/icx/icx_facts.py +++ /dev/null @@ -1,548 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: icx_facts -author: "Ruckus Wireless (@Commscope)" -short_description: Collect facts from remote Ruckus ICX 7000 series switches -description: - - Collects a base set of device facts from a remote device that - is running ICX. This module prepends all of the - base network fact keys with C(ansible_net_). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - type: list - default: '!config' -''' - -EXAMPLES = """ -# Collect all facts from the device -- icx_facts: - gather_subset: all - -# Collect only the config and default facts -- icx_facts: - gather_subset: - - config - -# Do not collect hardware facts -- icx_facts: - gather_subset: - - "!hardware" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str -ansible_net_image: - description: The image file the device is running - returned: always - type: str -ansible_net_stacked_models: - description: The model names of each device in the stack - returned: when multiple devices are configured in a stack - type: list -ansible_net_stacked_serialnums: - description: The serial numbers of each device in the stack - returned: when multiple devices are configured in a stack - type: list - -# hardware -ansible_net_filesystems: - description: All file system names available on the device - returned: when hardware is configured - type: list -ansible_net_filesystems_info: - description: A hash of all file systems containing info about each file system (e.g. free and total space) - returned: when hardware is configured - type: dict -ansible_net_memfree_mb: - description: The available free memory on the remote device in Mb - returned: when hardware is configured - type: int -ansible_net_memtotal_mb: - description: The total memory on the remote device in Mb - returned: when hardware is configured - type: int - -# config -ansible_net_config: - description: The current active config from the device - returned: when config is configured - type: str - -# interfaces -ansible_net_all_ipv4_addresses: - description: All IPv4 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_all_ipv6_addresses: - description: All IPv6 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -ansible_net_neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -""" - - -import re -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible.module_utils.six.moves import zip - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, commands=self.COMMANDS, check_rc=False) - - def run(self, cmd): - return run_commands(self.module, commands=cmd, check_rc=False) - - -class Default(FactsBase): - - COMMANDS = ['show version', 'show running-config | include hostname'] - - def populate(self): - super(Default, self).run(['skip']) - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - self.facts['model'] = self.parse_model(data) - self.facts['image'] = self.parse_image(data) - self.facts['hostname'] = self.parse_hostname(self.responses[1]) - self.parse_stacks(data) - - def parse_version(self, data): - match = re.search(r'SW: Version ([0-9]+.[0-9]+.[0-9a-zA-Z]+)', data) - if match: - return match.group(1) - - def parse_hostname(self, data): - match = re.search(r'^hostname (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'HW: (\S+ \S+)', data, re.M) - if match: - return match.group(1) - - def parse_image(self, data): - match = re.search(r'\([0-9]+ bytes\) from \S+ (\S+)', data) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'Serial #:(\S+)', data) - if match: - return match.group(1) - - def parse_stacks(self, data): - match = re.findall(r'UNIT [1-9]+: SL [1-9]+: (\S+)', data, re.M) - if match: - self.facts['stacked_models'] = match - - match = re.findall(r'^System [Ss]erial [Nn]umber\s+: (\S+)', data, re.M) - if match: - self.facts['stacked_serialnums'] = match - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show memory', - 'show flash' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - if data: - self.facts['filesystems'] = self.parse_filesystems(data) - self.facts['filesystems_info'] = self.parse_filesystems_info(self.responses[1]) - - if data: - if 'Invalid input detected' in data: - warnings.append('Unable to gather memory statistics') - else: - match = re.search(r'Dynamic memory: ([0-9]+) bytes total, ([0-9]+) bytes free, ([0-9]+%) used', data) - if match: - self.facts['memtotal_mb'] = int(match.group(1)) / 1024 - self.facts['memfree_mb'] = int(match.group(2)) / 1024 - - def parse_filesystems(self, data): - return "flash" - - def parse_filesystems_info(self, data): - facts = dict() - fs = '' - for line in data.split('\n'): - match = re.match(r'^(Stack unit \S+):', line) - if match: - fs = match.group(1) - facts[fs] = dict() - continue - match = re.match(r'\W+NAND Type: Micron NAND (\S+)', line) - if match: - facts[fs]['spacetotal'] = match.group(1) - match = re.match(r'\W+Code Flash Free Space = (\S+)', line) - if match: - facts[fs]['spacefree'] = int(int(match.group(1)) / 1024) - facts[fs]['spacefree'] = str(facts[fs]['spacefree']) + "Kb" - return {"flash": facts} - - -class Config(FactsBase): - - COMMANDS = ['skip', 'show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[1] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'skip', - 'show interfaces', - 'show running-config', - 'show lldp', - 'show media' - ] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - data = self.responses[1] - if data: - interfaces = self.parse_interfaces(data) - self.facts['interfaces'] = self.populate_interfaces(interfaces) - - data = self.responses[1] - if data: - data = self.parse_interfaces(data) - self.populate_ipv4_interfaces(data) - - data = self.responses[2] - if data: - self.populate_ipv6_interfaces(data) - - data = self.responses[3] - lldp_errs = ['Invalid input', 'LLDP is not enabled'] - - if data and not any(err in data for err in lldp_errs): - neighbors = self.run(['show lldp neighbors detail']) - if neighbors: - self.facts['neighbors'] = self.parse_neighbors(neighbors[0]) - - data = self.responses[4] - self.populate_mediatype(data) - - interfaceList = {} - for iface in self.facts['interfaces']: - if 'type' in self.facts['interfaces'][iface]: - newName = self.facts['interfaces'][iface]['type'] + iface - else: - newName = iface - interfaceList[newName] = self.facts['interfaces'][iface] - self.facts['interfaces'] = interfaceList - - def populate_mediatype(self, data): - lines = data.split("\n") - for line in lines: - match = re.match(r'Port (\S+):\W+Type\W+:\W+(.*)', line) - if match: - self.facts['interfaces'][match.group(1)]["mediatype"] = match.group(2) - - def populate_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - intf['description'] = self.parse_description(value) - intf['macaddress'] = self.parse_macaddress(value) - intf['mtu'] = self.parse_mtu(value) - intf['bandwidth'] = self.parse_bandwidth(value) - intf['duplex'] = self.parse_duplex(value) - intf['lineprotocol'] = self.parse_lineprotocol(value) - intf['operstatus'] = self.parse_operstatus(value) - intf['type'] = self.parse_type(value) - facts[key] = intf - return facts - - def populate_ipv4_interfaces(self, data): - for key, value in data.items(): - self.facts['interfaces'][key]['ipv4'] = dict() - primary_address = addresses = [] - primary_address = re.findall(r'Internet address is (\S+/\S+), .*$', value, re.M) - addresses = re.findall(r'Secondary address (.+)$', value, re.M) - if len(primary_address) == 0: - continue - addresses.append(primary_address[0]) - for address in addresses: - addr, subnet = address.split("/") - ipv4 = dict(address=addr.strip(), subnet=subnet.strip()) - self.add_ip_address(addr.strip(), 'ipv4') - self.facts['interfaces'][key]['ipv4'] = ipv4 - - def populate_ipv6_interfaces(self, data): - parts = data.split("\n") - for line in parts: - match = re.match(r'\W*interface \S+ (\S+)', line) - if match: - key = match.group(1) - try: - self.facts['interfaces'][key]['ipv6'] = list() - except KeyError: - self.facts['interfaces'][key] = dict() - self.facts['interfaces'][key]['ipv6'] = list() - self.facts['interfaces'][key]['ipv6'] = {} - continue - match = re.match(r'\W+ipv6 address (\S+)/(\S+)', line) - if match: - self.add_ip_address(match.group(1), "ipv6") - self.facts['interfaces'][key]['ipv6']["address"] = match.group(1) - self.facts['interfaces'][key]['ipv6']["subnet"] = match.group(2) - - def add_ip_address(self, address, family): - if family == 'ipv4': - self.facts['all_ipv4_addresses'].append(address) - else: - self.facts['all_ipv6_addresses'].append(address) - - def parse_neighbors(self, neighbors): - facts = dict() - for entry in neighbors.split('------------------------------------------------'): - if entry == '': - continue - intf = self.parse_lldp_intf(entry) - if intf not in facts: - facts[intf] = list() - fact = dict() - fact['host'] = self.parse_lldp_host(entry) - fact['port'] = self.parse_lldp_port(entry) - facts[intf].append(fact) - return facts - - def parse_interfaces(self, data): - parsed = dict() - key = '' - for line in data.split('\n'): - if len(line) == 0: - continue - elif line[0] == ' ': - parsed[key] += '\n%s' % line - else: - match = re.match(r'\S+Ethernet(\S+)', line) - if match: - key = match.group(1) - parsed[key] = line - return parsed - - def parse_description(self, data): - match = re.search(r'Port name is ([ \S]+)', data, re.M) - if match: - return match.group(1) - - def parse_macaddress(self, data): - match = re.search(r'Hardware is \S+, address is (\S+)', data) - if match: - return match.group(1) - - def parse_ipv4(self, data): - match = re.search(r'Internet address is (\S+)', data) - if match: - addr, masklen = match.group(1).split('/') - return dict(address=addr, masklen=int(masklen)) - - def parse_mtu(self, data): - match = re.search(r'MTU (\d+)', data) - if match: - return int(match.group(1)) - - def parse_bandwidth(self, data): - match = re.search(r'Configured speed (\S+), actual (\S+)', data) - if match: - return match.group(1) - - def parse_duplex(self, data): - match = re.search(r'configured duplex (\S+), actual (\S+)', data, re.M) - if match: - return match.group(2) - - def parse_mediatype(self, data): - match = re.search(r'media type is (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_type(self, data): - match = re.search(r'Hardware is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lineprotocol(self, data): - match = re.search(r'line protocol is (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_operstatus(self, data): - match = re.search(r'^(?:.+) is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lldp_intf(self, data): - match = re.search(r'^Local Intf: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_lldp_host(self, data): - match = re.search(r'System Name: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_lldp_port(self, data): - match = re.search(r'Port id: (.+)$', data, re.M) - if match: - return match.group(1) - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - -warnings = list() - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - gather_subset = module.params['gather_subset'] - - runable_subsets = set() - exclude_subsets = set() - - for subset in gather_subset: - if subset == 'all': - runable_subsets.update(VALID_SUBSETS) - continue - - if subset.startswith('!'): - subset = subset[1:] - if subset == 'all': - exclude_subsets.update(VALID_SUBSETS) - continue - exclude = True - else: - exclude = False - - if subset not in VALID_SUBSETS: - module.fail_json(msg='Bad subset') - - if exclude: - exclude_subsets.add(subset) - else: - runable_subsets.add(subset) - - if not runable_subsets: - runable_subsets.update(VALID_SUBSETS) - - runable_subsets.difference_update(exclude_subsets) - runable_subsets.add('default') - - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - instances.append(FACT_SUBSETS[key](module)) - - for inst in instances: - inst.populate() - facts.update(inst.facts) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_interface.py b/plugins/modules/network/icx/icx_interface.py deleted file mode 100644 index 1dbce9dde3..0000000000 --- a/plugins/modules/network/icx/icx_interface.py +++ /dev/null @@ -1,693 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: icx_interface -author: "Ruckus Wireless (@Commscope)" -short_description: Manage Interface on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of Interfaces - on ruckus icx devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - name: - description: - - Name of the Interface. - type: str - description: - description: - - Name of the description. - type: str - enabled: - description: - - Interface link status - default: yes - type: bool - speed: - description: - - Interface link speed/duplex - choices: ['10-full', '10-half', '100-full', '100-half', '1000-full', '1000-full-master', - '1000-full-slave', '10g-full', '10g-full-master', '10g-full-slave', '2500-full', '2500-full-master', - '2500-full-slave', '5g-full', '5g-full-master', '5g-full-slave', 'auto'] - type: str - stp: - description: - - enable/disable stp for the interface - type: bool - tx_rate: - description: - - Transmit rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - type: str - rx_rate: - description: - - Receiver rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - type: str - neighbors: - description: - - Check the operational state of given interface C(name) for CDP/LLDP neighbor. - - The following suboptions are available. - type: list - suboptions: - host: - description: - - "CDP/LLDP neighbor host for given interface C(name)." - type: str - port: - description: - - "CDP/LLDP neighbor port to which given interface C(name) is connected." - type: str - delay: - description: - - Time in seconds to wait before checking for the operational state on remote - device. This wait is applicable for operational state argument which are - I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate). - type: int - default: 10 - state: - description: - - State of the Interface configuration, C(up) means present and - operationally up and C(down) means present and operationally C(down) - default: present - type: str - choices: ['present', 'absent', 'up', 'down'] - power: - description: - - Inline power on Power over Ethernet (PoE) ports. - type: dict - suboptions: - by_class: - description: - - "The range is 0-4" - - "The power limit based on class value for given interface C(name)" - choices: ['0', '1', '2', '3', '4'] - type: str - limit: - description: - - "The range is 1000-15400|30000mW. For PoH ports the range is 1000-95000mW" - - "The power limit based on actual power value for given interface C(name)" - type: str - priority: - description: - - "The range is 1 (highest) to 3 (lowest)" - - "The priority for power management or given interface C(name)" - choices: ['1', '2', '3'] - type: str - enabled: - description: - - "enable/disable the poe of the given interface C(name)" - default: no - type: bool - aggregate: - description: - - List of Interfaces definitions. - type: list - suboptions: - name: - description: - - Name of the Interface. - type: str - description: - description: - - Name of the description. - type: str - enabled: - description: - - Interface link status - type: bool - speed: - description: - - Interface link speed/duplex - choices: ['10-full', '10-half', '100-full', '100-half', '1000-full', '1000-full-master', - '1000-full-slave', '10g-full', '10g-full-master', '10g-full-slave', '2500-full', '2500-full-master', - '2500-full-slave', '5g-full', '5g-full-master', '5g-full-slave', 'auto'] - type: str - stp: - description: - - enable/disable stp for the interface - type: bool - tx_rate: - description: - - Transmit rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - type: str - rx_rate: - description: - - Receiver rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - type: str - neighbors: - description: - - Check the operational state of given interface C(name) for CDP/LLDP neighbor. - - The following suboptions are available. - type: list - suboptions: - host: - description: - - "CDP/LLDP neighbor host for given interface C(name)." - type: str - port: - description: - - "CDP/LLDP neighbor port to which given interface C(name) is connected." - type: str - delay: - description: - - Time in seconds to wait before checking for the operational state on remote - device. This wait is applicable for operational state argument which are - I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate). - type: int - state: - description: - - State of the Interface configuration, C(up) means present and - operationally up and C(down) means present and operationally C(down) - type: str - choices: ['present', 'absent', 'up', 'down'] - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - power: - description: - - Inline power on Power over Ethernet (PoE) ports. - type: dict - suboptions: - by_class: - description: - - "The range is 0-4" - - "The power limit based on class value for given interface C(name)" - choices: ['0', '1', '2', '3', '4'] - type: str - limit: - description: - - "The range is 1000-15400|30000mW. For PoH ports the range is 1000-95000mW" - - "The power limit based on actual power value for given interface C(name)" - type: str - priority: - description: - - "The range is 1 (highest) to 3 (lowest)" - - "The priority for power management or given interface C(name)" - choices: ['1', '2', '3'] - type: str - enabled: - description: - - "enable/disable the poe of the given interface C(name)" - type: bool - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - - Module will use environment variable value(default:True), unless it is overridden, - by specifying it as module parameter. - default: yes - type: bool -''' - -EXAMPLES = """ -- name: enable ethernet port and set name - icx_interface: - name: ethernet 1/1/1 - description: interface-1 - stp: true - enabled: true - -- name: disable ethernet port 1/1/1 - icx_interface: - name: ethernet 1/1/1 - enabled: false - -- name: enable ethernet port range, set name and speed. - icx_interface: - name: ethernet 1/1/1 to 1/1/10 - description: interface-1 - speed: 100-full - enabled: true - -- name: enable poe. Set class. - icx_interface: - name: ethernet 1/1/1 - power: - by_class: 2 - -- name: configure poe limit of interface - icx_interface: - name: ethernet 1/1/1 - power: - limit: 10000 - -- name: disable poe of interface - icx_interface: - name: ethernet 1/1/1 - power: - enabled: false - -- name: set lag name for a range of lags - icx_interface: - name: lag 1 to 10 - description: test lags - -- name: Disable lag - icx_interface: - name: lag 1 - enabled: false - -- name: enable management interface - icx_interface: - name: management 1 - enabled: true - -- name: enable loopback interface - icx_interface: - name: loopback 10 - enabled: true - -- name: Add interface using aggregate - icx_interface: - aggregate: - - { name: ethernet 1/1/1, description: test-interface-1, power: { by_class: 2 } } - - { name: ethernet 1/1/3, description: test-interface-3} - speed: 10-full - enabled: true - -- name: Check tx_rate, rx_rate intent arguments - icx_interface: - name: ethernet 1/1/10 - state: up - tx_rate: ge(0) - rx_rate: le(0) - -- name: Check neighbors intent arguments - icx_interface: - name: ethernet 1/1/10 - neighbors: - - port: 1/1/5 - host: netdev -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface ethernet 1/1/1 - - port-name interface-1 - - state present - - speed-duplex 100-full - - inline power priority 1 -""" - -import re -from copy import deepcopy -from time import sleep -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import load_config, get_config -from ansible.module_utils.connection import Connection, ConnectionError, exec_command -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import conditional, remove_default_spec - - -def parse_enable(configobj, name): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'^disable', cfg, re.M) - if match: - return True - else: - return False - - -def parse_power_argument(configobj, name): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'(^inline power|^inline power(.*))+$', cfg, re.M) - if match: - return match.group(1) - - -def parse_config_argument(configobj, name, arg=None): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'%s (.+)$' % arg, cfg, re.M) - if match: - return match.group(1) - - -def parse_stp_arguments(module, item): - rc, out, err = exec_command(module, 'show interfaces ' + item) - match = re.search(r'STP configured to (\S+),', out, re.S) - if match: - return True if match.group(1) == "ON" else False - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - return None - - -def validate_power(module, power): - count = 0 - for item in power: - if power.get(item) is not None: - count += 1 - if count > 1: - module.fail_json(msg='power parameters are mutually exclusive: class,limit,priority,enabled') - - -def add_command_to_interface(interface, cmd, commands): - if interface not in commands: - commands.append(interface) - commands.append(cmd) - - -def map_config_to_obj(module): - compare = module.params['check_running_config'] - config = get_config(module, None, compare) - configobj = NetworkConfig(indent=1, contents=config) - match = re.findall(r'^interface (.+)$', config, re.M) - - if not match: - return list() - - instances = list() - - for item in set(match): - obj = { - 'name': item, - 'port-name': parse_config_argument(configobj, item, 'port-name'), - 'speed-duplex': parse_config_argument(configobj, item, 'speed-duplex'), - 'stp': parse_stp_arguments(module, item), - 'disable': True if parse_enable(configobj, item) else False, - 'power': parse_power_argument(configobj, item), - 'state': 'present' - } - instances.append(obj) - return instances - - -def parse_poe_config(poe, power): - if poe.get('by_class') is not None: - power += 'power-by-class %s' % poe.get('by_class') - elif poe.get('limit') is not None: - power += 'power-limit %s' % poe.get('limit') - elif poe.get('priority') is not None: - power += 'priority %s' % poe.get('priority') - elif poe.get('enabled'): - power = 'inline power' - elif poe.get('enabled') is False: - power = 'no inline power' - return power - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - item['port-name'] = item.pop('description') - item['speed-duplex'] = item.pop('speed') - poe = item.get('power') - if poe: - - validate_power(module, poe) - power = 'inline power' + ' ' - power_arg = parse_poe_config(poe, power) - item.update({'power': power_arg}) - - d = item.copy() - - if d['enabled']: - d['disable'] = False - else: - d['disable'] = True - - obj.append(d) - - else: - params = { - 'name': module.params['name'], - 'port-name': module.params['description'], - 'speed-duplex': module.params['speed'], - 'stp': module.params['stp'], - 'delay': module.params['delay'], - 'state': module.params['state'], - 'tx_rate': module.params['tx_rate'], - 'rx_rate': module.params['rx_rate'], - 'neighbors': module.params['neighbors'] - } - poe = module.params.get('power') - if poe: - validate_power(module, poe) - power = 'inline power' + ' ' - power_arg = parse_poe_config(poe, power) - params.update({'power': power_arg}) - - if module.params['enabled']: - params.update({'disable': False}) - else: - params.update({'disable': True}) - - obj.append(params) - return obj - - -def map_obj_to_commands(updates): - commands = list() - want, have = updates - - args = ('speed-duplex', 'port-name', 'power', 'stp') - for w in want: - name = w['name'] - disable = w['disable'] - state = w['state'] - - obj_in_have = search_obj_in_list(name, have) - interface = 'interface ' + name - - if state == 'absent' and have == []: - commands.append('no ' + interface) - - elif state == 'absent' and obj_in_have: - commands.append('no ' + interface) - - elif state in ('present', 'up', 'down'): - if obj_in_have: - for item in args: - candidate = w.get(item) - running = obj_in_have.get(item) - if candidate == 'no inline power' and running is None: - candidate = None - if candidate != running: - if candidate: - if item == 'power': - cmd = str(candidate) - elif item == 'stp': - cmd = 'spanning-tree' if candidate else 'no spanning-tree' - else: - cmd = item + ' ' + str(candidate) - add_command_to_interface(interface, cmd, commands) - - if disable and not obj_in_have.get('disable', False): - add_command_to_interface(interface, 'disable', commands) - elif not disable and obj_in_have.get('disable', False): - add_command_to_interface(interface, 'enable', commands) - else: - commands.append(interface) - for item in args: - value = w.get(item) - if value: - if item == 'power': - commands.append(str(value)) - elif item == 'stp': - cmd = 'spanning-tree' if item else 'no spanning-tree' - else: - commands.append(item + ' ' + str(value)) - - if disable: - commands.append('disable') - if disable is False: - commands.append('enable') - - return commands - - -def check_declarative_intent_params(module, want, result): - failed_conditions = [] - have_neighbors_lldp = None - have_neighbors_cdp = None - for w in want: - want_state = w.get('state') - want_tx_rate = w.get('tx_rate') - want_rx_rate = w.get('rx_rate') - want_neighbors = w.get('neighbors') - - if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate and not want_neighbors: - continue - - if result['changed']: - sleep(w['delay']) - - command = 'show interfaces %s' % w['name'] - rc, out, err = exec_command(module, command) - - if rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc) - - if want_state in ('up', 'down'): - match = re.search(r'%s (\w+)' % 'line protocol is', out, re.M) - have_state = None - if match: - have_state = match.group(1) - if have_state is None or not conditional(want_state, have_state.strip()): - failed_conditions.append('state ' + 'eq(%s)' % want_state) - - if want_tx_rate: - match = re.search(r'%s (\d+)' % 'output rate:', out, re.M) - have_tx_rate = None - if match: - have_tx_rate = match.group(1) - - if have_tx_rate is None or not conditional(want_tx_rate, have_tx_rate.strip(), cast=int): - failed_conditions.append('tx_rate ' + want_tx_rate) - - if want_rx_rate: - match = re.search(r'%s (\d+)' % 'input rate:', out, re.M) - have_rx_rate = None - if match: - have_rx_rate = match.group(1) - - if have_rx_rate is None or not conditional(want_rx_rate, have_rx_rate.strip(), cast=int): - failed_conditions.append('rx_rate ' + want_rx_rate) - - if want_neighbors: - have_host = [] - have_port = [] - - if have_neighbors_lldp is None: - rc, have_neighbors_lldp, err = exec_command(module, 'show lldp neighbors detail') - if rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc) - if have_neighbors_lldp: - lines = have_neighbors_lldp.strip().split('Local port: ') - - for line in lines: - field = line.split('\n') - if field[0].strip() == w['name'].split(' ')[1]: - for item in field: - match = re.search(r'\s*\+\s+System name\s+:\s+"(.*)"', item, re.M) - if match: - have_host.append(match.group(1)) - - match = re.search(r'\s*\+\s+Port description\s+:\s+"(.*)"', item, re.M) - if match: - have_port.append(match.group(1)) - - for item in want_neighbors: - host = item.get('host') - port = item.get('port') - if host and host not in have_host: - failed_conditions.append('host ' + host) - if port and port not in have_port: - failed_conditions.append('port ' + port) - return failed_conditions - - -def main(): - """ main entry point for module execution - """ - power_spec = dict( - by_class=dict(choices=['0', '1', '2', '3', '4']), - limit=dict(type='str'), - priority=dict(choices=['1', '2', '3']), - enabled=dict(type='bool') - ) - neighbors_spec = dict( - host=dict(), - port=dict() - ) - element_spec = dict( - name=dict(), - description=dict(), - enabled=dict(default=True, type='bool'), - speed=dict(type='str', choices=['10-full', '10-half', '100-full', '100-half', '1000-full', '1000-full-master', - '1000-full-slave', '10g-full', '10g-full-master', '10g-full-slave', '2500-full', '2500-full-master', - '2500-full-slave', '5g-full', '5g-full-master', '5g-full-slave', 'auto']), - stp=dict(type='bool'), - tx_rate=dict(), - rx_rate=dict(), - neighbors=dict(type='list', elements='dict', options=neighbors_spec), - delay=dict(default=10, type='int'), - state=dict(default='present', - choices=['present', 'absent', 'up', 'down']), - power=dict(type='dict', options=power_spec), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - argument_spec.update(element_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - warnings = list() - result = {} - result['changed'] = False - if warnings: - result['warnings'] = warnings - exec_command(module, 'skip') - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have)) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - failed_conditions = check_declarative_intent_params(module, want, result) - - if failed_conditions: - msg = 'One or more conditional statements have not been satisfied' - module.fail_json(msg=msg, failed_conditions=failed_conditions) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_l3_interface.py b/plugins/modules/network/icx/icx_l3_interface.py deleted file mode 100644 index cd382c3610..0000000000 --- a/plugins/modules/network/icx/icx_l3_interface.py +++ /dev/null @@ -1,438 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: icx_l3_interface -author: "Ruckus Wireless (@Commscope)" -short_description: Manage Layer-3 interfaces on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of Layer-3 interfaces - on ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - name: - description: - - Name of the Layer-3 interface to be configured eg. GigabitEthernet0/2, ve 10, ethernet 1/1/1 - type: str - ipv4: - description: - - IPv4 address to be set for the Layer-3 interface mentioned in I(name) option. - The address format is /, the mask is number - in range 0-32 eg. 192.168.0.1/24 - type: str - ipv6: - description: - - IPv6 address to be set for the Layer-3 interface mentioned in I(name) option. - The address format is /, the mask is number - in range 0-128 eg. fd5d:12c9:2201:1::1/64. - type: str - mode: - description: - - Specifies if ipv4 address should be dynamic/advertise to ospf/not advertise to ospf. - This should be specified only if ipv4 address is configured and if it is not secondary IP address. - choices: ['dynamic', 'ospf-ignore', 'ospf-passive'] - type: str - replace: - description: - - Replaces the configured primary IP address on the interface. - choices: ['yes', 'no'] - type: str - secondary: - description: - - Specifies that the configured address is a secondary IP address. - If this keyword is omitted, the configured address is the primary IP address. - choices: ['yes', 'no'] - type: str - aggregate: - description: - - List of Layer-3 interfaces definitions. Each of the entry in aggregate list should - define name of interface C(name) and a optional C(ipv4) or C(ipv6) address. - type: list - suboptions: - name: - description: - - Name of the Layer-3 interface to be configured eg. GigabitEthernet0/2, ve 10, ethernet 1/1/1 - type: str - ipv4: - description: - - IPv4 address to be set for the Layer-3 interface mentioned in I(name) option. - The address format is /, the mask is number - in range 0-32 eg. 192.168.0.1/24 - type: str - ipv6: - description: - - IPv6 address to be set for the Layer-3 interface mentioned in I(name) option. - The address format is /, the mask is number - in range 0-128 eg. fd5d:12c9:2201:1::1/64. - type: str - mode: - description: - - Specifies if ipv4 address should be dynamic/advertise to ospf/not advertise to ospf. - This should be specified only if ipv4 address is configured and if it is not secondary IP address. - choices: ['dynamic', 'ospf-ignore', 'ospf-passive'] - type: str - replace: - description: - - Replaces the configured primary IP address on the interface. - choices: ['yes', 'no'] - type: str - secondary: - description: - - Specifies that the configured address is a secondary IP address. - If this keyword is omitted, the configured address is the primary IP address. - choices: ['yes', 'no'] - type: str - state: - description: - - State of the Layer-3 interface configuration. It indicates if the configuration should - be present or absent on remote device. - choices: ['present', 'absent'] - type: str - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - state: - description: - - State of the Layer-3 interface configuration. It indicates if the configuration should - be present or absent on remote device. - default: present - choices: ['present', 'absent'] - type: str - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - default: yes -''' - -EXAMPLES = """ -- name: Remove ethernet 1/1/1 IPv4 and IPv6 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - ipv6: "fd5d:12c9:2201:1::1/64" - state: absent - -- name: Replace ethernet 1/1/1 primary IPv4 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - replace: yes - state: absent - -- name: Replace ethernet 1/1/1 dynamic IPv4 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - mode: dynamic - state: absent - -- name: Set ethernet 1/1/1 secondary IPv4 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - secondary: yes - state: absent - -- name: Set ethernet 1/1/1 IPv4 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv4: 192.168.0.1/24 - -- name: Set ethernet 1/1/1 IPv6 address - icx_l3_interface: - name: ethernet 1/1/1 - ipv6: "fd5d:12c9:2201:1::1/64" - -- name: Set IP addresses on aggregate - icx_l3_interface: - aggregate: - - { name: GigabitEthernet0/3, ipv4: 192.168.2.10/24 } - - { name: GigabitEthernet0/3, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" } - -- name: Remove IP addresses on aggregate - icx_l3_interface: - aggregate: - - { name: GigabitEthernet0/3, ipv4: 192.168.2.10/24 } - - { name: GigabitEthernet0/3, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" } - state: absent - - -- name: Set the ipv4 and ipv6 of a virtual ethernet(ve) - icx_l3_interface: - name: ve 100 - ipv4: 192.168.0.1 - ipv6: "2001:0db8:85a3:0000:0000:8a2e:0370:7334" -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - interface ethernet 1/1/1 - - ip address 192.168.0.1 255.255.255.0 - - ipv6 address fd5d:12c9:2201:1::1/64 -""" - - -import re -from copy import deepcopy -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import exec_command -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import get_config, load_config -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import is_netmask, is_masklen, to_netmask, to_masklen - - -def validate_ipv4(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json(msg='address format is /, got invalid format %s' % value) - else: - if not is_masklen(address[1]): - module.fail_json(msg='invalid value for mask: %s, mask should be in range 0-32' % address[1]) - - -def validate_ipv6(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json(msg='address format is /, got invalid format %s' % value) - else: - if not 0 <= int(address[1]) <= 128: - module.fail_json(msg='invalid value for mask: %s, mask should be in range 0-128' % address[1]) - - -def validate_param_values(module, obj, param=None): - if param is None: - param = module.params - for key in obj: - validator = globals().get('validate_%s' % key) - if callable(validator): - validator(param.get(key), module) - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - validate_param_values(module, item, item) - obj.append(item.copy()) - else: - obj.append({ - 'name': module.params['name'], - 'ipv4': module.params['ipv4'], - 'ipv6': module.params['ipv6'], - 'state': module.params['state'], - 'replace': module.params['replace'], - 'mode': module.params['mode'], - 'secondary': module.params['secondary'], - }) - - validate_param_values(module, obj) - - return obj - - -def parse_config_argument(configobj, name, arg=None): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - - values = [] - matches = re.finditer(r'%s (.+)$' % arg, cfg, re.M) - for match in matches: - match_str = match.group(1).strip() - if arg == 'ipv6 address': - values.append(match_str) - else: - values = match_str - break - - return values or None - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - return None - - -def map_config_to_obj(module): - compare = module.params['check_running_config'] - config = get_config(module, flags=['| begin interface'], compare=compare) - configobj = NetworkConfig(indent=1, contents=config) - - match = re.findall(r'^interface (\S+ \S+)', config, re.M) - if not match: - return list() - - instances = list() - - for item in set(match): - ipv4 = parse_config_argument(configobj, item, 'ip address') - if ipv4: - address = ipv4.strip().split(' ') - if len(address) == 2 and is_netmask(address[1]): - ipv4 = '{0}/{1}'.format(address[0], to_text(to_masklen(address[1]))) - obj = { - 'name': item, - 'ipv4': ipv4, - 'ipv6': parse_config_argument(configobj, item, 'ipv6 address'), - 'state': 'present' - } - instances.append(obj) - - return instances - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - for w in want: - name = w['name'] - ipv4 = w['ipv4'] - ipv6 = w['ipv6'] - state = w['state'] - if 'replace' in w: - replace = w['replace'] == 'yes' - else: - replace = False - if w['mode'] is not None: - mode = ' ' + w['mode'] - else: - mode = '' - if w['secondary'] is not None: - secondary = w['secondary'] == 'yes' - else: - secondary = False - - interface = 'interface ' + name - commands.append(interface) - - obj_in_have = search_obj_in_list(name, have) - if state == 'absent' and have == []: - if ipv4: - address = ipv4.split('/') - if len(address) == 2: - ipv4 = '{addr} {mask}'.format(addr=address[0], mask=to_netmask(address[1])) - commands.append('no ip address {ip}'.format(ip=ipv4)) - if ipv6: - commands.append('no ipv6 address {ip}'.format(ip=ipv6)) - - elif state == 'absent' and obj_in_have: - if obj_in_have['ipv4']: - if ipv4: - address = ipv4.split('/') - if len(address) == 2: - ipv4 = '{addr} {mask}'.format(addr=address[0], mask=to_netmask(address[1])) - commands.append('no ip address {ip}'.format(ip=ipv4)) - if obj_in_have['ipv6']: - if ipv6: - commands.append('no ipv6 address {ip}'.format(ip=ipv6)) - - elif state == 'present': - if ipv4: - if obj_in_have is None or obj_in_have.get('ipv4') is None or ipv4 != obj_in_have['ipv4']: - address = ipv4.split('/') - if len(address) == 2: - ipv4 = '{0} {1}'.format(address[0], to_netmask(address[1])) - commands.append('ip address %s%s%s%s' % (format(ipv4), mode, ' replace' if (replace) else '', ' secondary' if (secondary) else '')) - - if ipv6: - if obj_in_have is None or obj_in_have.get('ipv6') is None or ipv6.lower() not in [addr.lower() for addr in obj_in_have['ipv6']]: - commands.append('ipv6 address {ip}'.format(ip=ipv6)) - - if commands[-1] == interface: - commands.pop(-1) - else: - commands.append("exit") - - return commands - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - name=dict(), - ipv4=dict(), - ipv6=dict(), - replace=dict(choices=['yes', 'no']), - mode=dict(choices=['dynamic', 'ospf-ignore', 'ospf-passive']), - secondary=dict(choices=['yes', 'no']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])), - state=dict(default='present', - choices=['present', 'absent']), - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec) - ) - - argument_spec.update(element_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate'], ['secondary', 'replace'], ['secondary', 'mode']] - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - - result = {'changed': False} - exec_command(module, 'skip') - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - - if commands: - if not module.check_mode: - resp = load_config(module, commands) - warnings.extend((out for out in resp if out)) - - result['changed'] = True - - if warnings: - result['warnings'] = warnings - - result['commands'] = commands - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_linkagg.py b/plugins/modules/network/icx/icx_linkagg.py deleted file mode 100644 index 82d2b952be..0000000000 --- a/plugins/modules/network/icx/icx_linkagg.py +++ /dev/null @@ -1,327 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: icx_linkagg -author: "Ruckus Wireless (@Commscope)" -short_description: Manage link aggregation groups on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of link aggregation groups - on Ruckus ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - group: - description: - - Channel-group number for the port-channel - Link aggregation group. Range 1-255 or set to 'auto' to auto-generates a LAG ID - type: int - name: - description: - - Name of the LAG - type: str - mode: - description: - - Mode of the link aggregation group. - type: str - choices: ['dynamic', 'static'] - members: - description: - - List of port members or ranges of the link aggregation group. - type: list - state: - description: - - State of the link aggregation group. - type: str - default: present - choices: ['present', 'absent'] - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - default: yes - aggregate: - description: - - List of link aggregation definitions. - type: list - suboptions: - group: - description: - - Channel-group number for the port-channel - Link aggregation group. Range 1-255 or set to 'auto' to auto-generates a LAG ID - type: int - name: - description: - - Name of the LAG - type: str - mode: - description: - - Mode of the link aggregation group. - type: str - choices: ['dynamic', 'static'] - members: - description: - - List of port members or ranges of the link aggregation group. - type: list - state: - description: - - State of the link aggregation group. - type: str - choices: ['present', 'absent'] - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - purge: - description: - - Purge links not defined in the I(aggregate) parameter. - type: bool - default: no - -''' - -EXAMPLES = """ -- name: create static link aggregation group - icx_linkagg: - group: 10 - mode: static - name: LAG1 - -- name: create link aggregation group with auto id - icx_linkagg: - group: auto - mode: dynamic - name: LAG2 - -- name: delete link aggregation group - icx_linkagg: - group: 10 - state: absent - -- name: Set members to LAG - icx_linkagg: - group: 200 - mode: static - members: - - ethernet 1/1/1 to 1/1/6 - - ethernet 1/1/10 - -- name: Remove links other then LAG id 100 and 3 using purge - icx_linkagg: - aggregate: - - { group: 3} - - { group: 100} - purge: true -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - lag LAG1 dynamic id 11 - - ports ethernet 1/1/1 to 1/1/6 - - no ports ethernet 1/1/10 - - no lag LAG1 dynamic id 12 -""" - - -import re -from copy import deepcopy - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import ConnectionError, exec_command -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import run_commands, get_config, load_config -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import CustomNetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - - -def range_to_members(ranges, prefix=""): - match = re.findall(r'(ethe[a-z]* [0-9]/[0-9]/[0-9]+)( to [0-9]/[0-9]/[0-9]+)?', ranges) - members = list() - for m in match: - start, end = m - if(end == ''): - start = start.replace("ethe ", "ethernet ") - members.append("%s%s" % (prefix, start)) - else: - start_tmp = re.search(r'[0-9]/[0-9]/([0-9]+)', start) - end_tmp = re.search(r'[0-9]/[0-9]/([0-9]+)', end) - start = int(start_tmp.group(1)) - end = int(end_tmp.group(1)) + 1 - for num in range(start, end): - members.append("%sethernet 1/1/%s" % (prefix, num)) - return members - - -def map_config_to_obj(module): - objs = dict() - compare = module.params['check_running_config'] - config = get_config(module, None, compare=compare) - obj = None - for line in config.split('\n'): - l = line.strip() - match1 = re.search(r'lag (\S+) (\S+) id (\S+)', l, re.M) - if match1: - obj = dict() - obj['name'] = match1.group(1) - obj['mode'] = match1.group(2) - obj['group'] = match1.group(3) - obj['state'] = 'present' - obj['members'] = list() - else: - match2 = re.search(r'ports .*', l, re.M) - if match2 and obj is not None: - obj['members'].extend(range_to_members(match2.group(0))) - elif obj is not None: - objs[obj['group']] = obj - obj = None - return objs - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - d = item.copy() - d['group'] = str(d['group']) - obj.append(d) - else: - obj.append({ - 'group': str(module.params['group']), - 'mode': module.params['mode'], - 'members': module.params['members'], - 'state': module.params['state'], - 'name': module.params['name'] - }) - - return obj - - -def search_obj_in_list(group, lst): - for o in lst: - if o['group'] == group: - return o - return None - - -def is_member(member, lst): - for li in lst: - ml = range_to_members(li) - if member in ml: - return True - return False - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params['purge'] - - for w in want: - if have == {} and w['state'] == 'absent': - commands.append("%slag %s %s id %s" % ('no ' if w['state'] == 'absent' else '', w['name'], w['mode'], w['group'])) - elif have.get(w['group']) is None: - commands.append("%slag %s %s id %s" % ('no ' if w['state'] == 'absent' else '', w['name'], w['mode'], w['group'])) - if(w.get('members') is not None and w['state'] == 'present'): - for m in w['members']: - commands.append("ports %s" % (m)) - if w['state'] == 'present': - commands.append("exit") - else: - commands.append("%slag %s %s id %s" % ('no ' if w['state'] == 'absent' else '', w['name'], w['mode'], w['group'])) - if(w.get('members') is not None and w['state'] == 'present'): - for m in have[w['group']]['members']: - if not is_member(m, w['members']): - commands.append("no ports %s" % (m)) - for m in w['members']: - sm = range_to_members(ranges=m) - for smm in sm: - if smm not in have[w['group']]['members']: - commands.append("ports %s" % (smm)) - - if w['state'] == 'present': - commands.append("exit") - if purge: - for h in have: - if search_obj_in_list(have[h]['group'], want) is None: - commands.append("no lag %s %s id %s" % (have[h]['name'], have[h]['mode'], have[h]['group'])) - return commands - - -def main(): - element_spec = dict( - group=dict(type='int'), - name=dict(type='str'), - mode=dict(choices=['dynamic', 'static']), - members=dict(type='list'), - state=dict(default='present', - choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['group'] = dict(required=True, type='int') - - required_one_of = [['group', 'aggregate']] - required_together = [['name', 'group']] - mutually_exclusive = [['group', 'aggregate']] - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec, required_together=required_together), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - required_together=required_together, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - result = {'changed': False} - exec_command(module, 'skip') - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - - result["commands"] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_lldp.py b/plugins/modules/network/icx/icx_lldp.py deleted file mode 100644 index 955ebc4553..0000000000 --- a/plugins/modules/network/icx/icx_lldp.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: icx_lldp -author: "Ruckus Wireless (@Commscope)" -short_description: Manage LLDP configuration on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of LLDP service on ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - interfaces: - description: - - specify interfaces - suboptions: - name: - description: - - List of ethernet ports to enable lldp. To add a range of ports use 'to' keyword. See the example. - type: list - state: - description: - - State of lldp configuration for interfaces - type: str - choices: ['present', 'absent', 'enabled', 'disabled'] - type: list - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - default: yes - state: - description: - - Enables the receipt and transmission of Link Layer Discovery Protocol (LLDP) globally. - type: str - choices: ['present', 'absent', 'enabled', 'disabled'] -''' - -EXAMPLES = """ -- name: Disable LLDP - icx_lldp: - state: absent - -- name: Enable LLDP - icx_lldp: - state: present - -- name: Disable LLDP on ports 1/1/1 - 1/1/10, 1/1/20 - icx_lldp: - interfaces: - - name: - - ethernet 1/1/1 to 1/1/10 - - ethernet 1/1/20 - state: absent - state: present - -- name: Enable LLDP on ports 1/1/5 - 1/1/10 - icx_lldp: - interfaces: - - name: - - ethernet 1/1/1 to 1/1/10 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - lldp run - - no lldp run -""" - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import load_config, run_commands - - -def has_lldp(module): - run_commands(module, ['skip']) - output = run_commands(module, ['show lldp']) - is_lldp_enable = False - if len(output) > 0 and "LLDP is not running" not in output[0]: - is_lldp_enable = True - - return is_lldp_enable - - -def map_obj_to_commands(module, commands): - interfaces = module.params.get('interfaces') - for item in interfaces: - state = item.get('state') - if state == 'present': - for port in item.get('name'): - if 'all' in port: - module.fail_json(msg='cannot enable on all the ports') - else: - commands.append('lldp enable ports {0}'.format(str(port))) - elif state == 'absent': - for port in item.get('name'): - if 'all' in port: - module.fail_json(msg='cannot enable on all the ports') - else: - commands.append('no lldp enable ports {0}'.format(str(port))) - - -def main(): - """ main entry point for module execution - """ - interfaces_spec = dict( - name=dict(type='list'), - state=dict(choices=['present', 'absent', - 'enabled', 'disabled']) - ) - - argument_spec = dict( - interfaces=dict(type='list', elements='dict', options=interfaces_spec), - state=dict(choices=['present', 'absent', - 'enabled', 'disabled']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - warnings = list() - - result = {'changed': False} - - if warnings: - result['warnings'] = warnings - - if module.params['check_running_config'] is False: - HAS_LLDP = None - else: - HAS_LLDP = has_lldp(module) - - commands = [] - state = module.params['state'] - - if state is None: - if HAS_LLDP: - map_obj_to_commands(module, commands) - else: - module.fail_json(msg='LLDP is not running') - else: - if state == 'absent' and HAS_LLDP is None: - commands.append('no lldp run') - - if state == 'absent' and HAS_LLDP: - commands.append('no lldp run') - - elif state == 'present': - if not HAS_LLDP: - commands.append('lldp run') - if module.params.get('interfaces'): - map_obj_to_commands(module, commands) - - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_logging.py b/plugins/modules/network/icx/icx_logging.py deleted file mode 100644 index 847aef6711..0000000000 --- a/plugins/modules/network/icx/icx_logging.py +++ /dev/null @@ -1,581 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: icx_logging -author: "Ruckus Wireless (@Commscope)" -short_description: Manage logging on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of logging - on Ruckus ICX 7000 series switches. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - dest: - description: - - Destination of the logs. - choices: ['on', 'host', 'console', 'buffered', 'persistence', 'rfc5424'] - type: str - name: - description: - - ipv4 address/ipv6 address/name of syslog server. - type: str - udp_port: - description: - - UDP port of destination host(syslog server). - type: str - facility: - description: - - Specifies log facility to log messages from the device. - choices: ['auth','cron','daemon','kern','local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7', 'user', - 'lpr','mail','news','syslog','sys9','sys10','sys11','sys12','sys13','sys14','user','uucp'] - type: str - level: - description: - - Specifies the message level. - type: list - choices: ['alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational', - 'notifications', 'warnings'] - aggregate: - description: - - List of logging definitions. - type: list - suboptions: - dest: - description: - - Destination of the logs. - choices: ['on', 'host', 'console', 'buffered', 'persistence', 'rfc5424'] - type: str - name: - description: - - ipv4 address/ipv6 address/name of syslog server. - type: str - udp_port: - description: - - UDP port of destination host(syslog server). - type: str - facility: - description: - - Specifies log facility to log messages from the device. - choices: ['auth','cron','daemon','kern','local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7', 'user', - 'lpr','mail','news','syslog','sys9','sys10','sys11','sys12','sys13','sys14','user','uucp'] - type: str - level: - description: - - Specifies the message level. - type: list - choices: ['alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational', - 'notifications', 'warnings'] - state: - description: - - State of the logging configuration. - choices: ['present', 'absent'] - type: str - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - state: - description: - - State of the logging configuration. - default: present - choices: ['present', 'absent'] - type: str - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - default: yes -''' - -EXAMPLES = """ -- name: Configure host logging. - icx_logging: - dest: host - name: 172.16.0.1 - udp_port: 5555 -- name: Remove host logging configuration. - icx_logging: - dest: host - name: 172.16.0.1 - udp_port: 5555 - state: absent -- name: Disables the real-time display of syslog messages. - icx_logging: - dest: console - state: absent -- name: Enables local syslog logging. - icx_logging: - dest : on - state: present -- name: configure buffer level. - icx_logging: - dest: buffered - level: critical -- name: Configure logging using aggregate - icx_logging: - aggregate: - - { dest: buffered, level: ['notifications','errors'] } -- name: remove logging using aggregate - icx_logging: - aggregate: - - { dest: console } - - { dest: host, name: 172.16.0.1, udp_port: 5555 } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - logging host 172.16.0.1 - - logging console -""" - -import re -from copy import deepcopy -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import Connection, ConnectionError, exec_command -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec, validate_ip_v6_address -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import get_config, load_config - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - -def diff_in_list(want, have): - adds = set() - removes = set() - for w in want: - if w['dest'] == 'buffered': - for h in have: - if h['dest'] == 'buffered': - adds = w['level'] - h['level'] - removes = h['level'] - w['level'] - return adds, removes - return adds, removes - - -def map_obj_to_commands(updates): - dest_group = ('host', 'console', 'persistence', 'enable') - commands = list() - want, have = updates - - for w in want: - dest = w['dest'] - name = w['name'] - level = w['level'] - state = w['state'] - udp_port = w['udp_port'] - facility = w['facility'] - del w['state'] - del w['facility'] - - facility_name = '' - facility_level = '' - if name is not None and validate_ip_v6_address(name): - name = 'ipv6 ' + name - - if facility: - for item in have: - if item['dest'] == 'facility': - facility_name = item['dest'] - facility_level = item['facility'] - - if state == 'absent': - if have == []: - if facility: - commands.append('no logging facility') - - if dest == 'buffered': - for item in have: - if item['dest'] == 'buffered': - want_level = level - have_level = item['level'] - for item in want_level: - commands.append('no logging buffered {0}'.format(item)) - - if dest == 'host': - if name and udp_port: - commands.append('no logging host {0} udp-port {1}'.format(name, udp_port)) - elif name: - commands.append('no logging host {0}'.format(name)) - else: - if dest == 'rfc5424': - commands.append('no logging enable {0}'.format(dest)) - else: - if dest != 'buffered': - commands.append('no logging {0}'.format(dest)) - - if facility: - if facility_name == 'facility' and facility_level != 'user': - commands.append('no logging facility') - - if dest == 'buffered': - for item in have: - if item['dest'] == 'buffered': - want_level = level - have_level = item['level'] - for item in want_level: - if item in have_level: - commands.append('no logging buffered {0}'.format(item)) - - if w in have: - if dest == 'host': - if name and udp_port: - commands.append('no logging host {0} udp-port {1}'.format(name, udp_port)) - elif name: - commands.append('no logging host {0}'.format(name)) - else: - if dest == 'rfc5424': - commands.append('no logging enable {0}'.format(dest)) - else: - if dest != 'buffered': - commands.append('no logging {0}'.format(dest)) - - if state == 'present': - if facility: - if facility != facility_level: - commands.append('logging facility {0}'.format(facility)) - if w not in have: - if dest == 'host': - if name and udp_port: - commands.append('logging host {0} udp-port {1}'.format(name, udp_port)) - elif name: - commands.append('logging host {0}'.format(name)) - elif dest == 'buffered': - adds, removes = diff_in_list(want, have) - for item in adds: - commands.append('logging buffered {0}'.format(item)) - for item in removes: - commands.append('no logging buffered {0}'.format(item)) - elif dest == 'rfc5424': - commands.append('logging enable {0}'.format(dest)) - else: - commands.append('logging {0}'.format(dest)) - - return commands - - -def parse_port(line, dest): - port = None - if dest == 'host': - match = re.search(r'logging host \S+\s+udp-port\s+(\d+)', line, re.M) - if match: - port = match.group(1) - else: - match_port = re.search(r'logging host ipv6 \S+\s+udp-port\s+(\d+)', line, re.M) - if match_port: - port = match_port.group(1) - return port - - -def parse_name(line, dest): - name = None - if dest == 'host': - match = re.search(r'logging host (\S+)', line, re.M) - if match: - if match.group(1) == 'ipv6': - ipv6_add = re.search(r'logging host ipv6 (\S+)', line, re.M) - name = ipv6_add.group(1) - else: - name = match.group(1) - - return name - - -def parse_address(line, dest): - if dest == 'host': - match = re.search(r'^logging host ipv6 (\S+)', line.strip(), re.M) - if match: - return True - return False - - -def map_config_to_obj(module): - obj = [] - facility = '' - addr6 = False - dest_group = ('host', 'console', 'buffered', 'persistence', 'enable') - dest_level = ('alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational', 'notifications', 'warnings') - buff_level = list() - if module.params['check_running_config'] is False: - return [] - data = get_config(module, flags=['| include logging']) - facility_match = re.search(r'^logging facility (\S+)', data, re.M) - if facility_match: - facility = facility_match.group(1) - obj.append({ - 'dest': 'facility', - 'facility': facility - }) - else: - obj.append({ - 'dest': 'facility', - 'facility': 'user' - }) - for line in data.split('\n'): - match = re.search(r'^logging (\S+)', line.strip(), re.M) - if match: - - if match.group(1) in dest_group: - dest = match.group(1) - if dest == 'host': - obj.append({ - 'dest': dest, - 'name': parse_name(line.strip(), dest), - 'udp_port': parse_port(line, dest), - 'level': None, - 'addr6': parse_address(line, dest) - - }) - elif dest == 'buffered': - obj.append({ - 'dest': dest, - 'level': None, - 'name': None, - 'udp_port': None, - 'addr6': False - }) - else: - if dest == 'enable': - dest = 'rfc5424' - obj.append({ - 'dest': dest, - 'level': None, - 'name': None, - 'udp_port': None, - 'addr6': False - }) - else: - - ip_match = re.search(r'^no logging buffered (\S+)', line, re.M) - if ip_match: - dest = 'buffered' - buff_level.append(ip_match.group(1)) - if 'no logging on' not in data: - obj.append({ - 'dest': 'on', - 'level': None, - 'name': None, - 'udp_port': None, - 'addr6': False - - }) - levels = set() - for items in dest_level: - if items not in buff_level: - levels.add(items) - obj.append({ - 'dest': 'buffered', - 'level': levels, - 'name': None, - 'udp_port': None, - 'addr6': False - - }) - return obj - - -def count_terms(check, param=None): - count = 0 - for term in check: - if param[term] is not None: - count += 1 - return count - - -def check_required_if(module, spec, param): - for sp in spec: - missing = [] - max_missing_count = 0 - is_one_of = False - if len(sp) == 4: - key, val, requirements, is_one_of = sp - else: - key, val, requirements = sp - - if is_one_of: - max_missing_count = len(requirements) - term = 'any' - else: - term = 'all' - - if key in param and param[key] == val: - for check in requirements: - count = count_terms((check,), param) - if count == 0: - missing.append(check) - if len(missing) and len(missing) >= max_missing_count: - msg = "%s is %s but %s of the following are missing: %s" % (key, val, term, ', '.join(missing)) - module.fail_json(msg=msg) - - -def map_params_to_obj(module, required_if=None): - obj = [] - addr6 = False - aggregate = module.params.get('aggregate') - - if aggregate: - for item in aggregate: - if item['name'] is not None and validate_ip_v6_address(item['name']): - addr6 = True - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - check_required_if(module, required_if, item) - item.update({'addr6': addr6}) - - d = item.copy() - d['level'] = set(d['level']) if d['level'] is not None else None - if d['dest'] != 'host': - d['name'] = None - d['udp_port'] = None - - if d['dest'] != 'buffered': - d['level'] = None - del d['check_running_config'] - obj.append(d) - - else: - if module.params['name'] is not None and validate_ip_v6_address(module.params['name']): - addr6 = True - if module.params['dest'] != 'host': - module.params['name'] = None - module.params['udp_port'] = None - - if module.params['dest'] != 'buffered': - module.params['level'] = None - - obj.append({ - 'dest': module.params['dest'], - 'name': module.params['name'], - 'udp_port': module.params['udp_port'], - 'level': set(module.params['level']) if module.params['level'] else None, - 'facility': module.params['facility'], - 'state': module.params['state'], - 'addr6': addr6 - }) - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - dest=dict( - type='str', - choices=[ - 'on', - 'host', - 'console', - 'buffered', - 'persistence', - 'rfc5424']), - name=dict( - type='str'), - udp_port=dict(), - level=dict( - type='list', - choices=[ - 'alerts', - 'critical', - 'debugging', - 'emergencies', - 'errors', - 'informational', - 'notifications', - 'warnings']), - facility=dict( - type='str', - choices=[ - 'auth', - 'cron', - 'daemon', - 'kern', - 'local0', - 'local1', - 'local2', - 'local3', - 'local4', - 'local5', - 'local6', - 'local7', - 'user', - 'lpr', - 'mail', - 'news', - 'syslog', - 'sys9', - 'sys10', - 'sys11', - 'sys12', - 'sys13', - 'sys14', - 'user', - 'uucp']), - state=dict( - default='present', - choices=[ - 'present', - 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG']))) - - aggregate_spec = deepcopy(element_spec) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - required_if = [('dest', 'host', ['name']), - ('dest', 'buffered', ['level'])] - module = AnsibleModule(argument_spec=argument_spec, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - warnings = list() - - exec_command(module, 'skip') - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module, required_if=required_if) - have = map_config_to_obj(module) - result['want'] = want - result['have'] = have - - commands = map_obj_to_commands((want, have)) - result['commands'] = commands - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_ping.py b/plugins/modules/network/icx/icx_ping.py deleted file mode 100644 index 828570e6f6..0000000000 --- a/plugins/modules/network/icx/icx_ping.py +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: icx_ping -author: "Ruckus Wireless (@Commscope)" -short_description: Tests reachability using ping from Ruckus ICX 7000 series switches -description: - - Tests reachability using ping from switch to a remote destination. -notes: - - Tested against ICX 10.1 -options: - count: - description: - - Number of packets to send. Default is 1. - type: int - dest: - description: - - ip-addr | host-name | vrf vrf-name | ipv6 [ ipv6-addr | host-name | vrf vrf-name] (resolvable by switch) of the remote node. - required: true - type: str - timeout: - description: - - Specifies the time, in milliseconds for which the device waits for a reply from the pinged device. - The value can range from 1 to 4294967296. The default is 5000 (5 seconds). - type: int - ttl: - description: - - Specifies the time to live as a maximum number of hops. The value can range from 1 to 255. The default is 64. - type: int - size: - description: - - Specifies the size of the ICMP data portion of the packet, in bytes. This is the payload and does not include the header. - The value can range from 0 to 10000. The default is 16.. - type: int - source: - description: - - IP address to be used as the origin of the ping packets. - type: str - vrf: - description: - - Specifies the Virtual Routing and Forwarding (VRF) instance of the device to be pinged. - type: str - state: - description: - - Determines if the expected result is success or fail. - type: str - choices: [ absent, present ] - default: present -''' - -EXAMPLES = r''' -- name: Test reachability to 10.10.10.10 - icx_ping: - dest: 10.10.10.10 - -- name: Test reachability to ipv6 address from source with timeout - icx_ping: - dest: ipv6 2001:cdba:0000:0000:0000:0000:3257:9652 - source: 10.1.1.1 - timeout: 100000 - -- name: Test reachability to 10.1.1.1 through vrf using 5 packets - icx_ping: - dest: 10.1.1.1 - vrf: x.x.x.x - count: 5 - -- name: Test unreachability to 10.30.30.30 - icx_ping: - dest: 10.40.40.40 - state: absent - -- name: Test reachability to ipv4 with ttl and packet size - icx_ping: - dest: 10.10.10.10 - ttl: 20 - size: 500 -''' - -RETURN = ''' -commands: - description: Show the command sent. - returned: always - type: list - sample: ["ping 10.40.40.40 count 20 source loopback0", "ping 10.40.40.40"] -packet_loss: - description: Percentage of packets lost. - returned: always - type: str - sample: "0%" -packets_rx: - description: Packets successfully received. - returned: always - type: int - sample: 20 -packets_tx: - description: Packets successfully transmitted. - returned: always - type: int - sample: 20 -rtt: - description: Show RTT stats. - returned: always - type: dict - sample: {"avg": 2, "max": 8, "min": 1} -''' - -from ansible.module_utils._text import to_text -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection, ConnectionError -import re - - -def build_ping(dest, count=None, source=None, timeout=None, ttl=None, size=None, vrf=None): - """ - Function to build the command to send to the terminal for the switch - to execute. All args come from the module's unique params. - """ - - if vrf is not None: - cmd = "ping vrf {0} {1}".format(vrf, dest) - else: - cmd = "ping {0}".format(dest) - - if count is not None: - cmd += " count {0}".format(str(count)) - - if timeout is not None: - cmd += " timeout {0}".format(str(timeout)) - - if ttl is not None: - cmd += " ttl {0}".format(str(ttl)) - - if size is not None: - cmd += " size {0}".format(str(size)) - - if source is not None: - cmd += " source {0}".format(source) - - return cmd - - -def parse_ping(ping_stats): - """ - Function used to parse the statistical information from the ping response. - Example: "Success rate is 100 percent (5/5), round-trip min/avg/max=40/51/55 ms." - Returns the percent of packet loss, received packets, transmitted packets, and RTT dict. - """ - if ping_stats.startswith('Success'): - rate_re = re.compile(r"^\w+\s+\w+\s+\w+\s+(?P\d+)\s+\w+\s+\((?P\d+)/(?P\d+)\)") - rtt_re = re.compile(r".*,\s+\S+\s+\S+=(?P\d+)/(?P\d+)/(?P\d+)\s+\w+\.+\s*$|.*\s*$") - - rate = rate_re.match(ping_stats) - rtt = rtt_re.match(ping_stats) - return rate.group("pct"), rate.group("rx"), rate.group("tx"), rtt.groupdict() - else: - rate_re = re.compile(r"^Sending+\s+(?P\d+),") - rate = rate_re.match(ping_stats) - rtt = {'avg': 0, 'max': 0, 'min': 0} - return 0, 0, rate.group('tx'), rtt - - -def validate_results(module, loss, results): - """ - This function is used to validate whether the ping results were unexpected per "state" param. - """ - state = module.params["state"] - if state == "present" and loss == 100: - module.fail_json(msg="Ping failed unexpectedly", **results) - elif state == "absent" and loss < 100: - module.fail_json(msg="Ping succeeded unexpectedly", **results) - - -def validate_fail(module, responses): - if ("Success" in responses or "No reply" in responses) is False: - module.fail_json(msg=responses) - - -def validate_parameters(module, timeout, count): - if timeout and not 1 <= int(timeout) <= 4294967294: - module.fail_json(msg="bad value for timeout - valid range (1-4294967294)") - if count and not 1 <= int(count) <= 4294967294: - module.fail_json(msg="bad value for count - valid range (1-4294967294)") - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - count=dict(type="int"), - dest=dict(type="str", required=True), - timeout=dict(type="int"), - ttl=dict(type="int"), - size=dict(type="int"), - source=dict(type="str"), - state=dict(type="str", choices=["absent", "present"], default="present"), - vrf=dict(type="str") - ) - - module = AnsibleModule(argument_spec=argument_spec) - - count = module.params["count"] - dest = module.params["dest"] - source = module.params["source"] - timeout = module.params["timeout"] - ttl = module.params["ttl"] - size = module.params["size"] - vrf = module.params["vrf"] - results = {} - warnings = list() - - if warnings: - results["warnings"] = warnings - - response = '' - try: - validate_parameters(module, timeout, count) - results["commands"] = [build_ping(dest, count, source, timeout, ttl, size, vrf)] - ping_results = run_commands(module, commands=results["commands"]) - ping_results_list = ping_results[0].split("\n") - - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - validate_fail(module, ping_results[0]) - - stats = "" - statserror = '' - for line in ping_results_list: - if line.startswith('Sending'): - statserror = line - if line.startswith('Success'): - stats = line - elif line.startswith('No reply'): - stats = statserror - - success, rx, tx, rtt = parse_ping(stats) - loss = abs(100 - int(success)) - results["packet_loss"] = str(loss) + "%" - results["packets_rx"] = int(rx) - results["packets_tx"] = int(tx) - - # Convert rtt values to int - for k, v in rtt.items(): - if rtt[k] is not None: - rtt[k] = int(v) - - results["rtt"] = rtt - - validate_results(module, loss, results) - - module.exit_json(**results) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_static_route.py b/plugins/modules/network/icx/icx_static_route.py deleted file mode 100644 index 51b6855bfa..0000000000 --- a/plugins/modules/network/icx/icx_static_route.py +++ /dev/null @@ -1,314 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: icx_static_route -author: "Ruckus Wireless (@Commscope)" -short_description: Manage static IP routes on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of static - IP routes on Ruckus ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - prefix: - description: - - Network prefix of the static route. - type: str - mask: - description: - - Network prefix mask of the static route. - type: str - next_hop: - description: - - Next hop IP of the static route. - type: str - admin_distance: - description: - - Admin distance of the static route. Range is 1 to 255. - type: int - aggregate: - description: List of static route definitions. - type: list - suboptions: - prefix: - description: - - Network prefix of the static route. - type: str - mask: - description: - - Network prefix mask of the static route. - type: str - next_hop: - description: - - Next hop IP of the static route. - type: str - admin_distance: - description: - - Admin distance of the static route. Range is 1 to 255. - type: int - state: - description: - - State of the static route configuration. - type: str - choices: ['present', 'absent'] - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - purge: - description: - - Purge routes not defined in the I(aggregate) parameter. - default: no - type: bool - state: - description: - - State of the static route configuration. - type: str - default: present - choices: ['present', 'absent'] - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - default: yes -''' - -EXAMPLES = """ -- name: configure static route - icx_static_route: - prefix: 192.168.2.0/24 - next_hop: 10.0.0.1 - -- name: remove configuration - icx_static_route: - prefix: 192.168.2.0 - mask: 255.255.255.0 - next_hop: 10.0.0.1 - state: absent - -- name: Add static route aggregates - icx_static_route: - aggregate: - - { prefix: 172.16.32.0, mask: 255.255.255.0, next_hop: 10.0.0.8 } - - { prefix: 172.16.33.0, mask: 255.255.255.0, next_hop: 10.0.0.8 } - -- name: remove static route aggregates - icx_static_route: - aggregate: - - { prefix: 172.16.32.0, mask: 255.255.255.0, next_hop: 10.0.0.8 } - - { prefix: 172.16.33.0, mask: 255.255.255.0, next_hop: 10.0.0.8 } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - ip route 192.168.2.0 255.255.255.0 10.0.0.1 -""" - - -from copy import deepcopy -import re - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.connection import ConnectionError -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import get_config, load_config - -try: - from ipaddress import ip_network - HAS_IPADDRESS = True -except ImportError: - HAS_IPADDRESS = False - - -def map_obj_to_commands(want, have, module): - commands = list() - purge = module.params['purge'] - for w in want: - for h in have: - for key in ['prefix', 'mask', 'next_hop']: - if w[key] != h[key]: - break - else: - break - else: - h = None - - prefix = w['prefix'] - mask = w['mask'] - next_hop = w['next_hop'] - admin_distance = w.get('admin_distance') - if not admin_distance and h: - w['admin_distance'] = admin_distance = h['admin_distance'] - state = w['state'] - del w['state'] - - if state == 'absent' and have == []: - commands.append('no ip route %s %s %s' % (prefix, mask, next_hop)) - - if state == 'absent' and w in have: - commands.append('no ip route %s %s %s' % (prefix, mask, next_hop)) - elif state == 'present' and w not in have: - if admin_distance: - commands.append('ip route %s %s %s distance %s' % (prefix, mask, next_hop, admin_distance)) - else: - commands.append('ip route %s %s %s' % (prefix, mask, next_hop)) - if purge: - commands = [] - for h in have: - if h not in want: - commands.append('no ip route %s %s %s' % (prefix, mask, next_hop)) - return commands - - -def map_config_to_obj(module): - obj = [] - compare = module.params['check_running_config'] - out = get_config(module, flags='| include ip route', compare=compare) - - for line in out.splitlines(): - splitted_line = line.split() - if len(splitted_line) not in (4, 5, 6): - continue - cidr = ip_network(to_text(splitted_line[2])) - prefix = str(cidr.network_address) - mask = str(cidr.netmask) - next_hop = splitted_line[3] - if len(splitted_line) == 6: - admin_distance = splitted_line[5] - else: - admin_distance = '1' - - obj.append({ - 'prefix': prefix, 'mask': mask, 'next_hop': next_hop, - 'admin_distance': admin_distance - }) - - return obj - - -def prefix_length_parser(prefix, mask, module): - if '/' in prefix and mask is not None: - module.fail_json(msg='Ambigous, specifed both length and mask') - if '/' in prefix: - cidr = ip_network(to_text(prefix)) - prefix = str(cidr.network_address) - mask = str(cidr.netmask) - return prefix, mask - - -def map_params_to_obj(module, required_together=None): - keys = ['prefix', 'mask', 'next_hop', 'admin_distance', 'state'] - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - route = item.copy() - for key in keys: - if route.get(key) is None: - route[key] = module.params.get(key) - - module._check_required_together(required_together, route) - - prefix, mask = prefix_length_parser(route['prefix'], route['mask'], module) - route.update({'prefix': prefix, 'mask': mask}) - - obj.append(route) - else: - module._check_required_together(required_together, module.params) - prefix, mask = prefix_length_parser(module.params['prefix'], module.params['mask'], module) - - obj.append({ - 'prefix': prefix, - 'mask': mask, - 'next_hop': module.params['next_hop'].strip(), - 'admin_distance': module.params.get('admin_distance'), - 'state': module.params['state'], - }) - - for route in obj: - if route['admin_distance']: - route['admin_distance'] = str(route['admin_distance']) - - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - prefix=dict(type='str'), - mask=dict(type='str'), - next_hop=dict(type='str'), - admin_distance=dict(type='int'), - state=dict(default='present', choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['prefix'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - - required_one_of = [['aggregate', 'prefix']] - required_together = [['prefix', 'next_hop']] - mutually_exclusive = [['aggregate', 'prefix']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - if not HAS_IPADDRESS: - module.fail_json(msg="ipaddress python package is required") - - warnings = list() - - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module, required_together=required_together) - have = map_config_to_obj(module) - - commands = map_obj_to_commands(want, have, module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_system.py b/plugins/modules/network/icx/icx_system.py deleted file mode 100644 index ffda03fece..0000000000 --- a/plugins/modules/network/icx/icx_system.py +++ /dev/null @@ -1,470 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: icx_system -author: "Ruckus Wireless (@Commscope)" -short_description: Manage the system attributes on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of node system attributes - on Ruckus ICX 7000 series switches. It provides an option to configure host system - parameters or remove those parameters from the device active - configuration. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - hostname: - description: - - Configure the device hostname parameter. This option takes an ASCII string value. - type: str - domain_name: - description: - - Configure the IP domain name on the remote device to the provided value. - Value should be in the dotted name form and - will be appended to the hostname to create a fully-qualified domain name. - type: list - domain_search: - description: - - Provides the list of domain names to - append to the hostname for the purpose of doing name resolution. - This argument accepts a list of names and will be reconciled - with the current active configuration on the running node. - type: list - name_servers: - description: - - List of DNS name servers by IP address to use to perform name resolution - lookups. - type: list - aaa_servers: - description: - - Configures radius/tacacs server - type: list - suboptions: - type: - description: - - specify the type of the server - type: str - choices: ['radius','tacacs'] - hostname: - description: - - Configures the host name of the RADIUS server - type: str - auth_port_type: - description: - - specifies the type of the authentication port - type: str - choices: ['auth-port'] - auth_port_num: - description: - - Configures the authentication UDP port. The default value is 1812. - type: str - acct_port_num: - description: - - Configures the accounting UDP port. The default value is 1813. - type: str - acct_type: - description: - - Usage of the accounting port. - type: str - choices: ['accounting-only', 'authentication-only','authorization-only', default] - auth_key: - description: - - Configure the key for the server - type: str - auth_key_type: - description: - - List of authentication level specified in the choices - type: list - choices: ['dot1x','mac-auth','web-auth'] - state: - description: - - State of the configuration - values in the device's current active configuration. When set - to I(present), the values should be configured in the device active - configuration and when set to I(absent) the values should not be - in the device active configuration - type: str - default: present - choices: ['present', 'absent'] - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - default: yes -''' - -EXAMPLES = """ -- name: configure hostname and domain name - icx_system: - hostname: icx - domain_search: - - ansible.com - - redhat.com - - ruckus.com - -- name: configure radius server of type auth-port - icx_system: - aaa_servers: - - type: radius - hostname: radius-server - auth_port_type: auth-port - auth_port_num: 1821 - acct_port_num: 1321 - acct_type: accounting-only - auth_key: abc - auth_key_type: - - dot1x - - mac-auth - -- name: configure tacacs server - icx_system: - aaa_servers: - - type: tacacs - hostname: tacacs-server - auth_port_type: auth-port - auth_port_num: 1821 - acct_port_num: 1321 - acct_type: accounting-only - auth_key: xyz - -- name: configure name servers - icx_system: - name_servers: - - 8.8.8.8 - - 8.8.4.4 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - hostname icx - - ip domain name test.example.com - - radius-server host 172.16.10.12 auth-port 2083 acct-port 1850 default key abc dot1x mac-auth - - tacacs-server host 10.2.3.4 auth-port 4058 authorization-only key xyz - -""" - - -import re -from copy import deepcopy -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import get_config, load_config -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList, validate_ip_v6_address -from ansible.module_utils.connection import Connection, ConnectionError, exec_command - - -def diff_list(want, have): - adds = [w for w in want if w not in have] - removes = [h for h in have if h not in want] - return (adds, removes) - - -def map_obj_to_commands(want, have, module): - commands = list() - state = module.params['state'] - - def needs_update(x): - return want.get(x) is not None and (want.get(x) != have.get(x)) - - if state == 'absent': - if have['name_servers'] == [] and have['aaa_servers'] == [] and have['domain_search'] == [] and have['hostname'] is None: - if want['hostname']: - commands.append('no hostname') - - if want['domain_search']: - for item in want['domain_search']: - commands.append('no ip dns domain-list %s' % item) - - if want['name_servers']: - for item in want['name_servers']: - commands.append('no ip dns server-address %s' % item) - - if want['aaa_servers']: - want_servers = [] - want_server = want['aaa_servers'] - if want_server: - want_list = deepcopy(want_server) - for items in want_list: - items['auth_key'] = None - want_servers.append(items) - for item in want_servers: - ipv6addr = validate_ip_v6_address(item['hostname']) - if ipv6addr: - commands.append('no ' + item['type'] + '-server host ipv6 ' + item['hostname']) - else: - commands.append('no ' + item['type'] + '-server host ' + item['hostname']) - - if want['hostname']: - if have['hostname'] == want['hostname']: - commands.append('no hostname') - - if want['domain_search']: - for item in want['domain_search']: - if item in have['domain_search']: - commands.append('no ip dns domain-list %s' % item) - - if want['name_servers']: - for item in want['name_servers']: - if item in have['name_servers']: - commands.append('no ip dns server-address %s' % item) - - if want['aaa_servers']: - want_servers = [] - want_server = want['aaa_servers'] - have_server = have['aaa_servers'] - if want_server: - want_list = deepcopy(want_server) - for items in want_list: - items['auth_key'] = None - want_servers.append(items) - for item in want_servers: - if item in have_server: - ipv6addr = validate_ip_v6_address(item['hostname']) - if ipv6addr: - commands.append('no ' + item['type'] + '-server host ipv6 ' + item['hostname']) - else: - commands.append('no ' + item['type'] + '-server host ' + item['hostname']) - - elif state == 'present': - if needs_update('hostname'): - commands.append('hostname %s' % want['hostname']) - - if want['domain_search']: - adds, removes = diff_list(want['domain_search'], have['domain_search']) - for item in removes: - commands.append('no ip dns domain-list %s' % item) - for item in adds: - commands.append('ip dns domain-list %s' % item) - - if want['name_servers']: - adds, removes = diff_list(want['name_servers'], have['name_servers']) - for item in removes: - commands.append('no ip dns server-address %s' % item) - for item in adds: - commands.append('ip dns server-address %s' % item) - - if want['aaa_servers']: - want_servers = [] - want_server = want['aaa_servers'] - have_server = have['aaa_servers'] - want_list = deepcopy(want_server) - for items in want_list: - items['auth_key'] = None - want_servers.append(items) - - adds, removes = diff_list(want_servers, have_server) - - for item in removes: - ip6addr = validate_ip_v6_address(item['hostname']) - if ip6addr: - cmd = 'no ' + item['type'] + '-server host ipv6 ' + item['hostname'] - else: - cmd = 'no ' + item['type'] + '-server host ' + item['hostname'] - commands.append(cmd) - - for w_item in adds: - for item in want_server: - if item['hostname'] == w_item['hostname'] and item['type'] == w_item['type']: - auth_key = item['auth_key'] - - ip6addr = validate_ip_v6_address(w_item['hostname']) - if ip6addr: - cmd = w_item['type'] + '-server host ipv6 ' + w_item['hostname'] - else: - cmd = w_item['type'] + '-server host ' + w_item['hostname'] - if w_item['auth_port_type']: - cmd += ' ' + w_item['auth_port_type'] + ' ' + w_item['auth_port_num'] - if w_item['acct_port_num'] and w_item['type'] == 'radius': - cmd += ' acct-port ' + w_item['acct_port_num'] - if w_item['type'] == 'tacacs': - if any((w_item['acct_port_num'], w_item['auth_key_type'])): - module.fail_json(msg='acct_port and auth_key_type is not applicable for tacacs server') - if w_item['acct_type']: - cmd += ' ' + w_item['acct_type'] - if auth_key is not None: - cmd += ' key ' + auth_key - if w_item['auth_key_type'] and w_item['type'] == 'radius': - val = '' - for y in w_item['auth_key_type']: - val = val + ' ' + y - cmd += val - commands.append(cmd) - - return commands - - -def parse_hostname(config): - match = re.search(r'^hostname (\S+)', config, re.M) - if match: - return match.group(1) - - -def parse_domain_search(config): - match = re.findall(r'^ip dns domain[- ]list (\S+)', config, re.M) - matches = list() - for name in match: - matches.append(name) - return matches - - -def parse_name_servers(config): - matches = list() - values = list() - lines = config.split('\n') - for line in lines: - if 'ip dns server-address' in line: - values = line.split(' ') - for val in values: - match = re.search(r'([0-9.]+)', val) - if match: - matches.append(match.group()) - - return matches - - -def parse_aaa_servers(config): - configlines = config.split('\n') - obj = [] - for line in configlines: - auth_key_type = [] - if 'radius-server' in line or 'tacacs-server' in line: - aaa_type = 'radius' if 'radius-server' in line else 'tacacs' - match = re.search(r'(host ipv6 (\S+))|(host (\S+))', line) - if match: - hostname = match.group(2) if match.group(2) is not None else match.group(4) - match = re.search(r'auth-port ([0-9]+)', line) - if match: - auth_port_num = match.group(1) - else: - auth_port_num = None - match = re.search(r'acct-port ([0-9]+)', line) - if match: - acct_port_num = match.group(1) - else: - acct_port_num = None - match = re.search(r'acct-port [0-9]+ (\S+)', line) - if match: - acct_type = match.group(1) - else: - acct_type = None - if aaa_type == 'tacacs': - match = re.search(r'auth-port [0-9]+ (\S+)', line) - if match: - acct_type = match.group(1) - else: - acct_type = None - match = re.search(r'(dot1x)', line) - if match: - auth_key_type.append('dot1x') - match = re.search(r'(mac-auth)', line) - if match: - auth_key_type.append('mac-auth') - match = re.search(r'(web-auth)', line) - if match: - auth_key_type.append('web-auth') - - obj.append({ - 'type': aaa_type, - 'hostname': hostname, - 'auth_port_type': 'auth-port', - 'auth_port_num': auth_port_num, - 'acct_port_num': acct_port_num, - 'acct_type': acct_type, - 'auth_key': None, - 'auth_key_type': set(auth_key_type) if len(auth_key_type) > 0 else None - }) - - return obj - - -def map_config_to_obj(module): - compare = module.params['check_running_config'] - config = get_config(module, None, compare=compare) - return { - 'hostname': parse_hostname(config), - 'domain_search': parse_domain_search(config), - 'name_servers': parse_name_servers(config), - 'aaa_servers': parse_aaa_servers(config) - } - - -def map_params_to_obj(module): - if module.params['aaa_servers']: - for item in module.params['aaa_servers']: - if item['auth_key_type']: - item['auth_key_type'] = set(item['auth_key_type']) - obj = { - 'hostname': module.params['hostname'], - 'domain_name': module.params['domain_name'], - 'domain_search': module.params['domain_search'], - 'name_servers': module.params['name_servers'], - 'state': module.params['state'], - 'aaa_servers': module.params['aaa_servers'] - } - return obj - - -def main(): - """ Main entry point for Ansible module execution - """ - server_spec = dict( - type=dict(choices=['radius', 'tacacs']), - hostname=dict(), - auth_port_type=dict(choices=['auth-port']), - auth_port_num=dict(), - acct_port_num=dict(), - acct_type=dict(choices=['accounting-only', 'authentication-only', 'authorization-only', 'default']), - auth_key=dict(), - auth_key_type=dict(type='list', choices=['dot1x', 'mac-auth', 'web-auth']) - ) - argument_spec = dict( - hostname=dict(), - - domain_name=dict(type='list'), - domain_search=dict(type='list'), - name_servers=dict(type='list'), - - aaa_servers=dict(type='list', elements='dict', options=server_spec), - state=dict(choices=['present', 'absent'], default='present'), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - - result['warnings'] = warnings - exec_command(module, 'skip') - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands(want, have, module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/icx/icx_user.py b/plugins/modules/network/icx/icx_user.py deleted file mode 100644 index e26f9dc849..0000000000 --- a/plugins/modules/network/icx/icx_user.py +++ /dev/null @@ -1,390 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: icx_user -author: "Ruckus Wireless (@Commscope)" -short_description: Manage the user accounts on Ruckus ICX 7000 series switches. -description: - - This module creates or updates user account on network devices. It allows playbooks to manage - either individual usernames or the aggregate of usernames in the - current running config. It also supports purging usernames from the - configuration that are not explicitly defined. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - aggregate: - description: - - The set of username objects to be configured on the remote - ICX device. The list entries can either be the username - or a hash of username and properties. This argument is mutually - exclusive with the C(name) argument. - aliases: ['users', 'collection'] - type: list - suboptions: - name: - description: - - The username to be configured on the ICX device. - required: true - type: str - configured_password: - description: The password to be configured on the ICX device. - type: str - update_password: - description: - - This argument will instruct the module when to change the password. When - set to C(always), the password will always be updated in the device - and when set to C(on_create) the password will be updated only if - the username is created. - choices: ['on_create', 'always'] - type: str - privilege: - description: - - The privilege level to be granted to the user - choices: ['0', '4', '5'] - type: str - nopassword: - description: - - Defines the username without assigning - a password. This will allow the user to login to the system - without being authenticated by a password. - type: bool - state: - description: - - Configures the state of the username definition - as it relates to the device operational configuration. When set - to I(present), the username(s) should be configured in the device active - configuration and when set to I(absent) the username(s) should not be - in the device active configuration - choices: ['present', 'absent'] - type: str - access_time: - description: - - This parameter indicates the time the file's access time should be set to. - Should be preserve when no modification is required, YYYYMMDDHHMM.SS when using default time format, or now. - Default is None meaning that preserve is the default for state=[file,directory,link,hard] and now is default for state=touch - type: str - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - name: - description: - - The username to be configured on the ICX device. - required: true - type: str - configured_password: - description: The password to be configured on the ICX device. - type: str - update_password: - description: - - This argument will instruct the module when to change the password. When - set to C(always), the password will always be updated in the device - and when set to C(on_create) the password will be updated only if - the username is created. - default: always - choices: ['on_create', 'always'] - type: str - privilege: - description: - - The privilege level to be granted to the user - default: 0 - choices: ['0', '4', '5'] - type: str - nopassword: - description: - - Defines the username without assigning - a password. This will allow the user to login to the system - without being authenticated by a password. - type: bool - purge: - description: - - If set to true module will remove any previously - configured usernames on the device except the current defined set of users. - type: bool - default: false - state: - description: - - Configures the state of the username definition - as it relates to the device operational configuration. When set - to I(present), the username(s) should be configured in the device active - configuration and when set to I(absent) the username(s) should not be - in the device active configuration - default: present - choices: ['present', 'absent'] - type: str - access_time: - description: - - This parameter indicates the time the file's access time should be set to. - Should be preserve when no modification is required, YYYYMMDDHHMM.SS when using default time format, or now. - Default is None meaning that preserve is the default for state=[file,directory,link,hard] and now is default for state=touch - type: str - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - default: yes -''' - -EXAMPLES = """ -- name: create a new user without password - icx_user: - name: user1 - nopassword: true - -- name: create a new user with password - icx_user: - name: user1 - configured_password: 'newpassword' - -- name: remove users - icx_user: - name: user1 - state: absent - -- name: set user privilege level to 5 - icx_user: - name: user1 - privilege: 5 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - username ansible nopassword - - username ansible password-string alethea123 - - no username ansible - - username ansible privilege 5 - - username ansible enable -""" - -from copy import deepcopy - -import re -import base64 -import hashlib - -from functools import partial - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible.module_utils.connection import exec_command -from ansible.module_utils.six import iteritems -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import get_config, load_config - - -def get_param_value(key, item, module): - if not item.get(key): - value = module.params[key] - - else: - value_type = module.argument_spec[key].get('type', 'str') - type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type] - type_checker(item[key]) - value = item[key] - - validator = globals().get('validate_%s' % key) - if all((value, validator)): - validator(value, module) - - return value - - -def map_params_to_obj(module): - users = module.params['aggregate'] - if not users: - if not module.params['name'] and module.params['purge']: - return list() - elif not module.params['name']: - module.fail_json(msg='username is required') - else: - aggregate = [{'name': module.params['name']}] - else: - aggregate = list() - for item in users: - if not isinstance(item, dict): - aggregate.append({'name': item}) - elif 'name' not in item: - module.fail_json(msg='name is required') - else: - aggregate.append(item) - - objects = list() - - for item in aggregate: - get_value = partial(get_param_value, item=item, module=module) - item['configured_password'] = get_value('configured_password') - item['nopassword'] = get_value('nopassword') - item['privilege'] = get_value('privilege') - item['state'] = get_value('state') - objects.append(item) - - return objects - - -def parse_privilege(data): - match = re.search(r'privilege (\S)', data, re.M) - if match: - return match.group(1) - - -def map_config_to_obj(module): - compare = module.params['check_running_config'] - data = get_config(module, flags=['| include username'], compare=compare) - - match = re.findall(r'(?:^(?:u|\s{2}u))sername (\S+)', data, re.M) - if not match: - return list() - - instances = list() - - for user in set(match): - regex = r'username %s .+$' % user - cfg = re.findall(regex, data, re.M) - cfg = '\n'.join(cfg) - obj = { - 'name': user, - 'state': 'present', - 'nopassword': 'nopassword' in cfg, - 'configured_password': None, - 'privilege': parse_privilege(cfg) - } - instances.append(obj) - - return instances - - -def map_obj_to_commands(updates, module): - commands = list() - state = module.params['state'] - update_password = module.params['update_password'] - - def needs_update(want, have, x): - return want.get(x) and (want.get(x) != have.get(x)) - - def add(command, want, x): - command.append('username %s %s' % (want['name'], x)) - for update in updates: - want, have = update - if want['state'] == 'absent': - commands.append(user_del_cmd(want['name'])) - - if needs_update(want, have, 'privilege'): - add(commands, want, 'privilege %s password %s' % (want['privilege'], want['configured_password'])) - else: - if needs_update(want, have, 'configured_password'): - if update_password == 'always' or not have: - add(commands, want, '%spassword %s' % ('privilege ' + str(have.get('privilege')) + - " " if have.get('privilege') is not None else '', want['configured_password'])) - - if needs_update(want, have, 'nopassword'): - if want['nopassword']: - add(commands, want, 'nopassword') - - if needs_update(want, have, 'access_time'): - add(commands, want, 'access-time %s' % want['access_time']) - - if needs_update(want, have, 'expiry_days'): - add(commands, want, 'expires %s' % want['expiry_days']) - - return commands - - -def update_objects(want, have): - updates = list() - for entry in want: - item = next((i for i in have if i['name'] == entry['name']), None) - - if all((item is None, entry['state'] == 'present')): - updates.append((entry, {})) - - elif all((have == [], entry['state'] == 'absent')): - for key, value in iteritems(entry): - if key not in ['update_password']: - updates.append((entry, item)) - break - elif item: - for key, value in iteritems(entry): - if key not in ['update_password']: - if value is not None and value != item.get(key): - updates.append((entry, item)) - break - return updates - - -def user_del_cmd(username): - return 'no username %s' % username - - -def main(): - """entry point for module execution - """ - element_spec = dict( - name=dict(), - - configured_password=dict(no_log=True), - nopassword=dict(type='bool', default=False), - update_password=dict(default='always', choices=['on_create', 'always']), - privilege=dict(type='str', choices=['0', '4', '5']), - access_time=dict(type='str'), - state=dict(default='present', choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec, aliases=['users', 'collection']), - purge=dict(type='bool', default=False) - ) - - argument_spec.update(element_spec) - - mutually_exclusive = [('name', 'aggregate')] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - result = {'changed': False} - exec_command(module, 'skip') - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands(update_objects(want, have), module) - - if module.params['purge']: - want_users = [x['name'] for x in want] - have_users = [x['name'] for x in have] - for item in set(have_users).difference(want_users): - if item != 'admin': - commands.append(user_del_cmd(item)) - - result["commands"] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/icx/icx_vlan.py b/plugins/modules/network/icx/icx_vlan.py deleted file mode 100644 index 468a7d0988..0000000000 --- a/plugins/modules/network/icx/icx_vlan.py +++ /dev/null @@ -1,783 +0,0 @@ -#!/usr/bin/python -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: icx_vlan -author: "Ruckus Wireless (@Commscope)" -short_description: Manage VLANs on Ruckus ICX 7000 series switches -description: - - This module provides declarative management of VLANs - on ICX network devices. -notes: - - Tested against ICX 10.1. - - For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html). -options: - name: - description: - - Name of the VLAN. - type: str - vlan_id: - description: - - ID of the VLAN. Range 1-4094. - required: true - type: int - interfaces: - description: - - List of ethernet ports or LAGS to be added as access(untagged) ports to the vlan. - To add a range of ports use 'to' keyword. See the example. - suboptions: - name: - description: - - Name of the interface or lag - type: list - purge: - description: - - Purge interfaces not defined in the I(name) - type: bool - type: dict - tagged: - description: - - List of ethernet ports or LAGS to be added as trunk(tagged) ports to the vlan. - To add a range of ports use 'to' keyword. See the example. - suboptions: - name: - description: - - Name of the interface or lag - type: list - purge: - description: - - Purge interfaces not defined in the I(name) - type: bool - type: dict - ip_dhcp_snooping: - description: - - Enables DHCP snooping on a VLAN. - type: bool - ip_arp_inspection: - description: - - Enables dynamic ARP inspection on a VLAN. - type: bool - associated_interfaces: - description: - - This is a intent option and checks the operational state of the for given vlan C(name) - for associated interfaces. If the value in the C(associated_interfaces) does not match with - the operational state of vlan interfaces on device it will result in failure. - type: list - associated_tagged: - description: - - This is a intent option and checks the operational state of given vlan C(name) - for associated tagged ports and lags. If the value in the C(associated_tagged) does not match with - the operational state of vlan interfaces on device it will result in failure. - type: list - delay: - description: - - Delay the play should wait to check for declarative intent params values. - default: 10 - type: int - stp: - description: - - Enable spanning-tree 802-1w/rstp for this vlan. - suboptions: - type: - description: - - Specify the type of spanning-tree - type: str - default: 802-1w - choices: ['802-1w','rstp'] - priority: - description: - - Configures the priority of the bridge. The value ranges from - 0 through 65535. A lower numerical value means the bridge has - a higher priority. Thus, the highest priority is 0. The default is 32768. - type: str - enabled: - description: - - Manage the state(Enable/Disable) of the spanning_tree_802_1w in the current vlan - type: bool - type: dict - aggregate: - description: - - List of VLANs definitions. - type: list - suboptions: - name: - description: - - Name of the VLAN. - type: str - vlan_id: - description: - - ID of the VLAN. Range 1-4094. - required: true - type: str - ip_dhcp_snooping: - description: - - Enables DHCP snooping on a VLAN. - type: bool - ip_arp_inspection: - description: - - Enables dynamic ARP inspection on a VLAN. - type: bool - tagged: - description: - - List of ethernet ports or LAGS to be added as trunk(tagged) ports to the vlan. - To add a range of ports use 'to' keyword. See the example. - suboptions: - name: - description: - - Name of the interface or lag - type: list - purge: - description: - - Purge interfaces not defined in the I(name) - type: bool - type: dict - interfaces: - description: - - List of ethernet ports or LAGS to be added as access(untagged) ports to the vlan. - To add a range of ports use 'to' keyword. See the example. - suboptions: - name: - description: - - Name of the interface or lag - type: list - purge: - description: - - Purge interfaces not defined in the I(name) - type: bool - type: dict - delay: - description: - - Delay the play should wait to check for declarative intent params values. - type: int - stp: - description: - - Enable spanning-tree 802-1w/rstp for this vlan. - suboptions: - type: - description: - - Specify the type of spanning-tree - type: str - default: 802-1w - choices: ['802-1w','rstp'] - priority: - description: - - Configures the priority of the bridge. The value ranges from - 0 through 65535. A lower numerical value means the bridge has - a higher priority. Thus, the highest priority is 0. The default is 32768. - type: str - enabled: - description: - - Manage the state(Enable/Disable) of the spanning_tree_802_1w in the current vlan - type: bool - type: dict - state: - description: - - State of the VLAN configuration. - type: str - choices: ['present', 'absent'] - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - associated_interfaces: - description: - - This is a intent option and checks the operational state of the for given vlan C(name) - for associated interfaces. If the value in the C(associated_interfaces) does not match with - the operational state of vlan interfaces on device it will result in failure. - type: list - associated_tagged: - description: - - This is a intent option and checks the operational state of given vlan C(name) - for associated tagged ports and lags. If the value in the C(associated_tagged) does not match with - the operational state of vlan interfaces on device it will result in failure. - type: list - purge: - description: - - Purge VLANs not defined in the I(aggregate) parameter. - default: no - type: bool - state: - description: - - State of the VLAN configuration. - type: str - default: present - choices: ['present', 'absent'] - check_running_config: - description: - - Check running configuration. This can be set as environment variable. - Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter. - type: bool - default: yes -''' - -EXAMPLES = """ -- name: Add a single ethernet 1/1/48 as access(untagged) port to vlan 20 - icx_vlan: - name: test-vlan - vlan_id: 20 - interfaces: - name: - - ethernet 1/1/48 - -- name: Add a single LAG 10 as access(untagged) port to vlan 20 - icx_vlan: - vlan_id: 20 - interfaces: - name: - - lag 10 - -- name: Add a range of ethernet ports as trunk(tagged) ports to vlan 20 by port - icx_vlan: - vlan_id: 20 - tagged: - name: - - ethernet 1/1/40 to 1/1/48 - -- name: Add discontinuous lags, ethernet ports as access(untagged) and trunk(tagged) port to vlan 20. - icx_vlan: - vlan_id: 20 - interfaces: - name: - - ethernet 1/1/40 to 1/1/48 - - ethernet 2/1/1 - - lag 1 - - lag 3 to 5 - tagged: - name: - - ethernet 1/1/20 to 1/1/25 - - lag 1 to 3 - -- name: Remove an access and range of trunk ports from vlan - icx_vlan: - vlan_id: 20 - interfaces: - name: - - ethernet 1/1/40 - tagged: - name: - - ethernet 1/1/39 to 1/1/70 - -- name: Enable dhcp snooping, disable arp inspection in vlan - icx_vlan: - vlan_id: 20 - ip_dhcp_snooping: present - ip_arp_inspection: absent - -- name: Create vlan 20. Enable arp inspection in vlan. Purge all other vlans. - icx_vlan: - vlan_id: 20 - ip_arp_inspection: present - purge: present - -- name: Remove vlan 20. - icx_vlan: - vlan_id: 20 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - vlan 100 - - name test-vlan -""" - -import re -from time import sleep -import itertools -from copy import deepcopy -from time import sleep -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.community.general.plugins.module_utils.network.icx.icx import load_config, get_config -from ansible.module_utils.connection import Connection, ConnectionError, exec_command -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import conditional, remove_default_spec - - -def search_obj_in_list(vlan_id, lst): - obj = list() - for o in lst: - if str(o['vlan_id']) == vlan_id: - return o - - -def parse_vlan_brief(module, vlan_id): - command = 'show run vlan %s' % vlan_id - rc, out, err = exec_command(module, command) - lines = out.split('\n') - untagged_ports = list() - untagged_lags = list() - tagged_ports = list() - tagged_lags = list() - - for line in lines: - if 'tagged' in line.split(): - lags = line.split(" lag ") - ports = lags[0].split(" ethe ") - del ports[0] - del lags[0] - for port in ports: - if "to" in port: - p = port.split(" to ") - pr = int(p[1].split('/')[2]) - int(p[0].split('/')[2]) - for i in range(0, pr + 1): - tagged_ports.append((int(p[0].split('/')[2]) + i)) - else: - tagged_ports.append(int(port.split('/')[2])) - for lag in lags: - if "to" in lag: - l = lag.split(" to ") - lr = int(l[1]) - int(l[0]) - for i in range(0, lr + 1): - tagged_lags.append((int(l[0]) + i)) - else: - tagged_lags.append(int(lag)) - if 'untagged' in line.split(): - lags = line.split(" lag ") - ports = lags[0].split(" ethe ") - del ports[0] - del lags[0] - for port in ports: - if "to" in port: - p = port.split(" to ") - pr = int(p[1].split('/')[2]) - int(p[0].split('/')[2]) - for i in range(0, pr + 1): - untagged_ports.append((int(p[0].split('/')[2]) + i)) - else: - untagged_ports.append(int(port.split('/')[2])) - for lag in lags: - if "to" in lag: - l = lag.split(" to ") - lr = int(l[1]) - int(l[0]) - for i in range(0, lr + 1): - untagged_lags.append((int(l[0]) + i)) - else: - untagged_lags.append(int(lag)) - - return untagged_ports, untagged_lags, tagged_ports, tagged_lags - - -def extract_list_from_interface(interface): - if 'ethernet' in interface: - if 'to' in interface: - s = re.search(r"\d+\/\d+/(?P\d+)\sto\s+\d+\/\d+/(?P\d+)", interface) - low = int(s.group('low')) - high = int(s.group('high')) - else: - s = re.search(r"\d+\/\d+/(?P\d+)", interface) - low = int(s.group('low')) - high = int(s.group('low')) - elif 'lag' in interface: - if 'to' in interface: - s = re.search(r"(?P\d+)\sto\s(?P\d+)", interface) - low = int(s.group('low')) - high = int(s.group('high')) - else: - s = re.search(r"(?P\d+)", interface) - low = int(s.group('low')) - high = int(s.group('low')) - - return low, high - - -def parse_vlan_id(module): - vlans = [] - command = 'show vlan brief' - rc, out, err = exec_command(module, command) - lines = out.split('\n') - for line in lines: - if 'VLANs Configured :' in line: - values = line.split(':')[1] - vlans = [s for s in values.split() if s.isdigit()] - s = re.findall(r"(?P\d+)\sto\s(?P\d+)", values) - for ranges in s: - low = int(ranges[0]) + 1 - high = int(ranges[1]) - while(high > low): - vlans.append(str(low)) - low = low + 1 - return vlans - - -def spanning_tree(module, stp): - stp_cmd = list() - if stp.get('enabled') is False: - if stp.get('type') == '802-1w': - stp_cmd.append('no spanning-tree' + ' ' + stp.get('type')) - stp_cmd.append('no spanning-tree') - - elif stp.get('type'): - stp_cmd.append('spanning-tree' + ' ' + stp.get('type')) - if stp.get('priority') and stp.get('type') == 'rstp': - module.fail_json(msg='spanning-tree 802-1w only can have priority') - elif stp.get('priority'): - stp_cmd.append('spanning-tree' + ' ' + stp.get('type') + ' ' + 'priority' + ' ' + stp.get('priority')) - - return stp_cmd - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - stp = item.get('stp') - if stp: - stp_cmd = spanning_tree(module, stp) - item.update({'stp': stp_cmd}) - - d = item.copy() - - obj.append(d) - - else: - params = { - 'name': module.params['name'], - 'vlan_id': module.params['vlan_id'], - 'interfaces': module.params['interfaces'], - 'tagged': module.params['tagged'], - 'associated_interfaces': module.params['associated_interfaces'], - 'associated_tagged': module.params['associated_tagged'], - 'delay': module.params['delay'], - 'ip_dhcp_snooping': module.params['ip_dhcp_snooping'], - 'ip_arp_inspection': module.params['ip_arp_inspection'], - 'state': module.params['state'], - } - - stp = module.params.get('stp') - if stp: - stp_cmd = spanning_tree(module, stp) - params.update({'stp': stp_cmd}) - - obj.append(params) - - return obj - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params['purge'] - - for w in want: - vlan_id = w['vlan_id'] - state = w['state'] - name = w['name'] - interfaces = w.get('interfaces') - tagged = w.get('tagged') - dhcp = w.get('ip_dhcp_snooping') - arp = w.get('ip_arp_inspection') - stp = w.get('stp') - obj_in_have = search_obj_in_list(str(vlan_id), have) - - if state == 'absent': - if have == []: - commands.append('no vlan {0}'.format(vlan_id)) - if obj_in_have: - commands.append('no vlan {0}'.format(vlan_id)) - - elif state == 'present': - if not obj_in_have: - commands.append('vlan {0}'.format(vlan_id)) - if name: - commands.append('vlan {0} name {1}'.format(vlan_id, name)) - - if interfaces: - if interfaces['name']: - for item in interfaces['name']: - commands.append('untagged {0}'.format(item)) - - if tagged: - if tagged['name']: - for item in tagged['name']: - commands.append('tagged {0}'.format(item)) - - if dhcp is True: - commands.append('ip dhcp snooping vlan {0}'.format(vlan_id)) - elif dhcp is False: - commands.append('no ip dhcp snooping vlan {0}'.format(vlan_id)) - - if arp is True: - commands.append('ip arp inspection vlan {0}'.format(vlan_id)) - elif dhcp is False: - commands.append('no ip arp inspection vlan {0}'.format(vlan_id)) - - if stp: - if w.get('stp'): - [commands.append(cmd) for cmd in w['stp']] - - else: - commands.append('vlan {0}'.format(vlan_id)) - if name: - if name != obj_in_have['name']: - commands.append('vlan {0} name {1}'.format(vlan_id, name)) - - if interfaces: - if interfaces['name']: - have_interfaces = list() - for interface in interfaces['name']: - low, high = extract_list_from_interface(interface) - - while(high >= low): - if 'ethernet' in interface: - have_interfaces.append('ethernet 1/1/{0}'.format(low)) - if 'lag' in interface: - have_interfaces.append('lag {0}'.format(low)) - low = low + 1 - - if interfaces['purge'] is True: - remove_interfaces = list(set(obj_in_have['interfaces']) - set(have_interfaces)) - for item in remove_interfaces: - commands.append('no untagged {0}'.format(item)) - - if interfaces['name']: - add_interfaces = list(set(have_interfaces) - set(obj_in_have['interfaces'])) - for item in add_interfaces: - commands.append('untagged {0}'.format(item)) - - if tagged: - if tagged['name']: - have_tagged = list() - for tag in tagged['name']: - low, high = extract_list_from_interface(tag) - - while(high >= low): - if 'ethernet' in tag: - have_tagged.append('ethernet 1/1/{0}'.format(low)) - if 'lag' in tag: - have_tagged.append('lag {0}'.format(low)) - low = low + 1 - if tagged['purge'] is True: - remove_tagged = list(set(obj_in_have['tagged']) - set(have_tagged)) - for item in remove_tagged: - commands.append('no tagged {0}'.format(item)) - - if tagged['name']: - add_tagged = list(set(have_tagged) - set(obj_in_have['tagged'])) - for item in add_tagged: - commands.append('tagged {0}'.format(item)) - - if dhcp != obj_in_have['ip_dhcp_snooping']: - if dhcp is True: - commands.append('ip dhcp snooping vlan {0}'.format(vlan_id)) - elif dhcp is False: - commands.append('no ip dhcp snooping vlan {0}'.format(vlan_id)) - - if arp != obj_in_have['ip_arp_inspection']: - if arp is True: - commands.append('ip arp inspection vlan {0}'.format(vlan_id)) - elif arp is False: - commands.append('no ip arp inspection vlan {0}'.format(vlan_id)) - - if stp: - if w.get('stp'): - [commands.append(cmd) for cmd in w['stp']] - - if len(commands) == 1 and 'vlan ' + str(vlan_id) in commands: - commands = [] - - if purge: - commands = [] - vlans = parse_vlan_id(module) - for h in vlans: - obj_in_want = search_obj_in_list(h, want) - if not obj_in_want and h != '1': - commands.append('no vlan {0}'.format(h)) - - return commands - - -def parse_name_argument(module, item): - command = 'show vlan {0}'.format(item) - rc, out, err = exec_command(module, command) - match = re.search(r"Name (\S+),", out) - if match: - return match.group(1) - - -def parse_interfaces_argument(module, item, port_type): - untagged_ports, untagged_lags, tagged_ports, tagged_lags = parse_vlan_brief(module, item) - ports = list() - if port_type == "interfaces": - if untagged_ports: - for port in untagged_ports: - ports.append('ethernet 1/1/' + str(port)) - if untagged_lags: - for port in untagged_lags: - ports.append('lag ' + str(port)) - - elif port_type == "tagged": - if tagged_ports: - for port in tagged_ports: - ports.append('ethernet 1/1/' + str(port)) - if tagged_lags: - for port in tagged_lags: - ports.append('lag ' + str(port)) - - return ports - - -def parse_config_argument(config, arg): - match = re.search(arg, config, re.M) - if match: - return True - else: - return False - - -def map_config_to_obj(module): - config = get_config(module) - vlans = parse_vlan_id(module) - instance = list() - - for item in set(vlans): - obj = { - 'vlan_id': item, - 'name': parse_name_argument(module, item), - 'interfaces': parse_interfaces_argument(module, item, 'interfaces'), - 'tagged': parse_interfaces_argument(module, item, 'tagged'), - 'ip_dhcp_snooping': parse_config_argument(config, 'ip dhcp snooping vlan {0}'.format(item)), - 'ip_arp_inspection': parse_config_argument(config, 'ip arp inspection vlan {0}'.format(item)), - } - instance.append(obj) - return instance - - -def check_fail(module, output): - error = [ - re.compile(r"^error", re.I) - ] - for x in output: - for regex in error: - if regex.search(x): - module.fail_json(msg=x) - - -def check_declarative_intent_params(want, module, result): - def parse_ports(interfaces, ports, lags): - for interface in interfaces: - low, high = extract_list_from_interface(interface) - - while(high >= low): - if 'ethernet' in interface: - if not (low in ports): - module.fail_json(msg='One or more conditional statements have not been satisfied ' + interface) - if 'lag' in interface: - if not (low in lags): - module.fail_json(msg='One or more conditional statements have not been satisfied ' + interface) - low = low + 1 - - is_delay = False - low = 0 - high = 0 - for w in want: - if w.get('associated_interfaces') is None and w.get('associated_tagged') is None: - continue - - if result['changed'] and not is_delay: - sleep(module.params['delay']) - is_delay = True - - untagged_ports, untagged_lags, tagged_ports, tagged_lags = parse_vlan_brief(module, w['vlan_id']) - - if w['associated_interfaces']: - parse_ports(w.get('associated_interfaces'), untagged_ports, untagged_lags) - - if w['associated_tagged']: - parse_ports(w.get('associated_tagged'), tagged_ports, tagged_lags) - - -def main(): - """ main entry point for module execution - """ - stp_spec = dict( - type=dict(default='802-1w', choices=['802-1w', 'rstp']), - priority=dict(), - enabled=dict(type='bool'), - ) - inter_spec = dict( - name=dict(type='list'), - purge=dict(type='bool') - ) - tagged_spec = dict( - name=dict(type='list'), - purge=dict(type='bool') - ) - element_spec = dict( - vlan_id=dict(type='int'), - name=dict(), - interfaces=dict(type='dict', options=inter_spec), - tagged=dict(type='dict', options=tagged_spec), - ip_dhcp_snooping=dict(type='bool'), - ip_arp_inspection=dict(type='bool'), - associated_interfaces=dict(type='list'), - associated_tagged=dict(type='list'), - delay=dict(default=10, type='int'), - stp=dict(type='dict', options=stp_spec), - state=dict(default='present', choices=['present', 'absent']), - check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])) - ) - aggregate_spec = deepcopy(element_spec) - aggregate_spec['vlan_id'] = dict(required=True) - - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - purge=dict(default=False, type='bool') - ) - argument_spec.update(element_spec) - required_one_of = [['vlan_id', 'aggregate']] - mutually_exclusive = [['vlan_id', 'aggregate']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - warnings = list() - result = {} - result['changed'] = False - if warnings: - result['warnings'] = warnings - exec_command(module, 'skip') - want = map_params_to_obj(module) - if module.params['check_running_config'] is False: - have = [] - else: - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - output = load_config(module, commands) - if output: - check_fail(module, output) - result['output'] = output - result['changed'] = True - - check_declarative_intent_params(want, module, result) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/dladm_etherstub.py b/plugins/modules/network/illumos/dladm_etherstub.py deleted file mode 100644 index d9410c2336..0000000000 --- a/plugins/modules/network/illumos/dladm_etherstub.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2015, Adam Å tevko -# 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: dladm_etherstub -short_description: Manage etherstubs on Solaris/illumos systems. -description: - - Create or delete etherstubs on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - name: - description: - - Etherstub name. - required: true - temporary: - description: - - Specifies that the etherstub is temporary. Temporary etherstubs - do not persist across reboots. - required: false - default: false - type: bool - state: - description: - - Create or delete Solaris/illumos etherstub. - required: false - default: "present" - choices: [ "present", "absent" ] -''' - -EXAMPLES = ''' -# Create 'stub0' etherstub -- dladm_etherstub: - name: stub0 - state: present - -# Remove 'stub0 etherstub -- dladm_etherstub: - name: stub0 - state: absent -''' - -RETURN = ''' -name: - description: etherstub name - returned: always - type: str - sample: "switch0" -state: - description: state of the target - returned: always - type: str - sample: "present" -temporary: - description: etherstub's persistence - returned: always - type: bool - sample: "True" -''' -from ansible.module_utils.basic import AnsibleModule - - -class Etherstub(object): - - def __init__(self, module): - self.module = module - - self.name = module.params['name'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - def etherstub_exists(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('show-etherstub') - cmd.append(self.name) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - return False - - def create_etherstub(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('create-etherstub') - - if self.temporary: - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def delete_etherstub(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('delete-etherstub') - - if self.temporary: - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(required=True), - temporary=dict(default=False, type='bool'), - state=dict(default='present', choices=['absent', 'present']), - ), - supports_check_mode=True - ) - - etherstub = Etherstub(module) - - rc = None - out = '' - err = '' - result = {} - result['name'] = etherstub.name - result['state'] = etherstub.state - result['temporary'] = etherstub.temporary - - if etherstub.state == 'absent': - if etherstub.etherstub_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = etherstub.delete_etherstub() - if rc != 0: - module.fail_json(name=etherstub.name, msg=err, rc=rc) - elif etherstub.state == 'present': - if not etherstub.etherstub_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = etherstub.create_etherstub() - - if rc is not None and rc != 0: - module.fail_json(name=etherstub.name, msg=err, rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/dladm_iptun.py b/plugins/modules/network/illumos/dladm_iptun.py deleted file mode 100644 index 9e48be0d49..0000000000 --- a/plugins/modules/network/illumos/dladm_iptun.py +++ /dev/null @@ -1,277 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2016, Adam Å tevko -# 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: dladm_iptun -short_description: Manage IP tunnel interfaces on Solaris/illumos systems. -description: - - Manage IP tunnel interfaces on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - name: - description: - - IP tunnel interface name. - required: true - temporary: - description: - - Specifies that the IP tunnel interface is temporary. Temporary IP tunnel - interfaces do not persist across reboots. - required: false - default: false - type: bool - type: - description: - - Specifies the type of tunnel to be created. - required: false - default: "ipv4" - choices: [ "ipv4", "ipv6", "6to4" ] - aliases: ['tunnel_type'] - local_address: - description: - - Literal IP address or hostname corresponding to the tunnel source. - required: false - aliases: [ "local" ] - remote_address: - description: - - Literal IP address or hostname corresponding to the tunnel destination. - required: false - aliases: [ "remote" ] - state: - description: - - Create or delete Solaris/illumos VNIC. - required: false - default: "present" - choices: [ "present", "absent" ] -''' - -EXAMPLES = ''' -- name: Create IPv4 tunnel interface 'iptun0' - dladm_iptun: name=iptun0 local_address=192.0.2.23 remote_address=203.0.113.10 state=present - -- name: Change IPv4 tunnel remote address - dladm_iptun: name=iptun0 type=ipv4 local_address=192.0.2.23 remote_address=203.0.113.11 - -- name: Create IPv6 tunnel interface 'tun0' - dladm_iptun: name=tun0 type=ipv6 local_address=192.0.2.23 remote_address=203.0.113.42 - -- name: Remove 'iptun0' tunnel interface - dladm_iptun: name=iptun0 state=absent -''' - -RETURN = ''' -name: - description: tunnel interface name - returned: always - type: str - sample: iptun0 -state: - description: state of the target - returned: always - type: str - sample: present -temporary: - description: specifies if operation will persist across reboots - returned: always - type: bool - sample: True -local_address: - description: local IP address - returned: always - type: str - sample: 1.1.1.1/32 -remote_address: - description: remote IP address - returned: always - type: str - sample: 2.2.2.2/32 -type: - description: tunnel type - returned: always - type: str - sample: ipv4 -''' - -from ansible.module_utils.basic import AnsibleModule - - -SUPPORTED_TYPES = ['ipv4', 'ipv6', '6to4'] - - -class IPTun(object): - - def __init__(self, module): - self.module = module - - self.name = module.params['name'] - self.type = module.params['type'] - self.local_address = module.params['local_address'] - self.remote_address = module.params['remote_address'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - self.dladm_bin = self.module.get_bin_path('dladm', True) - - def iptun_exists(self): - cmd = [self.dladm_bin] - - cmd.append('show-iptun') - cmd.append(self.name) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - return False - - def create_iptun(self): - cmd = [self.dladm_bin] - - cmd.append('create-iptun') - - if self.temporary: - cmd.append('-t') - - cmd.append('-T') - cmd.append(self.type) - cmd.append('-a') - cmd.append('local=' + self.local_address + ',remote=' + self.remote_address) - cmd.append(self.name) - - return self.module.run_command(cmd) - - def delete_iptun(self): - cmd = [self.dladm_bin] - - cmd.append('delete-iptun') - - if self.temporary: - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def update_iptun(self): - cmd = [self.dladm_bin] - - cmd.append('modify-iptun') - - if self.temporary: - cmd.append('-t') - cmd.append('-a') - cmd.append('local=' + self.local_address + ',remote=' + self.remote_address) - cmd.append(self.name) - - return self.module.run_command(cmd) - - def _query_iptun_props(self): - cmd = [self.dladm_bin] - - cmd.append('show-iptun') - cmd.append('-p') - cmd.append('-c') - cmd.append('link,type,flags,local,remote') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def iptun_needs_updating(self): - (rc, out, err) = self._query_iptun_props() - - NEEDS_UPDATING = False - - if rc == 0: - configured_local, configured_remote = out.split(':')[3:] - - if self.local_address != configured_local or self.remote_address != configured_remote: - NEEDS_UPDATING = True - - return NEEDS_UPDATING - else: - self.module.fail_json(msg='Failed to query tunnel interface %s properties' % self.name, - err=err, - rc=rc) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(required=True, type='str'), - type=dict(default='ipv4', type='str', aliases=['tunnel_type'], - choices=SUPPORTED_TYPES), - local_address=dict(type='str', aliases=['local']), - remote_address=dict(type='str', aliases=['remote']), - temporary=dict(default=False, type='bool'), - state=dict(default='present', choices=['absent', 'present']), - ), - required_if=[ - ['state', 'present', ['local_address', 'remote_address']], - ], - supports_check_mode=True - ) - - iptun = IPTun(module) - - rc = None - out = '' - err = '' - result = {} - result['name'] = iptun.name - result['type'] = iptun.type - result['local_address'] = iptun.local_address - result['remote_address'] = iptun.remote_address - result['state'] = iptun.state - result['temporary'] = iptun.temporary - - if iptun.state == 'absent': - if iptun.iptun_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = iptun.delete_iptun() - if rc != 0: - module.fail_json(name=iptun.name, msg=err, rc=rc) - elif iptun.state == 'present': - if not iptun.iptun_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = iptun.create_iptun() - - if rc is not None and rc != 0: - module.fail_json(name=iptun.name, msg=err, rc=rc) - else: - if iptun.iptun_needs_updating(): - (rc, out, err) = iptun.update_iptun() - if rc != 0: - module.fail_json(msg='Error while updating tunnel interface: "%s"' % err, - name=iptun.name, - stderr=err, - rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/dladm_linkprop.py b/plugins/modules/network/illumos/dladm_linkprop.py deleted file mode 100644 index de8d699e4a..0000000000 --- a/plugins/modules/network/illumos/dladm_linkprop.py +++ /dev/null @@ -1,289 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2016, Adam Å tevko -# 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: dladm_linkprop -short_description: Manage link properties on Solaris/illumos systems. -description: - - Set / reset link properties on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - link: - description: - - Link interface name. - required: true - aliases: [ "nic", "interface" ] - property: - description: - - Specifies the name of the property we want to manage. - required: true - aliases: [ "name" ] - value: - description: - - Specifies the value we want to set for the link property. - required: false - temporary: - description: - - Specifies that lin property configuration is temporary. Temporary - link property configuration does not persist across reboots. - required: false - type: bool - default: false - state: - description: - - Set or reset the property value. - required: false - default: "present" - choices: [ "present", "absent", "reset" ] -''' - -EXAMPLES = ''' -- name: Set 'maxbw' to 100M on e1000g1 - dladm_linkprop: name=e1000g1 property=maxbw value=100M state=present - -- name: Set 'mtu' to 9000 on e1000g1 - dladm_linkprop: name=e1000g1 property=mtu value=9000 - -- name: Reset 'mtu' property on e1000g1 - dladm_linkprop: name=e1000g1 property=mtu state=reset -''' - -RETURN = ''' -property: - description: property name - returned: always - type: str - sample: mtu -state: - description: state of the target - returned: always - type: str - sample: present -temporary: - description: specifies if operation will persist across reboots - returned: always - type: bool - sample: True -link: - description: link name - returned: always - type: str - sample: e100g0 -value: - description: property value - returned: always - type: str - sample: 9000 -''' - -from ansible.module_utils.basic import AnsibleModule - - -class LinkProp(object): - - def __init__(self, module): - self.module = module - - self.link = module.params['link'] - self.property = module.params['property'] - self.value = module.params['value'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - self.dladm_bin = self.module.get_bin_path('dladm', True) - - def property_exists(self): - cmd = [self.dladm_bin] - - cmd.append('show-linkprop') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.link) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - self.module.fail_json(msg='Unknown property "%s" on link %s' % - (self.property, self.link), - property=self.property, - link=self.link) - - def property_is_modified(self): - cmd = [self.dladm_bin] - - cmd.append('show-linkprop') - cmd.append('-c') - cmd.append('-o') - cmd.append('value,default') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.link) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - (value, default) = out.split(':') - - if rc == 0 and value == default: - return True - else: - return False - - def property_is_readonly(self): - cmd = [self.dladm_bin] - - cmd.append('show-linkprop') - cmd.append('-c') - cmd.append('-o') - cmd.append('perm') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.link) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - - if rc == 0 and out == 'r-': - return True - else: - return False - - def property_is_set(self): - cmd = [self.dladm_bin] - - cmd.append('show-linkprop') - cmd.append('-c') - cmd.append('-o') - cmd.append('value') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.link) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - - if rc == 0 and self.value == out: - return True - else: - return False - - def set_property(self): - cmd = [self.dladm_bin] - - cmd.append('set-linkprop') - - if self.temporary: - cmd.append('-t') - - cmd.append('-p') - cmd.append(self.property + '=' + self.value) - cmd.append(self.link) - - return self.module.run_command(cmd) - - def reset_property(self): - cmd = [self.dladm_bin] - - cmd.append('reset-linkprop') - - if self.temporary: - cmd.append('-t') - - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.link) - - return self.module.run_command(cmd) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - link=dict(required=True, default=None, type='str', aliases=['nic', 'interface']), - property=dict(required=True, type='str', aliases=['name']), - value=dict(required=False, type='str'), - temporary=dict(default=False, type='bool'), - state=dict( - default='present', choices=['absent', 'present', 'reset']), - ), - required_if=[ - ['state', 'present', ['value']], - ], - - supports_check_mode=True - ) - - linkprop = LinkProp(module) - - rc = None - out = '' - err = '' - result = {} - result['property'] = linkprop.property - result['link'] = linkprop.link - result['state'] = linkprop.state - if linkprop.value: - result['value'] = linkprop.value - - if linkprop.state == 'absent' or linkprop.state == 'reset': - if linkprop.property_exists(): - if not linkprop.property_is_modified(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = linkprop.reset_property() - if rc != 0: - module.fail_json(property=linkprop.property, - link=linkprop.link, - msg=err, - rc=rc) - - elif linkprop.state == 'present': - if linkprop.property_exists(): - if not linkprop.property_is_readonly(): - if not linkprop.property_is_set(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = linkprop.set_property() - if rc != 0: - module.fail_json(property=linkprop.property, - link=linkprop.link, - msg=err, - rc=rc) - else: - module.fail_json(msg='Property "%s" is read-only!' % (linkprop.property), - property=linkprop.property, - link=linkprop.link) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/dladm_vlan.py b/plugins/modules/network/illumos/dladm_vlan.py deleted file mode 100644 index a651abd1fd..0000000000 --- a/plugins/modules/network/illumos/dladm_vlan.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2016, Adam Å tevko -# 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: dladm_vlan -short_description: Manage VLAN interfaces on Solaris/illumos systems. -description: - - Create or delete VLAN interfaces on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - name: - description: - - VLAN interface name. - required: true - link: - description: - - VLAN underlying link name. - required: true - temporary: - description: - - Specifies that the VLAN interface is temporary. Temporary VLANs - do not persist across reboots. - required: false - default: false - type: bool - vlan_id: - description: - - VLAN ID value for VLAN interface. - required: false - default: false - aliases: [ "vid" ] - state: - description: - - Create or delete Solaris/illumos VNIC. - required: false - default: "present" - choices: [ "present", "absent" ] -''' - -EXAMPLES = ''' -- name: Create 'vlan42' VLAN over 'bnx0' link - dladm_vlan: name=vlan42 link=bnx0 vlan_id=42 state=present - -- name: Remove 'vlan1337' VLAN interface - dladm_vlan: name=vlan1337 state=absent -''' - -RETURN = ''' -name: - description: VLAN name - returned: always - type: str - sample: vlan42 -state: - description: state of the target - returned: always - type: str - sample: present -temporary: - description: specifies if operation will persist across reboots - returned: always - type: bool - sample: True -link: - description: VLAN's underlying link name - returned: always - type: str - sample: e100g0 -vlan_id: - description: VLAN ID - returned: always - type: str - sample: 42 -''' - -from ansible.module_utils.basic import AnsibleModule - - -class VLAN(object): - - def __init__(self, module): - self.module = module - - self.name = module.params['name'] - self.link = module.params['link'] - self.vlan_id = module.params['vlan_id'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - def vlan_exists(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('show-vlan') - cmd.append(self.name) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - return False - - def create_vlan(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('create-vlan') - - if self.temporary: - cmd.append('-t') - - cmd.append('-l') - cmd.append(self.link) - cmd.append('-v') - cmd.append(self.vlan_id) - cmd.append(self.name) - - return self.module.run_command(cmd) - - def delete_vlan(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('delete-vlan') - - if self.temporary: - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def is_valid_vlan_id(self): - - return 0 <= int(self.vlan_id) <= 4095 - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(required=True, type='str'), - link=dict(default=None, type='str'), - vlan_id=dict(default=0, aliases=['vid']), - temporary=dict(default=False, type='bool'), - state=dict(default='present', choices=['absent', 'present']), - ), - required_if=[ - ['state', 'present', ['vlan_id', 'link', 'name']], - ], - supports_check_mode=True - ) - - vlan = VLAN(module) - - rc = None - out = '' - err = '' - result = {} - result['name'] = vlan.name - result['link'] = vlan.link - result['state'] = vlan.state - result['temporary'] = vlan.temporary - - if int(vlan.vlan_id) != 0: - if not vlan.is_valid_vlan_id(): - module.fail_json(msg='Invalid VLAN id value', - name=vlan.name, - state=vlan.state, - link=vlan.link, - vlan_id=vlan.vlan_id) - result['vlan_id'] = vlan.vlan_id - - if vlan.state == 'absent': - if vlan.vlan_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = vlan.delete_vlan() - if rc != 0: - module.fail_json(name=vlan.name, msg=err, rc=rc) - elif vlan.state == 'present': - if not vlan.vlan_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = vlan.create_vlan() - - if rc is not None and rc != 0: - module.fail_json(name=vlan.name, msg=err, rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/dladm_vnic.py b/plugins/modules/network/illumos/dladm_vnic.py deleted file mode 100644 index cd7b86a6aa..0000000000 --- a/plugins/modules/network/illumos/dladm_vnic.py +++ /dev/null @@ -1,265 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2015, Adam Å tevko -# -# 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: dladm_vnic -short_description: Manage VNICs on Solaris/illumos systems. -description: - - Create or delete VNICs on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - name: - description: - - VNIC name. - required: true - link: - description: - - VNIC underlying link name. - required: true - temporary: - description: - - Specifies that the VNIC is temporary. Temporary VNICs - do not persist across reboots. - required: false - default: false - type: bool - mac: - description: - - Sets the VNIC's MAC address. Must be valid unicast MAC address. - required: false - default: false - aliases: [ "macaddr" ] - vlan: - description: - - Enable VLAN tagging for this VNIC. The VLAN tag will have id - I(vlan). - required: false - default: false - aliases: [ "vlan_id" ] - state: - description: - - Create or delete Solaris/illumos VNIC. - required: false - default: "present" - choices: [ "present", "absent" ] -''' - -EXAMPLES = ''' -# Create 'vnic0' VNIC over 'bnx0' link -- dladm_vnic: - name: vnic0 - link: bnx0 - state: present - -# Create VNIC with specified MAC and VLAN tag over 'aggr0' -- dladm_vnic: - name: vnic1 - link: aggr0 - mac: '00:00:5E:00:53:23' - vlan: 4 - -# Remove 'vnic0' VNIC -- dladm_vnic: - name: vnic0 - link: bnx0 - state: absent -''' - -RETURN = ''' -name: - description: VNIC name - returned: always - type: str - sample: "vnic0" -link: - description: VNIC underlying link name - returned: always - type: str - sample: "igb0" -state: - description: state of the target - returned: always - type: str - sample: "present" -temporary: - description: VNIC's persistence - returned: always - type: bool - sample: "True" -mac: - description: MAC address to use for VNIC - returned: if mac is specified - type: str - sample: "00:00:5E:00:53:42" -vlan: - description: VLAN to use for VNIC - returned: success - type: int - sample: 42 -''' - -import re - -from ansible.module_utils.basic import AnsibleModule - - -class VNIC(object): - - UNICAST_MAC_REGEX = r'^[a-f0-9][2-9a-f0]:([a-f0-9]{2}:){4}[a-f0-9]{2}$' - - def __init__(self, module): - self.module = module - - self.name = module.params['name'] - self.link = module.params['link'] - self.mac = module.params['mac'] - self.vlan = module.params['vlan'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - def vnic_exists(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('show-vnic') - cmd.append(self.name) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - return False - - def create_vnic(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('create-vnic') - - if self.temporary: - cmd.append('-t') - - if self.mac: - cmd.append('-m') - cmd.append(self.mac) - - if self.vlan: - cmd.append('-v') - cmd.append(self.vlan) - - cmd.append('-l') - cmd.append(self.link) - cmd.append(self.name) - - return self.module.run_command(cmd) - - def delete_vnic(self): - cmd = [self.module.get_bin_path('dladm', True)] - - cmd.append('delete-vnic') - - if self.temporary: - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def is_valid_unicast_mac(self): - - mac_re = re.match(self.UNICAST_MAC_REGEX, self.mac) - - return mac_re is None - - def is_valid_vlan_id(self): - - return 0 <= self.vlan <= 4095 - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(required=True), - link=dict(required=True), - mac=dict(default=None, aliases=['macaddr']), - vlan=dict(default=None, aliases=['vlan_id']), - temporary=dict(default=False, type='bool'), - state=dict(default='present', choices=['absent', 'present']), - ), - supports_check_mode=True - ) - - vnic = VNIC(module) - - rc = None - out = '' - err = '' - result = {} - result['name'] = vnic.name - result['link'] = vnic.link - result['state'] = vnic.state - result['temporary'] = vnic.temporary - - if vnic.mac is not None: - if vnic.is_valid_unicast_mac(): - module.fail_json(msg='Invalid unicast MAC address', - mac=vnic.mac, - name=vnic.name, - state=vnic.state, - link=vnic.link, - vlan=vnic.vlan) - result['mac'] = vnic.mac - - if vnic.vlan is not None: - if vnic.is_valid_vlan_id(): - module.fail_json(msg='Invalid VLAN tag', - mac=vnic.mac, - name=vnic.name, - state=vnic.state, - link=vnic.link, - vlan=vnic.vlan) - result['vlan'] = vnic.vlan - - if vnic.state == 'absent': - if vnic.vnic_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = vnic.delete_vnic() - if rc != 0: - module.fail_json(name=vnic.name, msg=err, rc=rc) - elif vnic.state == 'present': - if not vnic.vnic_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = vnic.create_vnic() - - if rc is not None and rc != 0: - module.fail_json(name=vnic.name, msg=err, rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/flowadm.py b/plugins/modules/network/illumos/flowadm.py deleted file mode 100644 index b35a65fd45..0000000000 --- a/plugins/modules/network/illumos/flowadm.py +++ /dev/null @@ -1,513 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2016, Adam Å tevko -# 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: flowadm -short_description: Manage bandwidth resource control and priority for protocols, services and zones on Solaris/illumos systems -description: - - Create/modify/remove networking bandwidth and associated resources for a type of traffic on a particular link. -author: Adam Å tevko (@xen0l) -options: - name: - description: > - - A flow is defined as a set of attributes based on Layer 3 and Layer 4 - headers, which can be used to identify a protocol, service, or a zone. - required: true - aliases: [ 'flow' ] - link: - description: - - Specifiies a link to configure flow on. - required: false - local_ip: - description: - - Identifies a network flow by the local IP address. - required: false - remote_ip: - description: - - Identifies a network flow by the remote IP address. - required: false - transport: - description: > - - Specifies a Layer 4 protocol to be used. It is typically used in combination with I(local_port) to - identify the service that needs special attention. - required: false - local_port: - description: - - Identifies a service specified by the local port. - required: false - dsfield: - description: > - - Identifies the 8-bit differentiated services field (as defined in - RFC 2474). The optional dsfield_mask is used to state the bits of interest in - the differentiated services field when comparing with the dsfield - value. Both values must be in hexadecimal. - required: false - maxbw: - description: > - - Sets the full duplex bandwidth for the flow. The bandwidth is - specified as an integer with one of the scale suffixes(K, M, or G - for Kbps, Mbps, and Gbps). If no units are specified, the input - value will be read as Mbps. - required: false - priority: - description: - - Sets the relative priority for the flow. - required: false - default: 'medium' - choices: [ 'low', 'medium', 'high' ] - temporary: - description: - - Specifies that the configured flow is temporary. Temporary - flows do not persist across reboots. - required: false - default: false - type: bool - state: - description: - - Create/delete/enable/disable an IP address on the network interface. - required: false - default: present - choices: [ 'absent', 'present', 'resetted' ] -''' - -EXAMPLES = ''' -# Limit SSH traffic to 100M via vnic0 interface -- flowadm: - link: vnic0 - flow: ssh_out - transport: tcp - local_port: 22 - maxbw: 100M - state: present - -# Reset flow properties -- flowadm: - name: dns - state: resetted - -# Configure policy for EF PHB (DSCP value of 101110 from RFC 2598) with a bandwidth of 500 Mbps and a high priority. -- flowadm: - link: bge0 - dsfield: '0x2e:0xfc' - maxbw: 500M - priority: high - flow: efphb-flow - state: present -''' - -RETURN = ''' -name: - description: flow name - returned: always - type: str - sample: "http_drop" -link: - description: flow's link - returned: if link is defined - type: str - sample: "vnic0" -state: - description: state of the target - returned: always - type: str - sample: "present" -temporary: - description: flow's persistence - returned: always - type: bool - sample: "True" -priority: - description: flow's priority - returned: if priority is defined - type: str - sample: "low" -transport: - description: flow's transport - returned: if transport is defined - type: str - sample: "tcp" -maxbw: - description: flow's maximum bandwidth - returned: if maxbw is defined - type: str - sample: "100M" -local_Ip: - description: flow's local IP address - returned: if local_ip is defined - type: str - sample: "10.0.0.42" -local_port: - description: flow's local port - returned: if local_port is defined - type: int - sample: 1337 -remote_Ip: - description: flow's remote IP address - returned: if remote_ip is defined - type: str - sample: "10.0.0.42" -dsfield: - description: flow's differentiated services value - returned: if dsfield is defined - type: str - sample: "0x2e:0xfc" -''' - - -import socket - -from ansible.module_utils.basic import AnsibleModule - - -SUPPORTED_TRANSPORTS = ['tcp', 'udp', 'sctp', 'icmp', 'icmpv6'] -SUPPORTED_PRIORITIES = ['low', 'medium', 'high'] - -SUPPORTED_ATTRIBUTES = ['local_ip', 'remote_ip', 'transport', 'local_port', 'dsfield'] -SUPPORTPED_PROPERTIES = ['maxbw', 'priority'] - - -class Flow(object): - - def __init__(self, module): - self.module = module - - self.name = module.params['name'] - self.link = module.params['link'] - self.local_ip = module.params['local_ip'] - self.remote_ip = module.params['remote_ip'] - self.transport = module.params['transport'] - self.local_port = module.params['local_port'] - self.dsfield = module.params['dsfield'] - self.maxbw = module.params['maxbw'] - self.priority = module.params['priority'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - self._needs_updating = { - 'maxbw': False, - 'priority': False, - } - - @classmethod - def is_valid_port(cls, port): - return 1 <= int(port) <= 65535 - - @classmethod - def is_valid_address(cls, ip): - - if ip.count('/') == 1: - ip_address, netmask = ip.split('/') - else: - ip_address = ip - - if len(ip_address.split('.')) == 4: - try: - socket.inet_pton(socket.AF_INET, ip_address) - except socket.error: - return False - - if not 0 <= netmask <= 32: - return False - else: - try: - socket.inet_pton(socket.AF_INET6, ip_address) - except socket.error: - return False - - if not 0 <= netmask <= 128: - return False - - return True - - @classmethod - def is_hex(cls, number): - try: - int(number, 16) - except ValueError: - return False - - return True - - @classmethod - def is_valid_dsfield(cls, dsfield): - - dsmask = None - - if dsfield.count(':') == 1: - dsval = dsfield.split(':')[0] - else: - dsval, dsmask = dsfield.split(':') - - if dsmask and not 0x01 <= int(dsmask, 16) <= 0xff and not 0x01 <= int(dsval, 16) <= 0xff: - return False - elif not 0x01 <= int(dsval, 16) <= 0xff: - return False - - return True - - def flow_exists(self): - cmd = [self.module.get_bin_path('flowadm')] - - cmd.append('show-flow') - cmd.append(self.name) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - return False - - def delete_flow(self): - cmd = [self.module.get_bin_path('flowadm')] - - cmd.append('remove-flow') - if self.temporary: - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def create_flow(self): - cmd = [self.module.get_bin_path('flowadm')] - - cmd.append('add-flow') - cmd.append('-l') - cmd.append(self.link) - - if self.local_ip: - cmd.append('-a') - cmd.append('local_ip=' + self.local_ip) - - if self.remote_ip: - cmd.append('-a') - cmd.append('remote_ip=' + self.remote_ip) - - if self.transport: - cmd.append('-a') - cmd.append('transport=' + self.transport) - - if self.local_port: - cmd.append('-a') - cmd.append('local_port=' + self.local_port) - - if self.dsfield: - cmd.append('-a') - cmd.append('dsfield=' + self.dsfield) - - if self.maxbw: - cmd.append('-p') - cmd.append('maxbw=' + self.maxbw) - - if self.priority: - cmd.append('-p') - cmd.append('priority=' + self.priority) - - if self.temporary: - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def _query_flow_props(self): - cmd = [self.module.get_bin_path('flowadm')] - - cmd.append('show-flowprop') - cmd.append('-c') - cmd.append('-o') - cmd.append('property,possible') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def flow_needs_udpating(self): - (rc, out, err) = self._query_flow_props() - - NEEDS_UPDATING = False - - if rc == 0: - properties = (line.split(':') for line in out.rstrip().split('\n')) - for prop, value in properties: - if prop == 'maxbw' and self.maxbw != value: - self._needs_updating.update({prop: True}) - NEEDS_UPDATING = True - - elif prop == 'priority' and self.priority != value: - self._needs_updating.update({prop: True}) - NEEDS_UPDATING = True - - return NEEDS_UPDATING - else: - self.module.fail_json(msg='Error while checking flow properties: %s' % err, - stderr=err, - rc=rc) - - def update_flow(self): - cmd = [self.module.get_bin_path('flowadm')] - - cmd.append('set-flowprop') - - if self.maxbw and self._needs_updating['maxbw']: - cmd.append('-p') - cmd.append('maxbw=' + self.maxbw) - - if self.priority and self._needs_updating['priority']: - cmd.append('-p') - cmd.append('priority=' + self.priority) - - if self.temporary: - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(required=True, aliases=['flow']), - link=dict(required=False), - local_ip=dict(required=False), - remote_ip=dict(required=False), - transport=dict(required=False, choices=SUPPORTED_TRANSPORTS), - local_port=dict(required=False), - dsfield=dict(required=False), - maxbw=dict(required=False), - priority=dict(required=False, - default='medium', - choices=SUPPORTED_PRIORITIES), - temporary=dict(default=False, type='bool'), - state=dict(required=False, - default='present', - choices=['absent', 'present', 'resetted']), - ), - mutually_exclusive=[ - ('local_ip', 'remote_ip'), - ('local_ip', 'transport'), - ('local_ip', 'local_port'), - ('local_ip', 'dsfield'), - ('remote_ip', 'transport'), - ('remote_ip', 'local_port'), - ('remote_ip', 'dsfield'), - ('transport', 'dsfield'), - ('local_port', 'dsfield'), - ], - supports_check_mode=True - ) - - flow = Flow(module) - - rc = None - out = '' - err = '' - result = {} - result['name'] = flow.name - result['state'] = flow.state - result['temporary'] = flow.temporary - - if flow.link: - result['link'] = flow.link - - if flow.maxbw: - result['maxbw'] = flow.maxbw - - if flow.priority: - result['priority'] = flow.priority - - if flow.local_ip: - if flow.is_valid_address(flow.local_ip): - result['local_ip'] = flow.local_ip - - if flow.remote_ip: - if flow.is_valid_address(flow.remote_ip): - result['remote_ip'] = flow.remote_ip - - if flow.transport: - result['transport'] = flow.transport - - if flow.local_port: - if flow.is_valid_port(flow.local_port): - result['local_port'] = flow.local_port - else: - module.fail_json(msg='Invalid port: %s' % flow.local_port, - rc=1) - - if flow.dsfield: - if flow.is_valid_dsfield(flow.dsfield): - result['dsfield'] = flow.dsfield - else: - module.fail_json(msg='Invalid dsfield: %s' % flow.dsfield, - rc=1) - - if flow.state == 'absent': - if flow.flow_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = flow.delete_flow() - if rc != 0: - module.fail_json(msg='Error while deleting flow: "%s"' % err, - name=flow.name, - stderr=err, - rc=rc) - - elif flow.state == 'present': - if not flow.flow_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = flow.create_flow() - if rc != 0: - module.fail_json(msg='Error while creating flow: "%s"' % err, - name=flow.name, - stderr=err, - rc=rc) - else: - if flow.flow_needs_udpating(): - (rc, out, err) = flow.update_flow() - if rc != 0: - module.fail_json(msg='Error while updating flow: "%s"' % err, - name=flow.name, - stderr=err, - rc=rc) - - elif flow.state == 'resetted': - if flow.flow_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = flow.reset_flow() - if rc != 0: - module.fail_json(msg='Error while resetting flow: "%s"' % err, - name=flow.name, - stderr=err, - rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/ipadm_addr.py b/plugins/modules/network/illumos/ipadm_addr.py deleted file mode 100644 index d898d6c4bd..0000000000 --- a/plugins/modules/network/illumos/ipadm_addr.py +++ /dev/null @@ -1,403 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2016, Adam Å tevko -# 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: ipadm_addr -short_description: Manage IP addresses on an interface on Solaris/illumos systems -description: - - Create/delete static/dynamic IP addresses on network interfaces on Solaris/illumos systems. - - Up/down static/dynamic IP addresses on network interfaces on Solaris/illumos systems. - - Manage IPv6 link-local addresses on network interfaces on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - address: - description: - - Specifiies an IP address to configure in CIDR notation. - required: false - aliases: [ "addr" ] - addrtype: - description: - - Specifiies a type of IP address to configure. - required: false - default: static - choices: [ 'static', 'dhcp', 'addrconf' ] - addrobj: - description: - - Specifies an unique IP address on the system. - required: true - temporary: - description: - - Specifies that the configured IP address is temporary. Temporary - IP addresses do not persist across reboots. - required: false - default: false - type: bool - wait: - description: - - Specifies the time in seconds we wait for obtaining address via DHCP. - required: false - default: 60 - state: - description: - - Create/delete/enable/disable an IP address on the network interface. - required: false - default: present - choices: [ 'absent', 'present', 'up', 'down', 'enabled', 'disabled', 'refreshed' ] -''' - -EXAMPLES = ''' -- name: Configure IP address 10.0.0.1 on e1000g0 - ipadm_addr: addr=10.0.0.1/32 addrobj=e1000g0/v4 state=present - -- name: Delete addrobj - ipadm_addr: addrobj=e1000g0/v4 state=absent - -- name: Configure link-local IPv6 address - ipadm_addr: addtype=addrconf addrobj=vnic0/v6 - -- name: Configure address via DHCP and wait 180 seconds for address obtaining - ipadm_addr: addrobj=vnic0/dhcp addrtype=dhcp wait=180 -''' - -RETURN = ''' -addrobj: - description: address object name - returned: always - type: str - sample: bge0/v4 -state: - description: state of the target - returned: always - type: str - sample: present -temporary: - description: specifies if operation will persist across reboots - returned: always - type: bool - sample: True -addrtype: - description: address type - returned: always - type: str - sample: static -address: - description: IP address - returned: only if addrtype is 'static' - type: str - sample: 1.3.3.7/32 -wait: - description: time we wait for DHCP - returned: only if addrtype is 'dhcp' - type: str - sample: 10 -''' - -import socket - -from ansible.module_utils.basic import AnsibleModule - - -SUPPORTED_TYPES = ['static', 'addrconf', 'dhcp'] - - -class Addr(object): - - def __init__(self, module): - self.module = module - - self.address = module.params['address'] - self.addrtype = module.params['addrtype'] - self.addrobj = module.params['addrobj'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - self.wait = module.params['wait'] - - def is_cidr_notation(self): - - return self.address.count('/') == 1 - - def is_valid_address(self): - - ip_address = self.address.split('/')[0] - - try: - if len(ip_address.split('.')) == 4: - socket.inet_pton(socket.AF_INET, ip_address) - else: - socket.inet_pton(socket.AF_INET6, ip_address) - except socket.error: - return False - - return True - - def is_dhcp(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-addr') - cmd.append('-p') - cmd.append('-o') - cmd.append('type') - cmd.append(self.addrobj) - - (rc, out, err) = self.module.run_command(cmd) - - if rc == 0: - if out.rstrip() != 'dhcp': - return False - - return True - else: - self.module.fail_json(msg='Wrong addrtype %s for addrobj "%s": %s' % (out, self.addrobj, err), - rc=rc, - stderr=err) - - def addrobj_exists(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-addr') - cmd.append(self.addrobj) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - return False - - def delete_addr(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('delete-addr') - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - def create_addr(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('create-addr') - cmd.append('-T') - cmd.append(self.addrtype) - - if self.temporary: - cmd.append('-t') - - if self.addrtype == 'static': - cmd.append('-a') - cmd.append(self.address) - - if self.addrtype == 'dhcp' and self.wait: - cmd.append('-w') - cmd.append(self.wait) - - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - def up_addr(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('up-addr') - - if self.temporary: - cmd.append('-t') - - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - def down_addr(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('down-addr') - - if self.temporary: - cmd.append('-t') - - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - def enable_addr(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('enable-addr') - cmd.append('-t') - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - def disable_addr(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('disable-addr') - cmd.append('-t') - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - def refresh_addr(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('refresh-addr') - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - address=dict(aliases=['addr']), - addrtype=dict(default='static', choices=SUPPORTED_TYPES), - addrobj=dict(required=True), - temporary=dict(default=False, type='bool'), - state=dict( - default='present', choices=['absent', 'present', 'up', 'down', 'enabled', 'disabled', 'refreshed']), - wait=dict(default=60, type='int'), - ), - mutually_exclusive=[ - ('address', 'wait'), - ], - supports_check_mode=True - ) - - addr = Addr(module) - - rc = None - out = '' - err = '' - result = {} - result['addrobj'] = addr.addrobj - result['state'] = addr.state - result['temporary'] = addr.temporary - result['addrtype'] = addr.addrtype - - if addr.addrtype == 'static' and addr.address: - if addr.is_cidr_notation() and addr.is_valid_address(): - result['address'] = addr.address - else: - module.fail_json(msg='Invalid IP address: %s' % addr.address) - - if addr.addrtype == 'dhcp' and addr.wait: - result['wait'] = addr.wait - - if addr.state == 'absent': - if addr.addrobj_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = addr.delete_addr() - if rc != 0: - module.fail_json(msg='Error while deleting addrobj: "%s"' % err, - addrobj=addr.addrobj, - stderr=err, - rc=rc) - - elif addr.state == 'present': - if not addr.addrobj_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = addr.create_addr() - if rc != 0: - module.fail_json(msg='Error while configuring IP address: "%s"' % err, - addrobj=addr.addrobj, - addr=addr.address, - stderr=err, - rc=rc) - - elif addr.state == 'up': - if addr.addrobj_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = addr.up_addr() - if rc != 0: - module.fail_json(msg='Error while bringing IP address up: "%s"' % err, - addrobj=addr.addrobj, - stderr=err, - rc=rc) - - elif addr.state == 'down': - if addr.addrobj_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = addr.down_addr() - if rc != 0: - module.fail_json(msg='Error while bringing IP address down: "%s"' % err, - addrobj=addr.addrobj, - stderr=err, - rc=rc) - - elif addr.state == 'refreshed': - if addr.addrobj_exists(): - if addr.is_dhcp(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = addr.refresh_addr() - if rc != 0: - module.fail_json(msg='Error while refreshing IP address: "%s"' % err, - addrobj=addr.addrobj, - stderr=err, - rc=rc) - else: - module.fail_json(msg='state "refreshed" cannot be used with "%s" addrtype' % addr.addrtype, - addrobj=addr.addrobj, - stderr=err, - rc=1) - - elif addr.state == 'enabled': - if addr.addrobj_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = addr.enable_addr() - if rc != 0: - module.fail_json(msg='Error while enabling IP address: "%s"' % err, - addrobj=addr.addrobj, - stderr=err, - rc=rc) - - elif addr.state == 'disabled': - if addr.addrobj_exists(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = addr.disable_addr() - if rc != 0: - module.fail_json(msg='Error while disabling IP address: "%s"' % err, - addrobj=addr.addrobj, - stderr=err, - rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/ipadm_addrprop.py b/plugins/modules/network/illumos/ipadm_addrprop.py deleted file mode 100644 index 574c6b3ee4..0000000000 --- a/plugins/modules/network/illumos/ipadm_addrprop.py +++ /dev/null @@ -1,259 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2016, Adam Å tevko -# 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: ipadm_addrprop -short_description: Manage IP address properties on Solaris/illumos systems. -description: - - Modify IP address properties on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - addrobj: - description: - - Specifies the address object we want to manage. - required: true - aliases: [nic, interface] - property: - description: - - Specifies the name of the address property we want to manage. - required: true - aliases: [name] - value: - description: - - Specifies the value we want to set for the address property. - required: false - temporary: - description: - - Specifies that the address property value is temporary. - Temporary values do not persist across reboots. - required: false - default: false - type: bool - state: - description: - - Set or reset the property value. - required: false - default: present - choices: [ "present", "absent", "reset" ] -''' - -EXAMPLES = ''' -- name: Mark address on addrobj as deprecated - ipadm_addrprop: property=deprecated value=on addrobj=e1000g0/v6 - -- name: Set network prefix length for addrobj - ipadm_addrprop: addrobj=bge0/v4 name=prefixlen value=26 -''' - -RETURN = ''' -property: - description: property name - returned: always - type: str - sample: deprecated -addrobj: - description: address object name - returned: always - type: str - sample: bge0/v4 -state: - description: state of the target - returned: always - type: str - sample: present -temporary: - description: specifies if operation will persist across reboots - returned: always - type: bool - sample: True -value: - description: property value - returned: when value is provided - type: str - sample: 26 -''' - -from ansible.module_utils.basic import AnsibleModule - - -class AddrProp(object): - - def __init__(self, module): - self.module = module - - self.addrobj = module.params['addrobj'] - self.property = module.params['property'] - self.value = module.params['value'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - def property_exists(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-addrprop') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.addrobj) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - self.module.fail_json(msg='Unknown property "%s" on addrobj %s' % - (self.property, self.addrobj), - property=self.property, - addrobj=self.addrobj) - - def property_is_modified(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-addrprop') - cmd.append('-c') - cmd.append('-o') - cmd.append('current,default') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.addrobj) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - (value, default) = out.split(':') - - if rc == 0 and value == default: - return True - else: - return False - - def property_is_set(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-addrprop') - cmd.append('-c') - cmd.append('-o') - cmd.append('current') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.addrobj) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - - if rc == 0 and self.value == out: - return True - else: - return False - - def set_property(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('set-addrprop') - - if self.temporary: - cmd.append('-t') - - cmd.append('-p') - cmd.append(self.property + '=' + self.value) - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - def reset_property(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('reset-addrprop') - - if self.temporary: - cmd.append('-t') - - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.addrobj) - - return self.module.run_command(cmd) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - addrobj=dict(required=True, default=None, aliases=['nic', 'interface']), - property=dict(required=True, aliases=['name']), - value=dict(required=False), - temporary=dict(default=False, type='bool'), - state=dict( - default='present', choices=['absent', 'present', 'reset']), - ), - supports_check_mode=True - ) - - addrprop = AddrProp(module) - - rc = None - out = '' - err = '' - result = {} - result['property'] = addrprop.property - result['addrobj'] = addrprop.addrobj - result['state'] = addrprop.state - result['temporary'] = addrprop.temporary - if addrprop.value: - result['value'] = addrprop.value - - if addrprop.state == 'absent' or addrprop.state == 'reset': - if addrprop.property_exists(): - if not addrprop.property_is_modified(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = addrprop.reset_property() - if rc != 0: - module.fail_json(property=addrprop.property, - addrobj=addrprop.addrobj, - msg=err, - rc=rc) - - elif addrprop.state == 'present': - if addrprop.value is None: - module.fail_json(msg='Value is mandatory with state "present"') - - if addrprop.property_exists(): - if not addrprop.property_is_set(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = addrprop.set_property() - if rc != 0: - module.fail_json(property=addrprop.property, - addrobj=addrprop.addrobj, - msg=err, - rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/ipadm_if.py b/plugins/modules/network/illumos/ipadm_if.py deleted file mode 100644 index 89da9a8553..0000000000 --- a/plugins/modules/network/illumos/ipadm_if.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2015, Adam Å tevko -# 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: ipadm_if -short_description: Manage IP interfaces on Solaris/illumos systems. -description: - - Create, delete, enable or disable IP interfaces on Solaris/illumos - systems. -author: Adam Å tevko (@xen0l) -options: - name: - description: - - IP interface name. - required: true - temporary: - description: - - Specifies that the IP interface is temporary. Temporary IP - interfaces do not persist across reboots. - required: false - default: false - type: bool - state: - description: - - Create or delete Solaris/illumos IP interfaces. - required: false - default: "present" - choices: [ "present", "absent", "enabled", "disabled" ] -''' - -EXAMPLES = ''' -# Create vnic0 interface -- ipadm_if: - name: vnic0 - state: enabled - -# Disable vnic0 interface -- ipadm_if: - name: vnic0 - state: disabled -''' - -RETURN = ''' -name: - description: IP interface name - returned: always - type: str - sample: "vnic0" -state: - description: state of the target - returned: always - type: str - sample: "present" -temporary: - description: persistence of a IP interface - returned: always - type: bool - sample: "True" -''' -from ansible.module_utils.basic import AnsibleModule - - -class IPInterface(object): - - def __init__(self, module): - self.module = module - - self.name = module.params['name'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - def interface_exists(self): - cmd = [self.module.get_bin_path('ipadm', True)] - - cmd.append('show-if') - cmd.append(self.name) - - (rc, _, _) = self.module.run_command(cmd) - if rc == 0: - return True - else: - return False - - def interface_is_disabled(self): - cmd = [self.module.get_bin_path('ipadm', True)] - - cmd.append('show-if') - cmd.append('-o') - cmd.append('state') - cmd.append(self.name) - - (rc, out, err) = self.module.run_command(cmd) - if rc != 0: - self.module.fail_json(name=self.name, rc=rc, msg=err) - - return 'disabled' in out - - def create_interface(self): - cmd = [self.module.get_bin_path('ipadm', True)] - - cmd.append('create-if') - - if self.temporary: - cmd.append('-t') - - cmd.append(self.name) - - return self.module.run_command(cmd) - - def delete_interface(self): - cmd = [self.module.get_bin_path('ipadm', True)] - - cmd.append('delete-if') - - if self.temporary: - cmd.append('-t') - - cmd.append(self.name) - - return self.module.run_command(cmd) - - def enable_interface(self): - cmd = [self.module.get_bin_path('ipadm', True)] - - cmd.append('enable-if') - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - def disable_interface(self): - cmd = [self.module.get_bin_path('ipadm', True)] - - cmd.append('disable-if') - cmd.append('-t') - cmd.append(self.name) - - return self.module.run_command(cmd) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - name=dict(required=True), - temporary=dict(default=False, type='bool'), - state=dict(default='present', choices=['absent', - 'present', - 'enabled', - 'disabled']), - ), - supports_check_mode=True - ) - - interface = IPInterface(module) - - rc = None - out = '' - err = '' - result = {} - result['name'] = interface.name - result['state'] = interface.state - result['temporary'] = interface.temporary - - if interface.state == 'absent': - if interface.interface_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = interface.delete_interface() - if rc != 0: - module.fail_json(name=interface.name, msg=err, rc=rc) - elif interface.state == 'present': - if not interface.interface_exists(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = interface.create_interface() - - if rc is not None and rc != 0: - module.fail_json(name=interface.name, msg=err, rc=rc) - - elif interface.state == 'enabled': - if interface.interface_is_disabled(): - (rc, out, err) = interface.enable_interface() - - if rc is not None and rc != 0: - module.fail_json(name=interface.name, msg=err, rc=rc) - - elif interface.state == 'disabled': - if not interface.interface_is_disabled(): - (rc, out, err) = interface.disable_interface() - - if rc is not None and rc != 0: - module.fail_json(name=interface.name, msg=err, rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/ipadm_ifprop.py b/plugins/modules/network/illumos/ipadm_ifprop.py deleted file mode 100644 index 3ba86abe81..0000000000 --- a/plugins/modules/network/illumos/ipadm_ifprop.py +++ /dev/null @@ -1,287 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2016, Adam Å tevko -# 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: ipadm_ifprop -short_description: Manage IP interface properties on Solaris/illumos systems. -description: - - Modify IP interface properties on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - interface: - description: - - Specifies the IP interface we want to manage. - required: true - aliases: [nic] - protocol: - description: - - Specifies the protocol for which we want to manage properties. - required: true - property: - description: - - Specifies the name of the property we want to manage. - required: true - aliases: [name] - value: - description: - - Specifies the value we want to set for the property. - required: false - temporary: - description: - - Specifies that the property value is temporary. Temporary - property values do not persist across reboots. - required: false - default: false - type: bool - state: - description: - - Set or reset the property value. - required: false - default: present - choices: [ "present", "absent", "reset" ] -''' - -EXAMPLES = ''' -- name: Allow forwarding of IPv4 packets on network interface e1000g0 - ipadm_ifprop: protocol=ipv4 property=forwarding value=on interface=e1000g0 - -- name: Temporarily reset IPv4 forwarding property on network interface e1000g0 - ipadm_ifprop: protocol=ipv4 interface=e1000g0 temporary=true property=forwarding state=reset - -- name: Configure IPv6 metric on network interface e1000g0 - ipadm_ifprop: protocol=ipv6 nic=e1000g0 name=metric value=100 - -- name: Set IPv6 MTU on network interface bge0 - ipadm_ifprop: interface=bge0 name=mtu value=1280 protocol=ipv6 -''' - -RETURN = ''' -protocol: - description: property's protocol - returned: always - type: str - sample: ipv4 -property: - description: property's name - returned: always - type: str - sample: mtu -interface: - description: interface name we want to set property on - returned: always - type: str - sample: e1000g0 -state: - description: state of the target - returned: always - type: str - sample: present -value: - description: property's value - returned: when value is provided - type: str - sample: 1280 -''' - -from ansible.module_utils.basic import AnsibleModule - - -SUPPORTED_PROTOCOLS = ['ipv4', 'ipv6'] - - -class IfProp(object): - - def __init__(self, module): - self.module = module - - self.interface = module.params['interface'] - self.protocol = module.params['protocol'] - self.property = module.params['property'] - self.value = module.params['value'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - def property_exists(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-ifprop') - cmd.append('-p') - cmd.append(self.property) - cmd.append('-m') - cmd.append(self.protocol) - cmd.append(self.interface) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - self.module.fail_json(msg='Unknown %s property "%s" on IP interface %s' % - (self.protocol, self.property, self.interface), - protocol=self.protocol, - property=self.property, - interface=self.interface) - - def property_is_modified(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-ifprop') - cmd.append('-c') - cmd.append('-o') - cmd.append('current,default') - cmd.append('-p') - cmd.append(self.property) - cmd.append('-m') - cmd.append(self.protocol) - cmd.append(self.interface) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - (value, default) = out.split(':') - - if rc == 0 and value == default: - return True - else: - return False - - def property_is_set(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-ifprop') - cmd.append('-c') - cmd.append('-o') - cmd.append('current') - cmd.append('-p') - cmd.append(self.property) - cmd.append('-m') - cmd.append(self.protocol) - cmd.append(self.interface) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - - if rc == 0 and self.value == out: - return True - else: - return False - - def set_property(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('set-ifprop') - - if self.temporary: - cmd.append('-t') - - cmd.append('-p') - cmd.append(self.property + "=" + self.value) - cmd.append('-m') - cmd.append(self.protocol) - cmd.append(self.interface) - - return self.module.run_command(cmd) - - def reset_property(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('reset-ifprop') - - if self.temporary: - cmd.append('-t') - - cmd.append('-p') - cmd.append(self.property) - cmd.append('-m') - cmd.append(self.protocol) - cmd.append(self.interface) - - return self.module.run_command(cmd) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - protocol=dict(required=True, choices=SUPPORTED_PROTOCOLS), - property=dict(required=True, aliases=['name']), - value=dict(required=False), - temporary=dict(default=False, type='bool'), - interface=dict(required=True, default=None, aliases=['nic']), - state=dict( - default='present', choices=['absent', 'present', 'reset']), - ), - supports_check_mode=True - ) - - ifprop = IfProp(module) - - rc = None - out = '' - err = '' - result = {} - result['protocol'] = ifprop.protocol - result['property'] = ifprop.property - result['interface'] = ifprop.interface - result['state'] = ifprop.state - if ifprop.value: - result['value'] = ifprop.value - - if ifprop.state == 'absent' or ifprop.state == 'reset': - if ifprop.property_exists(): - if not ifprop.property_is_modified(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = ifprop.reset_property() - if rc != 0: - module.fail_json(protocol=ifprop.protocol, - property=ifprop.property, - interface=ifprop.interface, - msg=err, - rc=rc) - - elif ifprop.state == 'present': - if ifprop.value is None: - module.fail_json(msg='Value is mandatory with state "present"') - - if ifprop.property_exists(): - if not ifprop.property_is_set(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = ifprop.set_property() - if rc != 0: - module.fail_json(protocol=ifprop.protocol, - property=ifprop.property, - interface=ifprop.interface, - msg=err, - rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/illumos/ipadm_prop.py b/plugins/modules/network/illumos/ipadm_prop.py deleted file mode 100644 index 83512c4c80..0000000000 --- a/plugins/modules/network/illumos/ipadm_prop.py +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2015, Adam Å tevko -# 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: ipadm_prop -short_description: Manage protocol properties on Solaris/illumos systems. -description: - - Modify protocol properties on Solaris/illumos systems. -author: Adam Å tevko (@xen0l) -options: - protocol: - description: - - Specifies the protocol for which we want to manage properties. - required: true - property: - description: - - Specifies the name of property we want to manage. - required: true - value: - description: - - Specifies the value we want to set for the property. - required: false - temporary: - description: - - Specifies that the property value is temporary. Temporary - property values do not persist across reboots. - required: false - default: false - type: bool - state: - description: - - Set or reset the property value. - required: false - default: present - choices: [ "present", "absent", "reset" ] -''' - -EXAMPLES = ''' -# Set TCP receive buffer size -- ipadm_prop: protocol=tcp property=recv_buf value=65536 - -# Reset UDP send buffer size to the default value -- ipadm_prop: protocol=udp property=send_buf state=reset -''' - -RETURN = ''' -protocol: - description: property's protocol - returned: always - type: str - sample: "TCP" -property: - description: name of the property - returned: always - type: str - sample: "recv_maxbuf" -state: - description: state of the target - returned: always - type: str - sample: "present" -temporary: - description: property's persistence - returned: always - type: bool - sample: "True" -value: - description: value of the property. May be int or string depending on property. - returned: always - type: int - sample: "'1024' or 'never'" -''' - -from ansible.module_utils.basic import AnsibleModule - - -SUPPORTED_PROTOCOLS = ['ipv4', 'ipv6', 'icmp', 'tcp', 'udp', 'sctp'] - - -class Prop(object): - - def __init__(self, module): - self.module = module - - self.protocol = module.params['protocol'] - self.property = module.params['property'] - self.value = module.params['value'] - self.temporary = module.params['temporary'] - self.state = module.params['state'] - - def property_exists(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-prop') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.protocol) - - (rc, _, _) = self.module.run_command(cmd) - - if rc == 0: - return True - else: - self.module.fail_json(msg='Unknown property "%s" for protocol %s' % - (self.property, self.protocol), - protocol=self.protocol, - property=self.property) - - def property_is_modified(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-prop') - cmd.append('-c') - cmd.append('-o') - cmd.append('current,default') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.protocol) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - (value, default) = out.split(':') - - if rc == 0 and value == default: - return True - else: - return False - - def property_is_set(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('show-prop') - cmd.append('-c') - cmd.append('-o') - cmd.append('current') - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.protocol) - - (rc, out, _) = self.module.run_command(cmd) - - out = out.rstrip() - - if rc == 0 and self.value == out: - return True - else: - return False - - def set_property(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('set-prop') - - if self.temporary: - cmd.append('-t') - - cmd.append('-p') - cmd.append(self.property + "=" + self.value) - cmd.append(self.protocol) - - return self.module.run_command(cmd) - - def reset_property(self): - cmd = [self.module.get_bin_path('ipadm')] - - cmd.append('reset-prop') - - if self.temporary: - cmd.append('-t') - - cmd.append('-p') - cmd.append(self.property) - cmd.append(self.protocol) - - return self.module.run_command(cmd) - - -def main(): - module = AnsibleModule( - argument_spec=dict( - protocol=dict(required=True, choices=SUPPORTED_PROTOCOLS), - property=dict(required=True), - value=dict(required=False), - temporary=dict(default=False, type='bool'), - state=dict( - default='present', choices=['absent', 'present', 'reset']), - ), - supports_check_mode=True - ) - - prop = Prop(module) - - rc = None - out = '' - err = '' - result = {} - result['protocol'] = prop.protocol - result['property'] = prop.property - result['state'] = prop.state - result['temporary'] = prop.temporary - if prop.value: - result['value'] = prop.value - - if prop.state == 'absent' or prop.state == 'reset': - if prop.property_exists(): - if not prop.property_is_modified(): - if module.check_mode: - module.exit_json(changed=True) - (rc, out, err) = prop.reset_property() - if rc != 0: - module.fail_json(protocol=prop.protocol, - property=prop.property, - msg=err, - rc=rc) - - elif prop.state == 'present': - if prop.value is None: - module.fail_json(msg='Value is mandatory with state "present"') - - if prop.property_exists(): - if not prop.property_is_set(): - if module.check_mode: - module.exit_json(changed=True) - - (rc, out, err) = prop.set_property() - if rc != 0: - module.fail_json(protocol=prop.protocol, - property=prop.property, - msg=err, - rc=rc) - - if rc is None: - result['changed'] = False - else: - result['changed'] = True - - if out: - result['stdout'] = out - if err: - result['stderr'] = err - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ingate/ig_config.py b/plugins/modules/network/ingate/ig_config.py deleted file mode 100644 index 5e02e6c4c1..0000000000 --- a/plugins/modules/network/ingate/ig_config.py +++ /dev/null @@ -1,567 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2018, Ingate Systems AB -# -# 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 . - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' ---- -module: ig_config -short_description: Manage the configuration database on an Ingate SBC. -description: - - Manage the configuration database on an Ingate SBC. -extends_documentation_fragment: -- community.general.ingate - -options: - add: - description: - - Add a row to a table. - type: bool - delete: - description: - - Delete all rows in a table or a specific row. - type: bool - get: - description: - - Return all rows in a table or a specific row. - type: bool - modify: - description: - - Modify a row in a table. - type: bool - revert: - description: - - Reset the preliminary configuration. - type: bool - factory: - description: - - Reset the preliminary configuration to its factory defaults. - type: bool - store: - description: - - Store the preliminary configuration. - type: bool - no_response: - description: - - Expect no response when storing the preliminary configuration. - Refer to the C(store) option. - type: bool - return_rowid: - description: - - Get rowid(s) from a table where the columns match. - type: bool - download: - description: - - Download the configuration database from the unit. - type: bool - store_download: - description: - - If the downloaded configuration should be stored on disk. - Refer to the C(download) option. - type: bool - default: false - path: - description: - - Where in the filesystem to store the downloaded configuration. - Refer to the C(download) option. - filename: - description: - - The name of the file to store the downloaded configuration in. - Refer to the C(download) option. - table: - description: - - The name of the table. - rowid: - description: - - A row id. - type: int - columns: - description: - - A dict containing column names/values. -notes: - - If C(store_download) is set to True, and C(path) and C(filename) is omitted, - the file will be stored in the current directory with an automatic filename. -author: - - Ingate Systems AB (@ingatesystems) -''' - -EXAMPLES = ''' -- name: Add/remove DNS servers - hosts: 192.168.1.1 - connection: local - vars: - client_rw: - version: v1 - address: "{{ inventory_hostname }}" - scheme: http - username: alice - password: foobar - tasks: - - - name: Load factory defaults - ig_config: - client: "{{ client_rw }}" - factory: true - register: result - - debug: - var: result - - - name: Revert to last known applied configuration - ig_config: - client: "{{ client_rw }}" - revert: true - register: result - - debug: - var: result - - - name: Change the unit name - ig_config: - client: "{{ client_rw }}" - modify: true - table: misc.unitname - columns: - unitname: "Test Ansible" - register: result - - debug: - var: result - - - name: Add a DNS server - ig_config: - client: "{{ client_rw }}" - add: true - table: misc.dns_servers - columns: - server: 192.168.1.21 - register: result - - debug: - var: result - - - name: Add a DNS server - ig_config: - client: "{{ client_rw }}" - add: true - table: misc.dns_servers - columns: - server: 192.168.1.22 - register: result - - debug: - var: result - - - name: Add a DNS server - ig_config: - client: "{{ client_rw }}" - add: true - table: misc.dns_servers - columns: - server: 192.168.1.23 - register: last_dns - - debug: - var: last_dns - - - name: Modify the last added DNS server - ig_config: - client: "{{ client_rw }}" - modify: true - table: misc.dns_servers - rowid: "{{ last_dns['add'][0]['id'] }}" - columns: - server: 192.168.1.24 - register: result - - debug: - var: result - - - name: Return the last added DNS server - ig_config: - client: "{{ client_rw }}" - get: true - table: misc.dns_servers - rowid: "{{ last_dns['add'][0]['id'] }}" - register: result - - debug: - var: result - - - name: Remove last added DNS server - ig_config: - client: "{{ client_rw }}" - delete: true - table: misc.dns_servers - rowid: "{{ last_dns['add'][0]['id'] }}" - register: result - - debug: - var: result - - - name: Return the all rows from table misc.dns_servers - ig_config: - client: "{{ client_rw }}" - get: true - table: misc.dns_servers - register: result - - debug: - var: result - - - name: Remove remaining DNS servers - ig_config: - client: "{{ client_rw }}" - delete: true - table: misc.dns_servers - register: result - - debug: - var: result - - - name: Get rowid for interface eth0 - ig_config: - client: "{{ client_rw }}" - return_rowid: true - table: network.local_nets - columns: - interface: eth0 - register: result - - debug: - var: result - - - name: Store the preliminary configuration - ig_config: - client: "{{ client_rw }}" - store: true - register: result - - debug: - var: result - - - name: Do backup of the configuration database - ig_config: - client: "{{ client_rw }}" - download: true - store_download: true - register: result - - debug: - var: result -''' - -RETURN = ''' -add: - description: A list containing information about the added row - returned: when C(add) is yes and success - type: complex - contains: - href: - description: The REST API URL to the added row - returned: success - type: str - sample: http://192.168.1.1/api/v1/misc/dns_servers/2 - data: - description: Column names/values - returned: success - type: complex - sample: {'number': '2', 'server': '10.48.254.33'} - id: - description: The row id - returned: success - type: int - sample: 22 -delete: - description: A list containing information about the deleted row(s) - returned: when C(delete) is yes and success - type: complex - contains: - table: - description: The name of the table - returned: success - type: str - sample: misc.dns_servers - data: - description: Column names/values - returned: success - type: complex - sample: {'number': '2', 'server': '10.48.254.33'} - id: - description: The row id - returned: success - type: int - sample: 22 -get: - description: A list containing information about the row(s) - returned: when C(get) is yes and success - type: complex - contains: - table: - description: The name of the table - returned: success - type: str - sample: Testname - href: - description: The REST API URL to the row - returned: success - type: str - sample: http://192.168.1.1/api/v1/misc/dns_servers/1 - data: - description: Column names/values - returned: success - type: complex - sample: {'number': '2', 'server': '10.48.254.33'} - id: - description: The row id - returned: success - type: int - sample: 1 -modify: - description: A list containing information about the modified row - returned: when C(modify) is yes and success - type: complex - contains: - table: - description: The name of the table - returned: success - type: str - sample: Testname - href: - description: The REST API URL to the modified row - returned: success - type: str - sample: http://192.168.1.1/api/v1/misc/dns_servers/1 - data: - description: Column names/values - returned: success - type: complex - sample: {'number': '2', 'server': '10.48.254.33'} - id: - description: The row id - returned: success - type: int - sample: 10 -revert: - description: A command status message - returned: when C(revert) is yes and success - type: complex - contains: - msg: - description: The command status message - returned: success - type: str - sample: reverted the configuration to the last applied configuration. -factory: - description: A command status message - returned: when C(factory) is yes and success - type: complex - contains: - msg: - description: The command status message - returned: success - type: str - sample: reverted the configuration to the factory configuration. -store: - description: A command status message - returned: when C(store) is yes and success - type: complex - contains: - msg: - description: The command status message - returned: success - type: str - sample: Successfully applied and saved the configuration. -return_rowid: - description: The matched row id(s). - returned: when C(return_rowid) is yes and success - type: list - sample: [1, 3] -download: - description: Configuration database and meta data - returned: when C(download) is yes and success - type: complex - contains: - config: - description: The configuration database - returned: success - type: str - filename: - description: A suggested name for the configuration - returned: success - type: str - sample: testname_2018-10-01T214040.cfg - mimetype: - description: The mimetype - returned: success - type: str - sample: application/x-config-database -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.ingate.common import (ingate_argument_spec, - ingate_create_client) - -try: - from ingate import ingatesdk - HAS_INGATESDK = True -except ImportError: - HAS_INGATESDK = False - - -def make_request(module): - # Create client and authenticate. - api_client = ingate_create_client(**module.params) - - if module.params.get('add'): - # Add a row to a table. - table = module.params['table'] - columns = module.params['columns'] - response = api_client.add_row(table, **columns) - return True, 'add', response - elif module.params.get('delete'): - # Delete a row/table. - changed = False - table = module.params['table'] - rowid = module.params.get('rowid') - if rowid: - response = api_client.delete_row(table, rowid=rowid) - else: - response = api_client.delete_table(table) - if response: - changed = True - return changed, 'delete', response - elif module.params.get('get'): - # Get the contents of a table/row. - table = module.params['table'] - rowid = module.params.get('rowid') - if rowid: - response = api_client.dump_row(table, rowid=rowid) - else: - response = api_client.dump_table(table) - if response: - changed = True - return changed, 'get', response - elif module.params.get('modify'): - # Modify a table row. - table = module.params['table'] - columns = module.params['columns'] - rowid = module.params.get('rowid') - if rowid: - response = api_client.modify_row(table, rowid=rowid, **columns) - else: - response = api_client.modify_single_row(table, **columns) - if response: - changed = True - return changed, 'modify', response - elif module.params.get('revert'): - # Revert edits. - response = api_client.revert_edits() - if response: - response = response[0]['revert-edits'] - return True, 'revert', response - elif module.params.get('factory'): - # Load factory defaults. - response = api_client.load_factory() - if response: - response = response[0]['load-factory'] - return True, 'factory', response - elif module.params.get('store'): - # Store edit. - no_response = module.params.get('no_response') - response = api_client.store_edit(no_response=no_response) - if response: - response = response[0]['store-edit'] - return True, 'store', response - elif module.params.get('return_rowid'): - # Find matching rowid(s) in a table. - table = module.params['table'] - columns = module.params['columns'] - response = api_client.dump_table(table) - rowids = [] - for row in response: - match = False - for (name, value) in columns.items(): - if name not in row['data']: - continue - if not row['data'][name] == value: - match = False - break - else: - match = True - if match: - rowids.append(row['id']) - return False, 'return_rowid', rowids - elif module.params.get('download'): - # Download the configuration database. - store = module.params.get('store_download') - path = module.params.get('path') - filename = module.params.get('filename') - response = api_client.download_config(store=store, path=path, - filename=filename) - if response: - response = response[0]['download-config'] - return False, 'download', response - return False, '', {} - - -def main(): - argument_spec = ingate_argument_spec( - add=dict(type='bool'), - delete=dict(type='bool'), - get=dict(type='bool'), - modify=dict(type='bool'), - revert=dict(type='bool'), - factory=dict(type='bool'), - store=dict(type='bool'), - no_response=dict(type='bool', default=False), - return_rowid=dict(type='bool'), - download=dict(type='bool'), - store_download=dict(type='bool', default=False), - path=dict(), - filename=dict(), - table=dict(), - rowid=dict(type='int'), - columns=dict(type='dict'), - ) - - mutually_exclusive = [('add', 'delete', 'get', 'modify', 'revert', - 'factory', 'store', 'return_rowid', 'download')] - required_one_of = [['add', 'delete', 'get', 'modify', 'revert', 'factory', - 'store', 'return_rowid', 'download']] - required_if = [('add', True, ['table', 'columns']), - ('delete', True, ['table']), - ('get', True, ['table']), - ('modify', True, ['table', 'columns']), - ('return_rowid', True, ['table', 'columns'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - required_one_of=required_one_of, - supports_check_mode=False) - if not HAS_INGATESDK: - module.fail_json(msg='The Ingate Python SDK module is required') - - result = dict(changed=False) - try: - changed, command, response = make_request(module) - if response and command: - result[command] = response - result['changed'] = changed - except ingatesdk.SdkError as e: - module.fail_json(msg=str(e)) - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ingate/ig_unit_information.py b/plugins/modules/network/ingate/ig_unit_information.py deleted file mode 100644 index 4727d46f4a..0000000000 --- a/plugins/modules/network/ingate/ig_unit_information.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2018, Ingate Systems AB -# 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 = { - 'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1' -} - -DOCUMENTATION = ''' ---- -module: ig_unit_information -short_description: Get unit information from an Ingate SBC. -description: - - Get unit information from an Ingate SBC. -extends_documentation_fragment: -- community.general.ingate - -author: - - Ingate Systems AB (@ingatesystems) -''' - -EXAMPLES = ''' -- name: Get unit information - ig_unit_information: - client: - version: v1 - scheme: http - address: 192.168.1.1 - username: alice - password: foobar -''' - -RETURN = ''' -unit-information: - description: Information about the unit - returned: success - type: complex - contains: - installid: - description: The installation identifier - returned: success - type: str - sample: any - interfaces: - description: List of interface names - returned: success - type: str - sample: eth0 eth1 eth2 eth3 eth4 eth5 - lang: - description: The unit's language - returned: success - type: str - sample: en - lic_email: - description: License email information - returned: success - type: str - sample: example@example.com - lic_mac: - description: License MAC information - returned: success - type: str - sample: any - lic_name: - description: License name information - returned: success - type: str - sample: Example Inc - macaddr: - description: The MAC address of the first interface - returned: success - type: str - sample: 52:54:00:4c:e2:07 - mode: - description: Operational mode of the unit - returned: success - type: str - sample: Siparator - modules: - description: Installed module licenses - returned: success - type: str - sample: failover vpn sip qturn ems qos rsc voipsm - patches: - description: Installed patches on the unit - returned: success - type: list - sample: [] - product: - description: The product name - returned: success - type: str - sample: Software SIParator/Firewall - serial: - description: The serial number of the unit - returned: success - type: str - sample: IG-200-839-2008-0 - systemid: - description: The system identifier of the unit - returned: success - type: str - sample: IG-200-839-2008-0 - unitname: - description: The name of the unit - returned: success - type: str - sample: Testname - version: - description: Firmware version - returned: success - type: str - sample: 6.2.0-beta2 -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native -from ansible_collections.community.general.plugins.module_utils.network.ingate.common import (ingate_argument_spec, - ingate_create_client, - is_ingatesdk_installed) - -try: - from ingate import ingatesdk -except ImportError: - pass - - -def make_request(module): - # Create client and authenticate. - api_client = ingate_create_client(**module.params) - - # Get unit information. - response = api_client.unit_information() - return response - - -def main(): - argument_spec = ingate_argument_spec() - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=False) - - is_ingatesdk_installed(module) - - result = dict(changed=False) - try: - response = make_request(module) - result.update(response[0]) - except ingatesdk.SdkError as e: - module.fail_json(msg=to_native(e)) - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ironware/ironware_command.py b/plugins/modules/network/ironware/ironware_command.py deleted file mode 100644 index 9584fd17bc..0000000000 --- a/plugins/modules/network/ironware/ironware_command.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ironware_command -author: "Paul Baker (@paulquack)" -short_description: Run arbitrary commands on Extreme IronWare devices -description: - - Sends arbitrary commands to a Extreme Ironware node and returns the - results read from the device. This module includes a I(wait_for) - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. -extends_documentation_fragment: -- community.general.ironware - -options: - commands: - description: - - List of commands to send to the remote 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 retires as expired. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - match: - description: - - The I(match) argument is used in conjunction with the - I(wait_for) argument to specify the match policy. If the value - is set to C(all) then all conditionals in the I(wait_for) must be - satisfied. If the value is set to C(any) then only one of the - values must be satisfied. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -- ironware_command: - commands: - - show version - -- ironware_command: - commands: - - show interfaces brief wide - - show mpls vll -""" - -RETURN = """ -stdout: - description: the set of responses from the commands - returned: always - type: list - sample: ['...', '...'] - -stdout_lines: - description: The value of stdout split into a list - returned: always - type: list - sample: [['...', '...'], ['...'], ['...']] - -failed_conditions: - description: the conditionals that failed - returned: failed - type: list - sample: ['...', '...'] -""" -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.ironware.ironware import ironware_argument_spec, check_args -from ansible_collections.community.general.plugins.module_utils.network.ironware.ironware import run_commands -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 main(): - spec = dict( - # { command: , prompt: , response: } - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - spec.update(ironware_argument_spec) - - module = AnsibleModule(argument_spec=spec, supports_check_mode=True) - check_args(module) - - result = {'changed': False} - - wait_for = module.params['wait_for'] or list() - conditionals = [Conditional(c) for c in wait_for] - - commands = module.params['commands'] - 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/plugins/modules/network/ironware/ironware_config.py b/plugins/modules/network/ironware/ironware_config.py deleted file mode 100644 index 02e59fec00..0000000000 --- a/plugins/modules/network/ironware/ironware_config.py +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ironware_config -author: "Paul Baker (@paulquack)" -short_description: Manage configuration sections on Extreme Ironware devices -description: - - Extreme Ironware configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with Ironware configuration sections in - a deterministic way. -extends_documentation_fragment: -- community.general.ironware - -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. - aliases: ['commands'] - parents: - description: - - The ordered set of parents that uniquely identify the section - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - 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 - 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. - 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. - 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 - 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 the argument is set to - I(merge), the configuration changes are merged with the current - device running configuration. When the argument is set to I(check) - the configuration updates are determined but not actually configured - on the remote device. - default: merge - choices: ['merge', 'check'] - commit: - description: - - This argument specifies the update method to use when applying the - configuration changes to the remote node. If the value is set to - I(merge) the configuration updates are merged with the running- - config. If the value is set to I(check), no changes are made to - the remote host. - default: merge - choices: ['merge', 'check'] - 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' - config: - description: - - The C(config) argument allows the playbook designer to supply - the base configuration to be used to validate configuration - changes necessary. If this argument is provided, the module - will not download the running-config from the remote node. - save_when: - description: - - When changes are made to the device running-configuration, the - changes are not copied to non-volatile storage by default. Using - this argument will change that before. If the argument is set to - I(always), then the running-config will always be copied to the - startup-config and the I(modified) flag will always be set to - True. If the argument is set to I(modified), then the running-config - will only be copied to the startup-config if it has changed since - the last save to startup-config. If the argument is set to - I(never), the running-config will never be copied to the - startup-config - default: never - choices: ['always', 'never', 'modified'] - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- ironware_config: - lines: - - port-name test - - enable - - load-interval 30 - - rate-limit input broadcast unknown-unicast multicast 521216 64000 - parents: ['interface ethernet 1/2'] -""" - -RETURN = """ -updates: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['...', '...'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/ironware_config.2016-07-16@22:28:34 -""" -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.ironware.ironware import ironware_argument_spec, check_args -from ansible_collections.community.general.plugins.module_utils.network.ironware.ironware import get_config, load_config, run_commands -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() - candidate.add(module.params['lines'], parents=parents) - return candidate - - -def run(module, result): - match = module.params['match'] - replace = module.params['replace'] - path = module.params['parents'] - configobjs = None - - candidate = get_candidate(module) - if match != 'none': - contents = module.params['config'] - if not contents: - contents = get_config(module) - config = NetworkConfig(indent=1, contents=contents) - configobjs = candidate.difference(config, path=path, match=match, - replace=replace) - - else: - configobjs = candidate.items - if configobjs: - commands = dumps(configobjs, 'commands').split('\n') - - if module.params['lines']: - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['updates'] = commands - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - if result['changed'] or module.params['save_when'] == 'always': - result['changed'] = True - if not module.check_mode: - cmd = {'command': 'write memory'} - run_commands(module, [cmd]) - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - - config=dict(), - - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - save_when=dict(choices=['always', 'never', 'modified'], default='never') - - ) - - argument_spec.update(ironware_argument_spec) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - - check_args(module) - - if module.params['backup']: - result['__backup__'] = get_config(module) - - run(module, result) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ironware/ironware_facts.py b/plugins/modules/network/ironware/ironware_facts.py deleted file mode 100644 index 1aa738dbeb..0000000000 --- a/plugins/modules/network/ironware/ironware_facts.py +++ /dev/null @@ -1,652 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ironware_facts -author: "Paul Baker (@paulquack)" -short_description: Collect facts from devices running Extreme Ironware -description: - - Collects a base set of device facts from a remote device that - is running Ironware. This module prepends all of the - base network fact keys with C(ansible_net_). 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: -- community.general.ironware - -notes: - - Tested against Ironware 5.8e -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, mpls and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: ['!config','!mpls'] -''' - -EXAMPLES = """ -# Collect all facts from the device -- ironware_facts: - gather_subset: all - -# Collect only the config and default facts -- ironware_facts: - gather_subset: - - config - -# Do not collect hardware facts -- ironware_facts: - gather_subset: - - "!hardware" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str - -# hardware -ansible_net_filesystems: - description: All file system names available on the device - returned: when hardware is configured - type: list -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 - -# mpls -ansible_net_mpls_lsps: - description: All MPLS LSPs configured on the device - returned: When LSP is configured - type: dict -ansible_net_mpls_vll: - description: All VLL instances configured on the device - returned: When MPLS VLL is configured - type: dict -ansible_net_mpls_vll_local: - description: All VLL-LOCAL instances configured on the device - returned: When MPLS VLL-LOCAL is configured - type: dict -ansible_net_mpls_vpls: - description: All VPLS instances configured on the device - returned: When MPLS VPLS is configured - type: dict - -# interfaces -ansible_net_all_ipv4_addresses: - description: All IPv4 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_all_ipv6_addresses: - description: All IPv6 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -ansible_net_neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -""" -import re - -from ansible_collections.community.general.plugins.module_utils.network.ironware.ironware import run_commands -from ansible_collections.community.general.plugins.module_utils.network.ironware.ironware import ironware_argument_spec, check_args -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, self.COMMANDS, check_rc=False) - - def run(self, cmd): - return run_commands(self.module, cmd, check_rc=False) - - -class Default(FactsBase): - - COMMANDS = [ - 'show version', - 'show chassis' - ] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - - data = self.responses[1] - if data: - self.facts['model'] = self.parse_model(data) - - def parse_version(self, data): - match = re.search(r'IronWare : Version (\S+)', data) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'^\*\*\* (.+) \*\*\*$', data, re.M) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'Serial #: (\S+),', data) - if match: - return match.group(1) - - -class Hardware(FactsBase): - - COMMANDS = [ - 'dir | include Directory', - 'show memory' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - if data: - self.facts['filesystems'] = self.parse_filesystems(data) - - data = self.responses[1] - if data: - self.facts['memtotal_mb'] = int(round(int(self.parse_memtotal(data)) / 1024 / 1024, 0)) - self.facts['memfree_mb'] = int(round(int(self.parse_memfree(data)) / 1024 / 1024, 0)) - - def parse_filesystems(self, data): - return re.findall(r'^Directory of (\S+)', data, re.M) - - def parse_memtotal(self, data): - match = re.search(r'Total SDRAM\D*(\d+)\s', data, re.M) - if match: - return match.group(1) - - def parse_memfree(self, data): - match = re.search(r'(Total Free Memory|Available Memory)\D*(\d+)\s', data, re.M) - if match: - return match.group(2) - - -class Config(FactsBase): - - COMMANDS = ['show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class MPLS(FactsBase): - - COMMANDS = [ - 'show mpls lsp detail', - 'show mpls vll-local detail', - 'show mpls vll detail', - 'show mpls vpls detail' - ] - - def populate(self): - super(MPLS, self).populate() - data = self.responses[0] - if data: - data = self.parse_mpls(data) - self.facts['mpls_lsps'] = self.populate_lsps(data) - - data = self.responses[1] - if data: - data = self.parse_mpls(data) - self.facts['mpls_vll_local'] = self.populate_vll_local(data) - - data = self.responses[2] - if data: - data = self.parse_mpls(data) - self.facts['mpls_vll'] = self.populate_vll(data) - - data = self.responses[3] - if data: - data = self.parse_mpls(data) - self.facts['mpls_vpls'] = self.populate_vpls(data) - - def parse_mpls(self, data): - parsed = dict() - for line in data.split('\n'): - if not line: - continue - elif line[0] == ' ': - parsed[key] += '\n%s' % line - else: - match = re.match(r'^(LSP|VLL|VPLS) ([^\s,]+)', line) - if match: - key = match.group(2) - parsed[key] = line - return parsed - - def populate_vpls(self, vpls): - facts = dict() - for key, value in iteritems(vpls): - vpls = dict() - vpls['endpoints'] = self.parse_vpls_endpoints(value) - vpls['vc-id'] = self.parse_vpls_vcid(value) - facts[key] = vpls - return facts - - def populate_vll_local(self, vll_locals): - facts = dict() - for key, value in iteritems(vll_locals): - vll = dict() - vll['endpoints'] = self.parse_vll_endpoints(value) - facts[key] = vll - return facts - - def populate_vll(self, vlls): - facts = dict() - for key, value in iteritems(vlls): - vll = dict() - vll['endpoints'] = self.parse_vll_endpoints(value) - vll['vc-id'] = self.parse_vll_vcid(value) - vll['cos'] = self.parse_vll_cos(value) - facts[key] = vll - return facts - - def parse_vll_vcid(self, data): - match = re.search(r'VC-ID (\d+),', data, re.M) - if match: - return match.group(1) - - def parse_vll_cos(self, data): - match = re.search(r'COS +: +(\d+)', data, re.M) - if match: - return match.group(1) - - def parse_vll_endpoints(self, data): - facts = list() - regex = r'End-point[0-9 ]*: +(?Ptagged|untagged) +(vlan +(?P[0-9]+) +)?(inner- vlan +(?P[0-9]+) +)?(?Pe [0-9/]+|--)' - matches = re.finditer(regex, data, re.IGNORECASE | re.DOTALL) - for match in matches: - f = match.groupdict() - f['type'] = 'local' - facts.append(f) - - regex = r'Vll-Peer +: +(?P[0-9\.]+).*Tunnel LSP +: +(?P\S+)' - matches = re.finditer(regex, data, re.IGNORECASE | re.DOTALL) - for match in matches: - f = match.groupdict() - f['type'] = 'remote' - facts.append(f) - - return facts - - def parse_vpls_vcid(self, data): - match = re.search(r'Id (\d+),', data, re.M) - if match: - return match.group(1) - - def parse_vpls_endpoints(self, data): - facts = list() - regex = r'Vlan (?P[0-9]+)\s(?: +(?:L2.*)\s| +Tagged: (?P.+)+\s| +Untagged: (?P.+)\s)*' - matches = re.finditer(regex, data, re.IGNORECASE) - for match in matches: - f = match.groupdict() - f['type'] = 'local' - facts.append(f) - - regex = r'Peer address: (?P[0-9\.]+)' - matches = re.finditer(regex, data, re.IGNORECASE) - for match in matches: - f = match.groupdict() - f['type'] = 'remote' - facts.append(f) - - return facts - - def populate_lsps(self, lsps): - facts = dict() - for key, value in iteritems(lsps): - lsp = dict() - lsp['to'] = self.parse_lsp_to(value) - lsp['from'] = self.parse_lsp_from(value) - lsp['adminstatus'] = self.parse_lsp_adminstatus(value) - lsp['operstatus'] = self.parse_lsp_operstatus(value) - lsp['pri_path'] = self.parse_lsp_pripath(value) - lsp['sec_path'] = self.parse_lsp_secpath(value) - lsp['frr'] = self.parse_lsp_frr(value) - - facts[key] = lsp - - return facts - - def parse_lsp_to(self, data): - match = re.search(r'^LSP .* to (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_lsp_from(self, data): - match = re.search(r'From: ([^\s,]+),', data, re.M) - if match: - return match.group(1) - - def parse_lsp_adminstatus(self, data): - match = re.search(r'admin: (\w+),', data, re.M) - if match: - return match.group(1) - - def parse_lsp_operstatus(self, data): - match = re.search(r'From: .* status: (\w+)', data, re.M) - if match: - return match.group(1) - - def parse_lsp_pripath(self, data): - match = re.search(r'Pri\. path: ([^\s,]+), up: (\w+), active: (\w+)', data, re.M) - if match: - path = dict() - path['name'] = match.group(1) if match.group(1) != 'NONE' else None - path['up'] = True if match.group(2) == 'yes' else False - path['active'] = True if match.group(3) == 'yes' else False - return path - - def parse_lsp_secpath(self, data): - match = re.search(r'Sec\. path: ([^\s,]+), active: (\w+).*\n.* status: (\w+)', data, re.M) - if match: - path = dict() - path['name'] = match.group(1) if match.group(1) != 'NONE' else None - path['up'] = True if match.group(3) == 'up' else False - path['active'] = True if match.group(2) == 'yes' else False - return path - - def parse_lsp_frr(self, data): - match = re.search(r'Backup LSP: (\w+)', data, re.M) - if match: - path = dict() - path['up'] = True if match.group(1) == 'UP' else False - path['name'] = None - if path['up']: - match = re.search(r'bypass_lsp: (\S)', data, re.M) - path['name'] = match.group(1) if match else None - return path - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'show interfaces', - 'show ipv6 interface', - 'show lldp neighbors' - ] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - data = self.responses[0] - if data: - interfaces = self.parse_interfaces(data) - self.facts['interfaces'] = self.populate_interfaces(interfaces) - - data = self.responses[1] - if data: - data = self.parse_interfaces(data) - self.populate_ipv6_interfaces(data) - - data = self.responses[2] - if data and 'LLDP is not running' not in data: - self.facts['neighbors'] = self.parse_neighbors(data) - - def populate_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - intf['description'] = self.parse_description(value) - intf['macaddress'] = self.parse_macaddress(value) - - ipv4 = self.parse_ipv4(value) - intf['ipv4'] = self.parse_ipv4(value) - if ipv4: - self.add_ip_address(ipv4['address'], 'ipv4') - - intf['mtu'] = self.parse_mtu(value) - intf['bandwidth'] = self.parse_bandwidth(value) - intf['duplex'] = self.parse_duplex(value) - intf['lineprotocol'] = self.parse_lineprotocol(value) - intf['operstatus'] = self.parse_operstatus(value) - intf['type'] = self.parse_type(value) - - facts[key] = intf - return facts - - def populate_ipv6_interfaces(self, data): - for key, value in iteritems(data): - self.facts['interfaces'][key]['ipv6'] = list() - addresses = re.findall(r'\s([0-9a-f]+:+[0-9a-f:]+\/\d+)\s', value, re.M) - for addr in addresses: - address, masklen = addr.split('/') - ipv6 = dict(address=address, masklen=int(masklen)) - self.add_ip_address(ipv6['address'], 'ipv6') - self.facts['interfaces'][key]['ipv6'].append(ipv6) - - def add_ip_address(self, address, family): - if family == 'ipv4': - self.facts['all_ipv4_addresses'].append(address) - else: - self.facts['all_ipv6_addresses'].append(address) - - def parse_neighbors(self, neighbors): - facts = dict() - for line in neighbors.split('\n'): - if line == '': - continue - match = re.search(r'([\d\/]+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)', line, re.M) - if match: - intf = match.group(1) - if intf not in facts: - facts[intf] = list() - fact = dict() - fact['host'] = match.group(5) - fact['port'] = match.group(3) - facts[intf].append(fact) - return facts - - def parse_interfaces(self, data): - parsed = dict() - for line in data.split('\n'): - if not line: - continue - elif line[0] == ' ': - parsed[key] += '\n%s' % line - else: - match = re.match(r'^(\S+Ethernet|eth )(\S+)', line) - if match: - key = match.group(2) - parsed[key] = line - return parsed - - def parse_description(self, data): - match = re.search(r'Port name is (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_macaddress(self, data): - match = re.search(r'address is (\S+)', data) - if match: - return match.group(1) - - def parse_ipv4(self, data): - match = re.search(r'Internet address is ([^\s,]+)', data) - if match: - addr, masklen = match.group(1).split('/') - return dict(address=addr, masklen=int(masklen)) - - def parse_mtu(self, data): - match = re.search(r'MTU (\d+)', data) - if match: - return int(match.group(1)) - - def parse_bandwidth(self, data): - match = re.search(r'BW is (\d+)', data) - if match: - return int(match.group(1)) - - def parse_duplex(self, data): - match = re.search(r'configured duplex \S+ actual (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_mediatype(self, data): - match = re.search(r'Type\s*:\s*(.+)$', data, re.M) - if match: - return match.group(1) - - def parse_type(self, data): - match = re.search(r'Hardware is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lineprotocol(self, data): - match = re.search(r'line protocol is (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_operstatus(self, data): - match = re.search(r'^(?:.+) is (.+),', data, re.M) - if match: - return match.group(1) - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, - mpls=MPLS, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=["!config", "!mpls"], type='list') - ) - - argument_spec.update(ironware_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 - - check_args(module) - - module.exit_json(ansible_facts=ansible_facts) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/itential/iap_start_workflow.py b/plugins/modules/network/itential/iap_start_workflow.py deleted file mode 100644 index 9717cc3977..0000000000 --- a/plugins/modules/network/itential/iap_start_workflow.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2018, Itential -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -This module provides the ability to start a workflow from Itential Automation Platform -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: iap_start_workflow -author: "Itential (@cma0) " -short_description: Start a workflow in the Itential Automation Platform -description: - - This will start a specified workflow in the Itential Automation Platform with given arguments. -options: - iap_port: - description: - - Provide the port number for the Itential Automation Platform - required: true - type: str - default: null - - iap_fqdn: - description: - - Provide the fqdn for the Itential Automation Platform - required: true - type: str - default: null - - token_key: - description: - - Token key generated by iap_token module for the Itential Automation Platform - required: true - type: str - default: null - - workflow_name: - description: - - Provide the workflow name - required: true - type: str - default: null - - description: - description: - - Provide the description for the workflow - required: true - type: str - default: null - - variables: - description: - - Provide the values to the job variables - required: true - type: dict - default: null - - https: - description: - - Use HTTPS to connect - - By default using http - type: bool - default: False - - validate_certs: - description: - - If C(no), SSL certificates for the target url will not be validated. This should only be used - on personally controlled sites using self-signed certificates. - type: bool - default: False -''' - -EXAMPLES = ''' -- name: Start a workflow in the Itential Automation Platform - iap_start_workflow: - iap_port: 3000 - iap_fqdn: localhost - token_key: "DFSFSFHFGFGF[DSFSFAADAFASD%3D" - workflow_name: "RouterUpgradeWorkflow" - description: "OS-Router-Upgrade" - variables: {"deviceName":"ASR9K"} - register: result - -- debug: var=result -''' - -RETURN = ''' -response: - description: The result contains the response from the call - type: dict - returned: always -msg: - description: The msg will contain the error code or status of the workflow - type: str - returned: always -''' - -# Ansible imports -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import fetch_url - -# Standard library imports -import json - - -def start_workflow(module): - """ - :param module: - :return: response and msg - """ - # By default this will be http. - # By default when using https, self signed certificate is used - # If https needs to pass certificate then use validate_certs as true - if module.params['https']: - transport_protocol = 'https' - else: - transport_protocol = 'http' - - application_token = str(module.params['token_key']) - url = str(transport_protocol) + "://" + str(module.params['iap_fqdn']) + ":" + str(module.params[ - 'iap_port']) + "/workflow_engine/startJobWithOptions/" \ - + str(module.params['workflow_name']) + "?token=" + str(application_token) - options = { - "variables": module.params['variables'], - "description": str(module.params['description']) - } - - payload = { - "workflow": module.params['workflow_name'], - "options": options - } - - json_body = module.jsonify(payload) - headers = dict() - headers['Content-Type'] = 'application/json' - - # Using fetch url instead of requests - response, info = fetch_url(module, url, data=json_body, headers=headers) - response_code = str(info['status']) - if info['status'] not in [200, 201]: - module.fail_json(msg="Failed to connect to Itential Automation Platform. Response code is " + response_code) - - # in the event of a successful module execution, you will want to - # simple AnsibleModule.exit_json(), passing the key/value results - jsonResponse = json.loads(response.read().decode('utf-8')) - module.exit_json(changed=True, msg={"workflow_name": module.params['workflow_name'], "status": "started"}, - response=jsonResponse) - - -def main(): - """ - :return: response and msg - """ - # define the available arguments/parameters that a user can pass to - # the module - # the AnsibleModule object will be our abstraction working with Ansible - # this includes instantiation, a couple of common attr would be the - # args/params passed to the execution, as well as if the module - # supports check mode - module = AnsibleModule( - argument_spec=dict( - iap_port=dict(type='str', required=True), - iap_fqdn=dict(type='str', required=True), - token_key=dict(type='str', required=True), - workflow_name=dict(type='str', required=True), - description=dict(type='str', required=True), - variables=dict(type='dict', required=False), - https=(dict(type='bool', default=False)), - validate_certs=dict(type='bool', default=False) - ) - ) - start_workflow(module) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/itential/iap_token.py b/plugins/modules/network/itential/iap_token.py deleted file mode 100644 index 96b5607ca4..0000000000 --- a/plugins/modules/network/itential/iap_token.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright: (c) 2018, Itential -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -This module provides the token for Itential Automation Platform -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: iap_token -author: "Itential (@cma0) " -short_description: Get token for the Itential Automation Platform -description: - - Checks the connection to IAP and retrieves a login token. -options: - iap_port: - description: - - Provide the port number for the Itential Automation Platform - required: true - default: null - - iap_fqdn: - description: - - Provide the fqdn or ip-address for the Itential Automation Platform - required: true - default: null - - username: - description: - - Provide the username for the Itential Automation Platform - required: true - default: null - - password: - description: - - Provide the password for the Itential Automation Platform - required: true - default: null - - https: - description: - - Use HTTPS to connect - - By default using http - type: bool - default: False - - validate_certs: - description: - - If C(no), SSL certificates for the target url will not be validated. This should only be used - on personally controlled sites using self-signed certificates. - type: bool - default: False -''' - -EXAMPLES = ''' -- name: Get token for the Itential Automation Platform - iap_token: - iap_port: 3000 - iap_fqdn: localhost - username: myusername - password: mypass - register: result - -- debug: var=result.token -''' - -RETURN = ''' -token: - description: The token acquired from the Itential Automation Platform - type: str - returned: always -''' -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import fetch_url - - -def get_token(module): - """ - :param module: - :return: token - """ - # defaulting the value for transport_protocol to be : http - transport_protocol = 'http' - if module.params['https'] or module.params['validate_certs'] is True: - transport_protocol = 'https' - - url = transport_protocol + "://" + module.params['iap_fqdn'] + ":" + module.params['iap_port'] + "/login" - username = module.params['username'] - password = module.params['password'] - - login = { - "user": { - "username": username, - "password": password - } - } - json_body = module.jsonify(login) - headers = {} - headers['Content-Type'] = 'application/json' - - # Using fetch url instead of requests - response, info = fetch_url(module, url, data=json_body, headers=headers) - response_code = str(info['status']) - if info['status'] not in [200, 201]: - module.fail_json(msg="Failed to connect to Itential Automation Platform" + response_code) - response = response.read() - module.exit_json(changed=True, token=response) - - -def main(): - """ - :return: token - """ - # define the available arguments/parameters that a user can pass to - # the module - # the AnsibleModule object will be our abstraction working with Ansible - # this includes instantiation, a couple of common attr would be the - # args/params passed to the execution, as well as if the module - # supports check mode - module = AnsibleModule( - argument_spec=dict( - iap_port=dict(type='int', required=True), - iap_fqdn=dict(type='str', required=True), - username=dict(type='str', required=True), - password=dict(type='str', required=True, no_log=True), - https=(dict(type='bool', default=False)), - validate_certs=dict(type='bool', default=False) - ) - ) - get_token(module) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netact/netact_cm_command.py b/plugins/modules/network/netact/netact_cm_command.py deleted file mode 100644 index 74b077667f..0000000000 --- a/plugins/modules/network/netact/netact_cm_command.py +++ /dev/null @@ -1,366 +0,0 @@ -#!/usr/bin/python -# Copyright: Nokia -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# pylint: disable=invalid-name -# pylint: disable=wrong-import-position -# pylint: disable=too-many-locals -# pylint: disable=too-many-branches -# pylint: disable=too-many-statements - -""" -NetAct CM ansible command module -""" -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} - -DOCUMENTATION = ''' ---- -module: netact_cm_command - -short_description: Manage network configuration data in Nokia Core and Radio networks - - -description: - netact_cm_command can be used to run various configuration management operations. - This module requires that the target hosts have Nokia NetAct network management system installed. - Module will access the Configurator command line interface in NetAct to upload network configuration to NetAct, - run configuration export, plan import and configuration provision operations - To set the scope of the operation, define Distinguished Name (DN) or Working Set (WS) or - Maintenance Region (MR) as input -options: - operation: - description: - Supported operations allow user to upload actual configuration from the network, to import and - provision prepared plans, or export reference or actual configuration for planning purposes. - Provision_Mass_Modification enables provisioning the same parameters to multiple network elements. - This operation supports modifications only to one object class at a time. With this option - NetAct Configurator creates and provisions a plan to the network with the given scope and options. - required: true - choices: - - upload - - provision - - import - - export - - Provision_Mass_Modification - aliases: - - op - opsName: - description: - - user specified operation name - required: false - DN: - description: - Sets the exact scope of the operation in form of a list of managed object - Distinguished Names (DN) in the network. - A single DN or a list of DNs can be given (comma separated list without spaces). - Alternatively, if DN or a list of DNs is not given, working set (WS) or Maintenance Region (MR) - must be provided as parameter to set the scope of operation. - required: false - - WS: - description: - Sets the scope of the operation to use one or more pre-defined working sets (WS) in NetAct. - A working set contains network elements selected by user according to defined criteria. - A single WS name, or multiple WSs can be provided (comma-separated list without spaces). - Alternatively, if a WS name or a list of WSs is not given, Distinguished Name (DN) or - Maintenance Region(MR) must be provided as parameter to set the scope of operation. - required: false - MR: - description: - Sets the scope of the operation to network elements assigned to a Maintenance Region (MR) - Value can be set as MR IDs including the Maintenance Region Collection (MRC) - information (for example MRC-FIN1/MR-Hel). - Multiple MRs can be given (comma-separated list without spaces) - The value of this parameter is searched through MR IDs under given MRC. If there is no match, - then it is searched from all MR names. - Alternatively, if MR ID or a list or MR IDs is not given, Distinguished Name (DN) or Working Set (WS) - must be provided as parameter to set the scope of operation. - required: false - planName: - description: - - Specifies a plan name. - required: false - typeOption: - description: - Specifies the type of the export operation. - required: false - choices: - - plan - - actual - - reference - - template - - siteTemplate - aliases: - - type - fileFormat: - description: - Indicates file format. - required: false - choices: - - RAML2 - - CSV - - XLSX - fileName: - description: - - Specifies a file name. Valid for Import and Export operations. - required: false - inputFile: - description: - Specifies full path to plan file location for the import operation. - This parameter (inputFile) or the fileName parameter must be filled. If both are present then - the inputFile is used. - required: false - createBackupPlan: - description: - - Specifies if backup plan generation is enabled. - required: false - type: bool - backupPlanName: - description: - - Specifies a backup plan name - required: false - verbose: - description: - NetAct Configurator will print more info - required: false - extra_opts: - description: - Extra options to be set for operations. Check Configuration Management > Configuration Management - Operating Procedures > Command Line Operations in Nokia NetAct user documentation for further - information for extra options. - required: false -notes: - - Check mode is not currently supported -author: - - Harri Tuominen (@hatuomin) -''' - -EXAMPLES = ''' -# Pass in a message -- name: Upload - netact_cm_command: - operation: "Upload" - opsname: 'Uploading_test' - dn: "PLMN-PLMN/MRBTS-746" - extra_opts: '-btsContentInUse true' - -- name: Provision - netact_cm_command: - operation: "Provision" - opsname: 'Provision_test' - dn: "PLMN-PLMN/MRBTS-746" - planName: 'mySiteTemplate' - type: 'actual' - createBackupPlan: true - backupPlanName: 'myBackupPlanName' - -- name: Export and fetching data from target - netact_cm_command: - operation: "Export" - opsname: 'Export_test' - planName: 'mySiteTemplate' - type: 'actual' - fileName: 'exportTest.xml' -- fetch: - src: /var/opt/nokia/oss/global/racops/export/exportTest.xml - dest: fetched - -- name: Import - netact_cm_command: - operation: "Import" - opsname: 'Import_test' - fileFormat: 'CSV' - type: 'plan' - fileName: 'myCSVFile' - planName: 'myPlanName' - extra_ops: 'enablePolicyPlans true' - -# fail the module -- name: Test failure of the module - netact_cm_command: - name: fail me -''' - -RETURN = ''' -original_message: - description: The original name param that was passed in - returned: Command line - type: str - sample: '/opt/oss/bin/racclimx.sh -op Upload -opsName Uploading_testi -DN PLMN-PLMN/MRBTS-746' -message: - description: The output message that the netact_cm_command module generates - returned: Command output message - type: str -changed: - description: data changed - returned: true if data is changed - type: bool -''' - -from ansible.module_utils.basic import AnsibleModule - -racclimx = '/opt/oss/bin/racclimx.sh' - - -def main(): - """ - Main module where option are handled and command is executed - :return: - """ - # define the available arguments/parameters that a user can pass to - # the module - module_args = dict( - operation=dict(type='str', required=True, - aliases=['op'], - choices=['Upload', 'Provision', 'Import', - 'Export', 'Provision_Mass_Modification']), - opsName=dict(type='str', required=False), - DN=dict(type='str', required=False), - WS=dict(type='str', required=False), - MR=dict(type='str', required=False), - - planName=dict(type='str', required=False), - typeOption=dict(type='str', required=False, aliases=['type'], - choices=['plan', 'actual', 'reference', 'template', 'siteTemplate']), - fileFormat=dict(type='str', required=False, choices=['CSV', 'RAML2', 'XLSX']), - fileName=dict(type='str', required=False), - createBackupPlan=dict(type='bool', required=False), - backupPlanName=dict(type='str', required=False), - inputFile=dict(type='str', required=False), - - verbose=dict(type='str', required=False), - extra_opts=dict(type='str', required=False) - ) - - # seed the result dict in the object - # we primarily care about changed and state - # change is if this module effectively modified the target - # state will include any data that you want your module to pass back - # for consumption, for example, in a subsequent task - result = dict( - changed=False, - original_message='', - cmd='', - message='' - ) - - # the AnsibleModule object will be our abstraction working with Ansible - # this includes instantiation, a couple of common attr would be the - # args/params passed to the execution, as well as if the module - # supports check mode - module = AnsibleModule( - argument_spec=module_args, - supports_check_mode=True - ) - - # if the user is working with this module in only check mode we do not - # want to make any changes to the environment, just return the current - # state with no modifications - if module.check_mode: - result['skipped'] = True - result['msg'] = 'check mode not (yet) supported for this module' - module.exit_json(**result) - - # manipulate or modify the state as needed (this is going to be the - # part where your module will do what it needs to do) - - operation = module.params.get('operation') - if not operation: - module.fail_json(msg='Operation not defined', **result) - - opsname = module.params.get('opsName') - dn = module.params.get('DN') - ws = module.params.get('WS') - mr = module.params.get('MR') - - planname = module.params.get('planName') - typeoption = module.params.get('typeOption') - fileformat = module.params.get('fileFormat') - filename = module.params.get('fileName') - - createBackupPlan = module.params.get('createBackupPlan') - backupPlanName = module.params.get('backupPlanName') - inputfile = module.params.get('inputFile') - - extra_opts = module.params.get('extra_opts') - verbose = module.params.get('verbose') - - # parameter checks - - command = [racclimx, '-op', operation] - - if opsname: - command.append('-opsName') - command.append(opsname) - - if dn: - command.append('-DN') - command.append(dn) - - if ws: - command.append('-WS') - command.append(ws) - - if mr: - command.append('-MR') - command.append(mr) - - if planname: - command.append('-planName') - command.append(planname) - - if typeoption: - command.append('-type') - command.append(typeoption) - - if fileformat: - command.append('-fileFormat') - command.append(fileformat) - - if filename: - command.append('-fileName') - command.append(filename) - - if createBackupPlan: - command.append('-createBackupPlan') - command.append('true') - - if backupPlanName: - command.append('-backupPlanName') - command.append(backupPlanName) - - if inputfile: - command.append('-inputFile') - command.append(inputfile) - - if extra_opts: - command = command + extra_opts.split(" ") - - if verbose: - if verbose == 'True': - command.append("-v") - - rc, out, err = module.run_command(command, check_rc=True) - if rc != 0: - result['changed'] = False - module.fail_json(msg=err) - else: - result['changed'] = True - result['original_message'] = out - result['cmd'] = command - result['message'] = out - - # in the event of a successful module execution, you will want to - # simple AnsibleModule.exit_json(), passing the key/value results - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netscaler/netscaler_cs_action.py b/plugins/modules/network/netscaler/netscaler_cs_action.py deleted file mode 100644 index 7f2e16a273..0000000000 --- a/plugins/modules/network/netscaler/netscaler_cs_action.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_cs_action -short_description: Manage content switching actions -description: - - Manage content switching actions - - This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - name: - description: - - >- - Name for the content switching action. Must begin with an ASCII alphanumeric or underscore C(_) - character, and must contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space C( ), colon - C(:), at sign C(@), equal sign C(=), and hyphen C(-) characters. Can be changed after the content - switching action is created. - - targetlbvserver: - description: - - "Name of the load balancing virtual server to which the content is switched." - - targetvserver: - description: - - "Name of the VPN virtual server to which the content is switched." - - targetvserverexpr: - description: - - "Information about this content switching action." - - comment: - description: - - "Comments associated with this cs action." - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -# lb_vserver_1 must have been already created with the netscaler_lb_vserver module - -- name: Configure netscaler content switching action - delegate_to: localhost - netscaler_cs_action: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - validate_certs: no - - state: present - - name: action-1 - targetlbvserver: lb_vserver_1 -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: "['message 1', 'message 2']" - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: "{ 'targetlbvserver': 'difference. ours: (str) server1 other: (str) server2' }" -''' - -import json - -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.cs.csaction import csaction - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import ( - ConfigProxy, - get_nitro_client, - netscaler_common_arguments, - log, loglines, - ensure_feature_is_enabled, - get_immutables_intersection -) - - -def action_exists(client, module): - if csaction.count_filtered(client, 'name:%s' % module.params['name']) > 0: - return True - else: - return False - - -def action_identical(client, module, csaction_proxy): - if len(diff_list(client, module, csaction_proxy)) == 0: - return True - else: - return False - - -def diff_list(client, module, csaction_proxy): - action_list = csaction.get_filtered(client, 'name:%s' % module.params['name']) - diff_list = csaction_proxy.diff_object(action_list[0]) - if False and 'targetvserverexpr' in diff_list: - json_value = json.loads(action_list[0].targetvserverexpr) - if json_value == module.params['targetvserverexpr']: - del diff_list['targetvserverexpr'] - return diff_list - - -def main(): - - module_specific_arguments = dict( - - name=dict(type='str'), - targetlbvserver=dict(type='str'), - targetvserverexpr=dict(type='str'), - comment=dict(type='str'), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - - argument_spec.update(module_specific_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - readwrite_attrs = [ - 'name', - 'targetlbvserver', - 'targetvserverexpr', - 'comment', - ] - readonly_attrs = [ - 'hits', - 'referencecount', - 'undefhits', - 'builtin', - ] - - immutable_attrs = [ - 'name', - 'targetvserverexpr', - ] - - transforms = { - } - - # Instantiate config proxy - csaction_proxy = ConfigProxy( - actual=csaction(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - - ensure_feature_is_enabled(client, 'CS') - # Apply appropriate state - if module.params['state'] == 'present': - log('Applying actions for state present') - if not action_exists(client, module): - if not module.check_mode: - csaction_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not action_identical(client, module, csaction_proxy): - - # Check if we try to change value of immutable attributes - immutables_changed = get_immutables_intersection(csaction_proxy, diff_list(client, module, csaction_proxy).keys()) - if immutables_changed != []: - module.fail_json( - msg='Cannot update immutable attributes %s' % (immutables_changed,), - diff=diff_list(client, module, csaction_proxy), - **module_result - ) - - if not module.check_mode: - csaction_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - log('Sanity checks for state present') - if not module.check_mode: - if not action_exists(client, module): - module.fail_json(msg='Content switching action does not exist', **module_result) - if not action_identical(client, module, csaction_proxy): - module.fail_json( - msg='Content switching action differs from configured', - diff=diff_list(client, module, csaction_proxy), - **module_result - ) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if action_exists(client, module): - if not module.check_mode: - csaction_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state absent') - if action_exists(client, module): - module.fail_json(msg='Content switching action still exists', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_cs_policy.py b/plugins/modules/network/netscaler/netscaler_cs_policy.py deleted file mode 100644 index f31452d11f..0000000000 --- a/plugins/modules/network/netscaler/netscaler_cs_policy.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_cs_policy -short_description: Manage content switching policy -description: - - Manage content switching policy. - - "This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance." - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - policyname: - description: - - >- - Name for the content switching policy. Must begin with an ASCII alphanumeric or underscore C(_) - character, and must contain only ASCII alphanumeric, underscore, hash C(#), period C(.), space C( ), colon - C(:), at sign C(@), equal sign C(=), and hyphen C(-) characters. Cannot be changed after a policy is - created. - - "The following requirement applies only to the NetScaler CLI:" - - >- - If the name includes one or more spaces, enclose the name in double or single quotation marks (for - example, my policy or my policy). - - "Minimum length = 1" - - url: - description: - - >- - URL string that is matched with the URL of a request. Can contain a wildcard character. Specify the - string value in the following format: C([[prefix] [*]] [.suffix]). - - "Minimum length = 1" - - "Maximum length = 208" - - rule: - description: - - >- - Expression, or name of a named expression, against which traffic is evaluated. Written in the classic - or default syntax. - - "Note:" - - >- - Maximum length of a string literal in the expression is 255 characters. A longer string can be split - into smaller strings of up to 255 characters each, and the smaller strings concatenated with the + - operator. For example, you can create a 500-character string as follows: '"" + ""' - - domain: - description: - - "The domain name. The string value can range to 63 characters." - - "Minimum length = 1" - - action: - description: - - >- - Content switching action that names the target load balancing virtual server to which the traffic is - switched. - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -- name: Create url cs policy - delegate_to: localhost - netscaler_cs_policy: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - validate_certs: no - - state: present - - policyname: policy_1 - url: /example/ -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: ['message 1', 'message 2'] - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Could not load nitro python sdk" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: { 'url': 'difference. ours: (str) example1 other: (str) /example1' } -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import (ConfigProxy, get_nitro_client, netscaler_common_arguments, - log, loglines, ensure_feature_is_enabled) -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.cs.cspolicy import cspolicy - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - - -def policy_exists(client, module): - log('Checking if policy exists') - if cspolicy.count_filtered(client, 'policyname:%s' % module.params['policyname']) > 0: - return True - else: - return False - - -def policy_identical(client, module, cspolicy_proxy): - log('Checking if defined policy is identical to configured') - if cspolicy.count_filtered(client, 'policyname:%s' % module.params['policyname']) == 0: - return False - policy_list = cspolicy.get_filtered(client, 'policyname:%s' % module.params['policyname']) - diff_dict = cspolicy_proxy.diff_object(policy_list[0]) - if 'ip' in diff_dict: - del diff_dict['ip'] - if len(diff_dict) == 0: - return True - else: - return False - - -def diff_list(client, module, cspolicy_proxy): - policy_list = cspolicy.get_filtered(client, 'policyname:%s' % module.params['policyname']) - return cspolicy_proxy.diff_object(policy_list[0]) - - -def main(): - - module_specific_arguments = dict( - policyname=dict(type='str'), - url=dict(type='str'), - rule=dict(type='str'), - domain=dict(type='str'), - action=dict(type='str'), - ) - - hand_inserted_arguments = dict( - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - argument_spec.update(module_specific_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - readwrite_attrs = [ - 'policyname', - 'url', - 'rule', - 'domain', - 'action', - ] - readonly_attrs = [ - 'vstype', - 'hits', - 'bindhits', - 'labelname', - 'labeltype', - 'priority', - 'activepolicy', - 'cspolicytype', - ] - - transforms = { - } - - # Instantiate config proxy - cspolicy_proxy = ConfigProxy( - actual=cspolicy(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - transforms=transforms, - ) - - try: - ensure_feature_is_enabled(client, 'CS') - - # Apply appropriate state - if module.params['state'] == 'present': - log('Sanity checks for state present') - if not policy_exists(client, module): - if not module.check_mode: - cspolicy_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not policy_identical(client, module, cspolicy_proxy): - if not module.check_mode: - cspolicy_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state present') - if not policy_exists(client, module): - module.fail_json(msg='Policy does not exist', **module_result) - if not policy_identical(client, module, cspolicy_proxy): - module.fail_json(msg='Policy differs from configured', diff=diff_list(client, module, cspolicy_proxy), **module_result) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if policy_exists(client, module): - if not module.check_mode: - cspolicy_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state absent') - if policy_exists(client, module): - module.fail_json(msg='Policy still exists', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_cs_vserver.py b/plugins/modules/network/netscaler/netscaler_cs_vserver.py deleted file mode 100644 index 7fbc495cc0..0000000000 --- a/plugins/modules/network/netscaler/netscaler_cs_vserver.py +++ /dev/null @@ -1,1307 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_cs_vserver -short_description: Manage content switching vserver -description: - - Manage content switching vserver - - This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - name: - description: - - >- - Name for the content switching virtual server. Must begin with an ASCII alphanumeric or underscore - C(_) character, and must contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space, - colon C(:), at sign C(@), equal sign C(=), and hyphen C(-) characters. - - "Cannot be changed after the CS virtual server is created." - - "Minimum length = 1" - - td: - description: - - >- - Integer value that uniquely identifies the traffic domain in which you want to configure the entity. - If you do not specify an ID, the entity becomes part of the default traffic domain, which has an ID - of 0. - - "Minimum value = 0" - - "Maximum value = 4094" - - servicetype: - choices: - - 'HTTP' - - 'SSL' - - 'TCP' - - 'FTP' - - 'RTSP' - - 'SSL_TCP' - - 'UDP' - - 'DNS' - - 'SIP_UDP' - - 'SIP_TCP' - - 'SIP_SSL' - - 'ANY' - - 'RADIUS' - - 'RDP' - - 'MYSQL' - - 'MSSQL' - - 'DIAMETER' - - 'SSL_DIAMETER' - - 'DNS_TCP' - - 'ORACLE' - - 'SMPP' - description: - - "Protocol used by the virtual server." - - ipv46: - description: - - "IP address of the content switching virtual server." - - "Minimum length = 1" - - targettype: - choices: - - 'GSLB' - description: - - "Virtual server target type." - - ippattern: - description: - - >- - IP address pattern, in dotted decimal notation, for identifying packets to be accepted by the virtual - server. The IP Mask parameter specifies which part of the destination IP address is matched against - the pattern. Mutually exclusive with the IP Address parameter. - - >- - For example, if the IP pattern assigned to the virtual server is C(198.51.100.0) and the IP mask is - C(255.255.240.0) (a forward mask), the first 20 bits in the destination IP addresses are matched with - the first 20 bits in the pattern. The virtual server accepts requests with IP addresses that range - from 198.51.96.1 to 198.51.111.254. You can also use a pattern such as C(0.0.2.2) and a mask such as - C(0.0.255.255) (a reverse mask). - - >- - If a destination IP address matches more than one IP pattern, the pattern with the longest match is - selected, and the associated virtual server processes the request. For example, if the virtual - servers, C(vs1) and C(vs2), have the same IP pattern, C(0.0.100.128), but different IP masks of C(0.0.255.255) - and C(0.0.224.255), a destination IP address of 198.51.100.128 has the longest match with the IP pattern - of C(vs1). If a destination IP address matches two or more virtual servers to the same extent, the - request is processed by the virtual server whose port number matches the port number in the request. - - ipmask: - description: - - >- - IP mask, in dotted decimal notation, for the IP Pattern parameter. Can have leading or trailing - non-zero octets (for example, C(255.255.240.0) or C(0.0.255.255)). Accordingly, the mask specifies whether - the first n bits or the last n bits of the destination IP address in a client request are to be - matched with the corresponding bits in the IP pattern. The former is called a forward mask. The - latter is called a reverse mask. - - range: - description: - - >- - Number of consecutive IP addresses, starting with the address specified by the IP Address parameter, - to include in a range of addresses assigned to this virtual server. - - "Minimum value = C(1)" - - "Maximum value = C(254)" - - port: - description: - - "Port number for content switching virtual server." - - "Minimum value = 1" - - "Range C(1) - C(65535)" - - "* in CLI is represented as 65535 in NITRO API" - - stateupdate: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Enable state updates for a specific content switching virtual server. By default, the Content - Switching virtual server is always UP, regardless of the state of the Load Balancing virtual servers - bound to it. This parameter interacts with the global setting as follows: - - "Global Level | Vserver Level | Result" - - "enabled enabled enabled" - - "enabled disabled enabled" - - "disabled enabled enabled" - - "disabled disabled disabled" - - >- - If you want to enable state updates for only some content switching virtual servers, be sure to - disable the state update parameter. - - cacheable: - description: - - >- - Use this option to specify whether a virtual server, used for load balancing or content switching, - routes requests to the cache redirection virtual server before sending it to the configured servers. - type: bool - - redirecturl: - description: - - >- - URL to which traffic is redirected if the virtual server becomes unavailable. The service type of the - virtual server should be either C(HTTP) or C(SSL). - - >- - Caution: Make sure that the domain in the URL does not match the domain specified for a content - switching policy. If it does, requests are continuously redirected to the unavailable virtual server. - - "Minimum length = 1" - - clttimeout: - description: - - "Idle time, in seconds, after which the client connection is terminated. The default values are:" - - "Minimum value = C(0)" - - "Maximum value = C(31536000)" - - precedence: - choices: - - 'RULE' - - 'URL' - description: - - >- - Type of precedence to use for both RULE-based and URL-based policies on the content switching virtual - server. With the default C(RULE) setting, incoming requests are evaluated against the rule-based - content switching policies. If none of the rules match, the URL in the request is evaluated against - the URL-based content switching policies. - - casesensitive: - description: - - >- - Consider case in URLs (for policies that use URLs instead of RULES). For example, with the C(on) - setting, the URLs /a/1.html and /A/1.HTML are treated differently and can have different targets (set - by content switching policies). With the C(off) setting, /a/1.html and /A/1.HTML are switched to the - same target. - type: bool - - somethod: - choices: - - 'CONNECTION' - - 'DYNAMICCONNECTION' - - 'BANDWIDTH' - - 'HEALTH' - - 'NONE' - description: - - >- - Type of spillover used to divert traffic to the backup virtual server when the primary virtual server - reaches the spillover threshold. Connection spillover is based on the number of connections. - Bandwidth spillover is based on the total Kbps of incoming and outgoing traffic. - - sopersistence: - choices: - - 'enabled' - - 'disabled' - description: - - "Maintain source-IP based persistence on primary and backup virtual servers." - - sopersistencetimeout: - description: - - "Time-out value, in minutes, for spillover persistence." - - "Minimum value = C(2)" - - "Maximum value = C(1440)" - - sothreshold: - description: - - >- - Depending on the spillover method, the maximum number of connections or the maximum total bandwidth - (Kbps) that a virtual server can handle before spillover occurs. - - "Minimum value = C(1)" - - "Maximum value = C(4294967287)" - - sobackupaction: - choices: - - 'DROP' - - 'ACCEPT' - - 'REDIRECT' - description: - - >- - Action to be performed if spillover is to take effect, but no backup chain to spillover is usable or - exists. - - redirectportrewrite: - choices: - - 'enabled' - - 'disabled' - description: - - "State of port rewrite while performing HTTP redirect." - - downstateflush: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Flush all active transactions associated with a virtual server whose state transitions from UP to - DOWN. Do not enable this option for applications that must complete their transactions. - - backupvserver: - description: - - >- - Name of the backup virtual server that you are configuring. Must begin with an ASCII alphanumeric or - underscore C(_) character, and must contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), - space C( ), colon C(:), at sign C(@), equal sign C(=), and hyphen C(-) characters. Can be changed after the - backup virtual server is created. You can assign a different backup virtual server or rename the - existing virtual server. - - "Minimum length = 1" - - disableprimaryondown: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Continue forwarding the traffic to backup virtual server even after the primary server comes UP from - the DOWN state. - - insertvserveripport: - choices: - - 'OFF' - - 'VIPADDR' - - 'V6TOV4MAPPING' - description: - - >- - Insert the virtual server's VIP address and port number in the request header. Available values - function as follows: - - "C(VIPADDR) - Header contains the vserver's IP address and port number without any translation." - - "C(OFF) - The virtual IP and port header insertion option is disabled." - - >- - C(V6TOV4MAPPING) - Header contains the mapped IPv4 address corresponding to the IPv6 address of the - vserver and the port number. An IPv6 address can be mapped to a user-specified IPv4 address using the - set ns ip6 command. - - vipheader: - description: - - "Name of virtual server IP and port header, for use with the VServer IP Port Insertion parameter." - - "Minimum length = 1" - - rtspnat: - description: - - "Enable network address translation (NAT) for real-time streaming protocol (RTSP) connections." - type: bool - - authenticationhost: - description: - - >- - FQDN of the authentication virtual server. The service type of the virtual server should be either - C(HTTP) or C(SSL). - - "Minimum length = 3" - - "Maximum length = 252" - - authentication: - description: - - "Authenticate users who request a connection to the content switching virtual server." - type: bool - - listenpolicy: - description: - - >- - String specifying the listen policy for the content switching virtual server. Can be either the name - of an existing expression or an in-line expression. - - authn401: - description: - - "Enable HTTP 401-response based authentication." - type: bool - - authnvsname: - description: - - >- - Name of authentication virtual server that authenticates the incoming user requests to this content - switching virtual server. . - - "Minimum length = 1" - - "Maximum length = 252" - - push: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Process traffic with the push virtual server that is bound to this content switching virtual server - (specified by the Push VServer parameter). The service type of the push virtual server should be - either C(HTTP) or C(SSL). - - pushvserver: - description: - - >- - Name of the load balancing virtual server, of type C(PUSH) or C(SSL_PUSH), to which the server pushes - updates received on the client-facing load balancing virtual server. - - "Minimum length = 1" - - pushlabel: - description: - - >- - Expression for extracting the label from the response received from server. This string can be either - an existing rule name or an inline expression. The service type of the virtual server should be - either C(HTTP) or C(SSL). - - pushmulticlients: - description: - - >- - Allow multiple Web 2.0 connections from the same client to connect to the virtual server and expect - updates. - type: bool - - tcpprofilename: - description: - - "Name of the TCP profile containing TCP configuration settings for the virtual server." - - "Minimum length = 1" - - "Maximum length = 127" - - httpprofilename: - description: - - >- - Name of the HTTP profile containing HTTP configuration settings for the virtual server. The service - type of the virtual server should be either C(HTTP) or C(SSL). - - "Minimum length = 1" - - "Maximum length = 127" - - dbprofilename: - description: - - "Name of the DB profile." - - "Minimum length = 1" - - "Maximum length = 127" - - oracleserverversion: - choices: - - '10G' - - '11G' - description: - - "Oracle server version." - - comment: - description: - - "Information about this virtual server." - - mssqlserverversion: - choices: - - '70' - - '2000' - - '2000SP1' - - '2005' - - '2008' - - '2008R2' - - '2012' - - '2014' - description: - - "The version of the MSSQL server." - - l2conn: - description: - - "Use L2 Parameters to identify a connection." - type: bool - - mysqlprotocolversion: - description: - - "The protocol version returned by the mysql vserver." - - mysqlserverversion: - description: - - "The server version string returned by the mysql vserver." - - "Minimum length = 1" - - "Maximum length = 31" - - mysqlcharacterset: - description: - - "The character set returned by the mysql vserver." - - mysqlservercapabilities: - description: - - "The server capabilities returned by the mysql vserver." - - appflowlog: - choices: - - 'enabled' - - 'disabled' - description: - - "Enable logging appflow flow information." - - netprofile: - description: - - "The name of the network profile." - - "Minimum length = 1" - - "Maximum length = 127" - - icmpvsrresponse: - choices: - - 'PASSIVE' - - 'ACTIVE' - description: - - "Can be active or passive." - - rhistate: - choices: - - 'PASSIVE' - - 'ACTIVE' - description: - - "A host route is injected according to the setting on the virtual servers" - - >- - * If set to C(PASSIVE) on all the virtual servers that share the IP address, the appliance always - injects the hostroute. - - >- - * If set to C(ACTIVE) on all the virtual servers that share the IP address, the appliance injects even - if one virtual server is UP. - - >- - * If set to C(ACTIVE) on some virtual servers and C(PASSIVE) on the others, the appliance, injects even if - one virtual server set to C(ACTIVE) is UP. - - authnprofile: - description: - - "Name of the authentication profile to be used when authentication is turned on." - - dnsprofilename: - description: - - >- - Name of the DNS profile to be associated with the VServer. DNS profile properties will applied to the - transactions processed by a VServer. This parameter is valid only for DNS and DNS-TCP VServers. - - "Minimum length = 1" - - "Maximum length = 127" - - domainname: - description: - - "Domain name for which to change the time to live (TTL) and/or backup service IP address." - - "Minimum length = 1" - - ttl: - description: - - "." - - "Minimum value = C(1)" - - backupip: - description: - - "." - - "Minimum length = 1" - - cookiedomain: - description: - - "." - - "Minimum length = 1" - - cookietimeout: - description: - - "." - - "Minimum value = C(0)" - - "Maximum value = C(1440)" - - sitedomainttl: - description: - - "." - - "Minimum value = C(1)" - - lbvserver: - description: - - The default Load Balancing virtual server. - - ssl_certkey: - description: - - The name of the ssl certificate that is bound to this service. - - The ssl certificate must already exist. - - Creating the certificate can be done with the M(netscaler_ssl_certkey) module. - - This option is only applicable only when C(servicetype) is C(SSL). - - disabled: - description: - - When set to C(yes) the cs vserver will be disabled. - - When set to C(no) the cs vserver will be enabled. - - >- - Note that due to limitations of the underlying NITRO API a C(disabled) state change alone - does not cause the module result to report a changed status. - type: bool - default: 'no' - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -# policy_1 must have been already created with the netscaler_cs_policy module -# lbvserver_1 must have been already created with the netscaler_lb_vserver module - -- name: Setup content switching vserver - delegate_to: localhost - netscaler_cs_vserver: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - - state: present - - name: cs_vserver_1 - ipv46: 192.168.1.1 - port: 80 - servicetype: HTTP - - policybindings: - - policyname: policy_1 - targetlbvserver: lbvserver_1 -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: ['message 1', 'message 2'] - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: { 'clttimeout': 'difference. ours: (float) 100.0 other: (float) 60.0' } -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import ( - ConfigProxy, - get_nitro_client, - netscaler_common_arguments, - log, - loglines, - ensure_feature_is_enabled, - get_immutables_intersection -) -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.cs.csvserver import csvserver - from nssrc.com.citrix.netscaler.nitro.resource.config.cs.csvserver_lbvserver_binding import csvserver_lbvserver_binding - from nssrc.com.citrix.netscaler.nitro.resource.config.cs.csvserver_cspolicy_binding import csvserver_cspolicy_binding - from nssrc.com.citrix.netscaler.nitro.resource.config.ssl.sslvserver_sslcertkey_binding import sslvserver_sslcertkey_binding - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - - -def cs_vserver_exists(client, module): - if csvserver.count_filtered(client, 'name:%s' % module.params['name']) > 0: - return True - else: - return False - - -def cs_vserver_identical(client, module, csvserver_proxy): - csvserver_list = csvserver.get_filtered(client, 'name:%s' % module.params['name']) - diff_dict = csvserver_proxy.diff_object(csvserver_list[0]) - if len(diff_dict) == 0: - return True - else: - return False - - -def get_configured_policybindings(client, module): - log('Getting configured policy bindigs') - bindings = {} - if module.params['policybindings'] is None: - return bindings - - for binding in module.params['policybindings']: - binding['name'] = module.params['name'] - key = binding['policyname'] - binding_proxy = ConfigProxy( - actual=csvserver_cspolicy_binding(), - client=client, - readwrite_attrs=[ - 'priority', - 'bindpoint', - 'policyname', - 'labelname', - 'gotopriorityexpression', - 'targetlbvserver', - 'name', - 'invoke', - 'labeltype', - ], - readonly_attrs=[], - attribute_values_dict=binding - ) - bindings[key] = binding_proxy - return bindings - - -def get_default_lb_vserver(client, module): - try: - default_lb_vserver = csvserver_lbvserver_binding.get(client, module.params['name']) - return default_lb_vserver[0] - except nitro_exception as e: - if e.errorcode == 258: - return csvserver_lbvserver_binding() - else: - raise - - -def default_lb_vserver_identical(client, module): - d = get_default_lb_vserver(client, module) - configured = ConfigProxy( - actual=csvserver_lbvserver_binding(), - client=client, - readwrite_attrs=[ - 'name', - 'lbvserver', - ], - attribute_values_dict={ - 'name': module.params['name'], - 'lbvserver': module.params['lbvserver'], - } - ) - log('default lb vserver %s' % ((d.name, d.lbvserver),)) - if d.name is None and module.params['lbvserver'] is None: - log('Default lb vserver identical missing') - return True - elif d.name is not None and module.params['lbvserver'] is None: - log('Default lb vserver needs removing') - return False - elif configured.has_equal_attributes(d): - log('Default lb vserver identical') - return True - else: - log('Default lb vserver not identical') - return False - - -def sync_default_lb_vserver(client, module): - d = get_default_lb_vserver(client, module) - - if module.params['lbvserver'] is not None: - configured = ConfigProxy( - actual=csvserver_lbvserver_binding(), - client=client, - readwrite_attrs=[ - 'name', - 'lbvserver', - ], - attribute_values_dict={ - 'name': module.params['name'], - 'lbvserver': module.params['lbvserver'], - } - ) - - if not configured.has_equal_attributes(d): - if d.name is not None: - log('Deleting default lb vserver %s' % d.lbvserver) - csvserver_lbvserver_binding.delete(client, d) - log('Adding default lb vserver %s' % configured.lbvserver) - configured.add() - else: - if d.name is not None: - log('Deleting default lb vserver %s' % d.lbvserver) - csvserver_lbvserver_binding.delete(client, d) - - -def get_actual_policybindings(client, module): - log('Getting actual policy bindigs') - bindings = {} - try: - count = csvserver_cspolicy_binding.count(client, name=module.params['name']) - if count == 0: - return bindings - except nitro_exception as e: - if e.errorcode == 258: - return bindings - else: - raise - - for binding in csvserver_cspolicy_binding.get(client, name=module.params['name']): - key = binding.policyname - bindings[key] = binding - - return bindings - - -def cs_policybindings_identical(client, module): - log('Checking policy bindings identical') - actual_bindings = get_actual_policybindings(client, module) - configured_bindings = get_configured_policybindings(client, module) - - actual_keyset = set(actual_bindings.keys()) - configured_keyset = set(configured_bindings.keys()) - if len(actual_keyset ^ configured_keyset) > 0: - return False - - # Compare item to item - for key in actual_bindings.keys(): - configured_binding_proxy = configured_bindings[key] - actual_binding_object = actual_bindings[key] - if not configured_binding_proxy.has_equal_attributes(actual_binding_object): - return False - - # Fallthrough to success - return True - - -def sync_cs_policybindings(client, module): - log('Syncing cs policybindings') - actual_bindings = get_actual_policybindings(client, module) - configured_bindings = get_configured_policybindings(client, module) - - # Delete actual bindings not in configured - delete_keys = list(set(actual_bindings.keys()) - set(configured_bindings.keys())) - for key in delete_keys: - log('Deleting binding for policy %s' % key) - csvserver_cspolicy_binding.delete(client, actual_bindings[key]) - - # Add configured bindings not in actual - add_keys = list(set(configured_bindings.keys()) - set(actual_bindings.keys())) - for key in add_keys: - log('Adding binding for policy %s' % key) - configured_bindings[key].add() - - # Update existing if changed - modify_keys = list(set(configured_bindings.keys()) & set(actual_bindings.keys())) - for key in modify_keys: - if not configured_bindings[key].has_equal_attributes(actual_bindings[key]): - log('Updating binding for policy %s' % key) - csvserver_cspolicy_binding.delete(client, actual_bindings[key]) - configured_bindings[key].add() - - -def ssl_certkey_bindings_identical(client, module): - log('Checking if ssl cert key bindings are identical') - vservername = module.params['name'] - if sslvserver_sslcertkey_binding.count(client, vservername) == 0: - bindings = [] - else: - bindings = sslvserver_sslcertkey_binding.get(client, vservername) - - if module.params['ssl_certkey'] is None: - if len(bindings) == 0: - return True - else: - return False - else: - certificate_list = [item.certkeyname for item in bindings] - if certificate_list == [module.params['ssl_certkey']]: - return True - else: - return False - - -def ssl_certkey_bindings_sync(client, module): - log('Syncing certkey bindings') - vservername = module.params['name'] - if sslvserver_sslcertkey_binding.count(client, vservername) == 0: - bindings = [] - else: - bindings = sslvserver_sslcertkey_binding.get(client, vservername) - - # Delete existing bindings - for binding in bindings: - log('Deleting existing binding for certkey %s' % binding.certkeyname) - sslvserver_sslcertkey_binding.delete(client, binding) - - # Add binding if appropriate - if module.params['ssl_certkey'] is not None: - log('Adding binding for certkey %s' % module.params['ssl_certkey']) - binding = sslvserver_sslcertkey_binding() - binding.vservername = module.params['name'] - binding.certkeyname = module.params['ssl_certkey'] - sslvserver_sslcertkey_binding.add(client, binding) - - -def diff_list(client, module, csvserver_proxy): - csvserver_list = csvserver.get_filtered(client, 'name:%s' % module.params['name']) - return csvserver_proxy.diff_object(csvserver_list[0]) - - -def do_state_change(client, module, csvserver_proxy): - if module.params['disabled']: - log('Disabling cs vserver') - result = csvserver.disable(client, csvserver_proxy.actual) - else: - log('Enabling cs vserver') - result = csvserver.enable(client, csvserver_proxy.actual) - return result - - -def main(): - - module_specific_arguments = dict( - - name=dict(type='str'), - td=dict(type='float'), - servicetype=dict( - type='str', - choices=[ - 'HTTP', - 'SSL', - 'TCP', - 'FTP', - 'RTSP', - 'SSL_TCP', - 'UDP', - 'DNS', - 'SIP_UDP', - 'SIP_TCP', - 'SIP_SSL', - 'ANY', - 'RADIUS', - 'RDP', - 'MYSQL', - 'MSSQL', - 'DIAMETER', - 'SSL_DIAMETER', - 'DNS_TCP', - 'ORACLE', - 'SMPP' - ] - ), - - ipv46=dict(type='str'), - dnsrecordtype=dict( - type='str', - choices=[ - 'A', - 'AAAA', - 'CNAME', - 'NAPTR', - ] - ), - ippattern=dict(type='str'), - ipmask=dict(type='str'), - range=dict(type='float'), - port=dict(type='int'), - stateupdate=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - cacheable=dict(type='bool'), - redirecturl=dict(type='str'), - clttimeout=dict(type='float'), - precedence=dict( - type='str', - choices=[ - 'RULE', - 'URL', - ] - ), - casesensitive=dict(type='bool'), - somethod=dict( - type='str', - choices=[ - 'CONNECTION', - 'DYNAMICCONNECTION', - 'BANDWIDTH', - 'HEALTH', - 'NONE', - ] - ), - sopersistence=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - sopersistencetimeout=dict(type='float'), - sothreshold=dict(type='float'), - sobackupaction=dict( - type='str', - choices=[ - 'DROP', - 'ACCEPT', - 'REDIRECT', - ] - ), - redirectportrewrite=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - downstateflush=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - disableprimaryondown=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - insertvserveripport=dict( - type='str', - choices=[ - 'OFF', - 'VIPADDR', - 'V6TOV4MAPPING', - ] - ), - vipheader=dict(type='str'), - rtspnat=dict(type='bool'), - authenticationhost=dict(type='str'), - authentication=dict(type='bool'), - listenpolicy=dict(type='str'), - authn401=dict(type='bool'), - authnvsname=dict(type='str'), - push=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - pushvserver=dict(type='str'), - pushlabel=dict(type='str'), - pushmulticlients=dict(type='bool'), - tcpprofilename=dict(type='str'), - httpprofilename=dict(type='str'), - dbprofilename=dict(type='str'), - oracleserverversion=dict( - type='str', - choices=[ - '10G', - '11G', - ] - ), - comment=dict(type='str'), - mssqlserverversion=dict( - type='str', - choices=[ - '70', - '2000', - '2000SP1', - '2005', - '2008', - '2008R2', - '2012', - '2014', - ] - ), - l2conn=dict(type='bool'), - mysqlprotocolversion=dict(type='float'), - mysqlserverversion=dict(type='str'), - mysqlcharacterset=dict(type='float'), - mysqlservercapabilities=dict(type='float'), - appflowlog=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - netprofile=dict(type='str'), - icmpvsrresponse=dict( - type='str', - choices=[ - 'PASSIVE', - 'ACTIVE', - ] - ), - rhistate=dict( - type='str', - choices=[ - 'PASSIVE', - 'ACTIVE', - ] - ), - authnprofile=dict(type='str'), - dnsprofilename=dict(type='str'), - ) - - hand_inserted_arguments = dict( - policybindings=dict(type='list'), - ssl_certkey=dict(type='str'), - disabled=dict( - type='bool', - default=False - ), - lbvserver=dict(type='str'), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - - argument_spec.update(module_specific_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - readwrite_attrs = [ - 'name', - 'td', - 'servicetype', - 'ipv46', - 'dnsrecordtype', - 'ippattern', - 'ipmask', - 'range', - 'port', - 'stateupdate', - 'cacheable', - 'redirecturl', - 'clttimeout', - 'precedence', - 'casesensitive', - 'somethod', - 'sopersistence', - 'sopersistencetimeout', - 'sothreshold', - 'sobackupaction', - 'redirectportrewrite', - 'downstateflush', - 'disableprimaryondown', - 'insertvserveripport', - 'vipheader', - 'rtspnat', - 'authenticationhost', - 'authentication', - 'listenpolicy', - 'authn401', - 'authnvsname', - 'push', - 'pushvserver', - 'pushlabel', - 'pushmulticlients', - 'tcpprofilename', - 'httpprofilename', - 'dbprofilename', - 'oracleserverversion', - 'comment', - 'mssqlserverversion', - 'l2conn', - 'mysqlprotocolversion', - 'mysqlserverversion', - 'mysqlcharacterset', - 'mysqlservercapabilities', - 'appflowlog', - 'netprofile', - 'icmpvsrresponse', - 'rhistate', - 'authnprofile', - 'dnsprofilename', - ] - - readonly_attrs = [ - 'ip', - 'value', - 'ngname', - 'type', - 'curstate', - 'sc', - 'status', - 'cachetype', - 'redirect', - 'homepage', - 'dnsvservername', - 'domain', - 'policyname', - 'servicename', - 'weight', - 'cachevserver', - 'targetvserver', - 'priority', - 'url', - 'gotopriorityexpression', - 'bindpoint', - 'invoke', - 'labeltype', - 'labelname', - 'gt2gb', - 'statechangetimesec', - 'statechangetimemsec', - 'tickssincelaststatechange', - 'ruletype', - 'lbvserver', - 'targetlbvserver', - ] - - immutable_attrs = [ - 'name', - 'td', - 'servicetype', - 'ipv46', - 'targettype', - 'range', - 'port', - 'state', - 'vipheader', - 'newname', - ] - - transforms = { - 'cacheable': ['bool_yes_no'], - 'rtspnat': ['bool_on_off'], - 'authn401': ['bool_on_off'], - 'casesensitive': ['bool_on_off'], - 'authentication': ['bool_on_off'], - 'l2conn': ['bool_on_off'], - 'pushmulticlients': ['bool_yes_no'], - 'stateupdate': [lambda v: v.upper()], - 'sopersistence': [lambda v: v.upper()], - 'redirectportrewrite': [lambda v: v.upper()], - 'downstateflush': [lambda v: v.upper()], - 'disableprimaryondown': [lambda v: v.upper()], - 'push': [lambda v: v.upper()], - 'appflowlog': [lambda v: v.upper()], - } - - # Instantiate config proxy - csvserver_proxy = ConfigProxy( - actual=csvserver(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - ensure_feature_is_enabled(client, 'CS') - - # Apply appropriate state - if module.params['state'] == 'present': - log('Applying actions for state present') - if not cs_vserver_exists(client, module): - if not module.check_mode: - csvserver_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not cs_vserver_identical(client, module, csvserver_proxy): - - # Check if we try to change value of immutable attributes - immutables_changed = get_immutables_intersection(csvserver_proxy, diff_list(client, module, csvserver_proxy).keys()) - if immutables_changed != []: - module.fail_json( - msg='Cannot update immutable attributes %s' % (immutables_changed,), - diff=diff_list(client, module, csvserver_proxy), - **module_result - ) - - if not module.check_mode: - csvserver_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Check policybindings - if not cs_policybindings_identical(client, module): - if not module.check_mode: - sync_cs_policybindings(client, module) - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - - if module.params['servicetype'] != 'SSL' and module.params['ssl_certkey'] is not None: - module.fail_json(msg='ssl_certkey is applicable only to SSL vservers', **module_result) - - # Check ssl certkey bindings - if module.params['servicetype'] == 'SSL': - if not ssl_certkey_bindings_identical(client, module): - if not module.check_mode: - ssl_certkey_bindings_sync(client, module) - - module_result['changed'] = True - - # Check default lb vserver - if not default_lb_vserver_identical(client, module): - if not module.check_mode: - sync_default_lb_vserver(client, module) - module_result['changed'] = True - - if not module.check_mode: - res = do_state_change(client, module, csvserver_proxy) - if res.errorcode != 0: - msg = 'Error when setting disabled state. errorcode: %s message: %s' % (res.errorcode, res.message) - module.fail_json(msg=msg, **module_result) - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state present') - if not cs_vserver_exists(client, module): - module.fail_json(msg='CS vserver does not exist', **module_result) - if not cs_vserver_identical(client, module, csvserver_proxy): - module.fail_json(msg='CS vserver differs from configured', diff=diff_list(client, module, csvserver_proxy), **module_result) - if not cs_policybindings_identical(client, module): - module.fail_json(msg='Policy bindings differ') - - if module.params['servicetype'] == 'SSL': - if not ssl_certkey_bindings_identical(client, module): - module.fail_json(msg='sll certkey bindings not identical', **module_result) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if cs_vserver_exists(client, module): - if not module.check_mode: - csvserver_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state absent') - if cs_vserver_exists(client, module): - module.fail_json(msg='CS vserver still exists', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_gslb_service.py b/plugins/modules/network/netscaler/netscaler_gslb_service.py deleted file mode 100644 index 2d2e0f8296..0000000000 --- a/plugins/modules/network/netscaler/netscaler_gslb_service.py +++ /dev/null @@ -1,696 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_gslb_service -short_description: Manage gslb service entities in Netscaler. -description: - - Manage gslb service entities in Netscaler. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - servicename: - description: - - >- - Name for the GSLB service. Must begin with an ASCII alphanumeric or underscore C(_) character, and - must contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space, colon C(:), at C(@), - equals C(=), and hyphen C(-) characters. Can be changed after the GSLB service is created. - - >- - - "Minimum length = 1" - - cnameentry: - description: - - "Canonical name of the GSLB service. Used in CNAME-based GSLB." - - "Minimum length = 1" - - - servername: - description: - - "Name of the server hosting the GSLB service." - - "Minimum length = 1" - - servicetype: - choices: - - 'HTTP' - - 'FTP' - - 'TCP' - - 'UDP' - - 'SSL' - - 'SSL_BRIDGE' - - 'SSL_TCP' - - 'NNTP' - - 'ANY' - - 'SIP_UDP' - - 'SIP_TCP' - - 'SIP_SSL' - - 'RADIUS' - - 'RDP' - - 'RTSP' - - 'MYSQL' - - 'MSSQL' - - 'ORACLE' - description: - - "Type of service to create." - - port: - description: - - "Port on which the load balancing entity represented by this GSLB service listens." - - "Minimum value = 1" - - "Range 1 - 65535" - - "* in CLI is represented as 65535 in NITRO API" - - publicip: - description: - - >- - The public IP address that a NAT device translates to the GSLB service's private IP address. - Optional. - - publicport: - description: - - >- - The public port associated with the GSLB service's public IP address. The port is mapped to the - service's private port number. Applicable to the local GSLB service. Optional. - - maxclient: - description: - - >- - The maximum number of open connections that the service can support at any given time. A GSLB service - whose connection count reaches the maximum is not considered when a GSLB decision is made, until the - connection count drops below the maximum. - - "Minimum value = C(0)" - - "Maximum value = C(4294967294)" - - healthmonitor: - description: - - "Monitor the health of the GSLB service." - type: bool - - sitename: - description: - - "Name of the GSLB site to which the service belongs." - - "Minimum length = 1" - - cip: - choices: - - 'enabled' - - 'disabled' - description: - - >- - In the request that is forwarded to the GSLB service, insert a header that stores the client's IP - address. Client IP header insertion is used in connection-proxy based site persistence. - - cipheader: - description: - - >- - Name for the HTTP header that stores the client's IP address. Used with the Client IP option. If - client IP header insertion is enabled on the service and a name is not specified for the header, the - NetScaler appliance uses the name specified by the cipHeader parameter in the set ns param command - or, in the GUI, the Client IP Header parameter in the Configure HTTP Parameters dialog box. - - "Minimum length = 1" - - sitepersistence: - choices: - - 'ConnectionProxy' - - 'HTTPRedirect' - - 'NONE' - description: - - "Use cookie-based site persistence. Applicable only to C(HTTP) and C(SSL) GSLB services." - - siteprefix: - description: - - >- - The site's prefix string. When the service is bound to a GSLB virtual server, a GSLB site domain is - generated internally for each bound service-domain pair by concatenating the site prefix of the - service and the name of the domain. If the special string NONE is specified, the site-prefix string - is unset. When implementing HTTP redirect site persistence, the NetScaler appliance redirects GSLB - requests to GSLB services by using their site domains. - - clttimeout: - description: - - >- - Idle time, in seconds, after which a client connection is terminated. Applicable if connection proxy - based site persistence is used. - - "Minimum value = 0" - - "Maximum value = 31536000" - - maxbandwidth: - description: - - >- - Integer specifying the maximum bandwidth allowed for the service. A GSLB service whose bandwidth - reaches the maximum is not considered when a GSLB decision is made, until its bandwidth consumption - drops below the maximum. - - downstateflush: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Flush all active transactions associated with the GSLB service when its state transitions from UP to - DOWN. Do not enable this option for services that must complete their transactions. Applicable if - connection proxy based site persistence is used. - - maxaaausers: - description: - - >- - Maximum number of SSL VPN users that can be logged on concurrently to the VPN virtual server that is - represented by this GSLB service. A GSLB service whose user count reaches the maximum is not - considered when a GSLB decision is made, until the count drops below the maximum. - - "Minimum value = C(0)" - - "Maximum value = C(65535)" - - monthreshold: - description: - - >- - Monitoring threshold value for the GSLB service. If the sum of the weights of the monitors that are - bound to this GSLB service and are in the UP state is not equal to or greater than this threshold - value, the service is marked as DOWN. - - "Minimum value = C(0)" - - "Maximum value = C(65535)" - - hashid: - description: - - "Unique hash identifier for the GSLB service, used by hash based load balancing methods." - - "Minimum value = C(1)" - - comment: - description: - - "Any comments that you might want to associate with the GSLB service." - - appflowlog: - choices: - - 'enabled' - - 'disabled' - description: - - "Enable logging appflow flow information." - - ipaddress: - description: - - >- - IP address for the GSLB service. Should represent a load balancing, content switching, or VPN virtual - server on the NetScaler appliance, or the IP address of another load balancing device. - - monitor_bindings: - description: - - Bind monitors to this gslb service - suboptions: - - weight: - description: - - Weight to assign to the monitor-service binding. - - A larger number specifies a greater weight. - - Contributes to the monitoring threshold, which determines the state of the service. - - Minimum value = C(1) - - Maximum value = C(100) - - monitor_name: - description: - - Monitor name. - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -- name: Setup gslb service 2 - - delegate_to: localhost - register: result - check_mode: "{{ check_mode }}" - - netscaler_gslb_service: - operation: present - - servicename: gslb-service-2 - cnameentry: example.com - sitename: gslb-site-1 -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: "['message 1', 'message 2']" - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: "{ 'targetlbvserver': 'difference. ours: (str) server1 other: (str) server2' }" -''' - -import copy - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import ( - ConfigProxy, - get_nitro_client, - netscaler_common_arguments, - log, - loglines, - ensure_feature_is_enabled, - monkey_patch_nitro_api, - get_immutables_intersection, -) - -try: - monkey_patch_nitro_api() - from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbservice import gslbservice - from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbservice_lbmonitor_binding import gslbservice_lbmonitor_binding - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - - -def gslb_service_exists(client, module): - if gslbservice.count_filtered(client, 'servicename:%s' % module.params['servicename']) > 0: - return True - else: - return False - - -def gslb_service_identical(client, module, gslb_service_proxy): - gslb_service_list = gslbservice.get_filtered(client, 'servicename:%s' % module.params['servicename']) - diff_dict = gslb_service_proxy.diff_object(gslb_service_list[0]) - # Ignore ip attribute missing - if 'ip' in diff_dict: - del diff_dict['ip'] - if len(diff_dict) == 0: - return True - else: - return False - - -def get_actual_monitor_bindings(client, module): - log('get_actual_monitor_bindings') - # Get actual monitor bindings and index them by monitor_name - actual_monitor_bindings = {} - if gslbservice_lbmonitor_binding.count(client, servicename=module.params['servicename']) != 0: - # Get all monitor bindings associated with the named gslb vserver - fetched_bindings = gslbservice_lbmonitor_binding.get(client, servicename=module.params['servicename']) - # index by monitor name - for binding in fetched_bindings: - # complete_missing_attributes(binding, gslbservice_lbmonitor_binding_rw_attrs, fill_value=None) - actual_monitor_bindings[binding.monitor_name] = binding - return actual_monitor_bindings - - -def get_configured_monitor_bindings(client, module): - log('get_configured_monitor_bindings') - configured_monitor_proxys = {} - gslbservice_lbmonitor_binding_rw_attrs = [ - 'weight', - 'servicename', - 'monitor_name', - ] - # Get configured monitor bindings and index them by monitor_name - if module.params['monitor_bindings'] is not None: - for configured_monitor_bindings in module.params['monitor_bindings']: - binding_values = copy.deepcopy(configured_monitor_bindings) - binding_values['servicename'] = module.params['servicename'] - proxy = ConfigProxy( - actual=gslbservice_lbmonitor_binding(), - client=client, - attribute_values_dict=binding_values, - readwrite_attrs=gslbservice_lbmonitor_binding_rw_attrs, - readonly_attrs=[], - ) - configured_monitor_proxys[configured_monitor_bindings['monitor_name']] = proxy - return configured_monitor_proxys - - -def monitor_bindings_identical(client, module): - log('monitor_bindings_identical') - actual_bindings = get_actual_monitor_bindings(client, module) - configured_proxys = get_configured_monitor_bindings(client, module) - - actual_keyset = set(actual_bindings.keys()) - configured_keyset = set(configured_proxys.keys()) - - symmetric_difference = actual_keyset ^ configured_keyset - if len(symmetric_difference) != 0: - log('Symmetric difference %s' % symmetric_difference) - return False - - # Item for item equality test - for key, proxy in configured_proxys.items(): - if not proxy.has_equal_attributes(actual_bindings[key]): - log('monitor binding difference %s' % proxy.diff_object(actual_bindings[key])) - return False - - # Fallthrough to True result - return True - - -def sync_monitor_bindings(client, module): - log('sync_monitor_bindings') - - actual_monitor_bindings = get_actual_monitor_bindings(client, module) - configured_monitor_proxys = get_configured_monitor_bindings(client, module) - - # Delete actual bindings not in configured bindings - for monitor_name, actual_binding in actual_monitor_bindings.items(): - if monitor_name not in configured_monitor_proxys.keys(): - log('Deleting absent binding for monitor %s' % monitor_name) - log('dir is %s' % dir(actual_binding)) - gslbservice_lbmonitor_binding.delete(client, actual_binding) - - # Delete and re-add actual bindings that differ from configured - for proxy_key, binding_proxy in configured_monitor_proxys.items(): - if proxy_key in actual_monitor_bindings: - actual_binding = actual_monitor_bindings[proxy_key] - if not binding_proxy.has_equal_attributes(actual_binding): - log('Deleting differing binding for monitor %s' % actual_binding.monitor_name) - log('dir %s' % dir(actual_binding)) - log('attribute monitor_name %s' % getattr(actual_binding, 'monitor_name')) - log('attribute monitorname %s' % getattr(actual_binding, 'monitorname', None)) - gslbservice_lbmonitor_binding.delete(client, actual_binding) - log('Adding anew binding for monitor %s' % binding_proxy.monitor_name) - binding_proxy.add() - - # Add configured monitors that are missing from actual - for proxy_key, binding_proxy in configured_monitor_proxys.items(): - if proxy_key not in actual_monitor_bindings.keys(): - log('Adding monitor binding for monitor %s' % binding_proxy.monitor_name) - binding_proxy.add() - - -def diff_list(client, module, gslb_service_proxy): - gslb_service_list = gslbservice.get_filtered(client, 'servicename:%s' % module.params['servicename']) - diff_list = gslb_service_proxy.diff_object(gslb_service_list[0]) - if 'ip' in diff_list: - del diff_list['ip'] - return diff_list - - -def all_identical(client, module, gslb_service_proxy): - return gslb_service_identical(client, module, gslb_service_proxy) and monitor_bindings_identical(client, module) - - -def main(): - - module_specific_arguments = dict( - servicename=dict(type='str'), - cnameentry=dict(type='str'), - servername=dict(type='str'), - servicetype=dict( - type='str', - choices=[ - 'HTTP', - 'FTP', - 'TCP', - 'UDP', - 'SSL', - 'SSL_BRIDGE', - 'SSL_TCP', - 'NNTP', - 'ANY', - 'SIP_UDP', - 'SIP_TCP', - 'SIP_SSL', - 'RADIUS', - 'RDP', - 'RTSP', - 'MYSQL', - 'MSSQL', - 'ORACLE', - ] - ), - port=dict(type='int'), - publicip=dict(type='str'), - publicport=dict(type='int'), - maxclient=dict(type='float'), - healthmonitor=dict(type='bool'), - sitename=dict(type='str'), - cip=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - cipheader=dict(type='str'), - sitepersistence=dict( - type='str', - choices=[ - 'ConnectionProxy', - 'HTTPRedirect', - 'NONE', - ] - ), - siteprefix=dict(type='str'), - clttimeout=dict(type='float'), - maxbandwidth=dict(type='float'), - downstateflush=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - maxaaausers=dict(type='float'), - monthreshold=dict(type='float'), - hashid=dict(type='float'), - comment=dict(type='str'), - appflowlog=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - ipaddress=dict(type='str'), - ) - - hand_inserted_arguments = dict( - monitor_bindings=dict(type='list'), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - argument_spec.update(module_specific_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - readwrite_attrs = [ - 'servicename', - 'cnameentry', - 'ip', - 'servername', - 'servicetype', - 'port', - 'publicip', - 'publicport', - 'maxclient', - 'healthmonitor', - 'sitename', - 'cip', - 'cipheader', - 'sitepersistence', - 'siteprefix', - 'clttimeout', - 'maxbandwidth', - 'downstateflush', - 'maxaaausers', - 'monthreshold', - 'hashid', - 'comment', - 'appflowlog', - 'ipaddress', - ] - - readonly_attrs = [ - 'gslb', - 'svrstate', - 'svreffgslbstate', - 'gslbthreshold', - 'gslbsvcstats', - 'monstate', - 'preferredlocation', - 'monitor_state', - 'statechangetimesec', - 'tickssincelaststatechange', - 'threshold', - 'clmonowner', - 'clmonview', - '__count', - ] - - immutable_attrs = [ - 'servicename', - 'cnameentry', - 'ip', - 'servername', - 'servicetype', - 'port', - 'sitename', - 'state', - 'cipheader', - 'cookietimeout', - 'clttimeout', - 'svrtimeout', - 'viewip', - 'monitor_name_svc', - 'newname', - ] - - transforms = { - 'healthmonitor': ['bool_yes_no'], - 'cip': [lambda v: v.upper()], - 'downstateflush': [lambda v: v.upper()], - 'appflowlog': [lambda v: v.upper()], - } - - # params = copy.deepcopy(module.params) - module.params['ip'] = module.params['ipaddress'] - - # Instantiate config proxy - gslb_service_proxy = ConfigProxy( - actual=gslbservice(), - client=client, - attribute_values_dict=module.params, - transforms=transforms, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - ) - - try: - ensure_feature_is_enabled(client, 'GSLB') - # Apply appropriate state - if module.params['state'] == 'present': - if not gslb_service_exists(client, module): - if not module.check_mode: - gslb_service_proxy.add() - sync_monitor_bindings(client, module) - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not all_identical(client, module, gslb_service_proxy): - - # Check if we try to change value of immutable attributes - immutables_changed = get_immutables_intersection(gslb_service_proxy, diff_list(client, module, gslb_service_proxy).keys()) - if immutables_changed != []: - module.fail_json( - msg='Cannot update immutable attributes %s' % (immutables_changed,), - diff=diff_list(client, module, gslb_service_proxy), - **module_result - ) - - # Update main configuration object - if not gslb_service_identical(client, module, gslb_service_proxy): - if not module.check_mode: - gslb_service_proxy.update() - - # Update monitor bindigns - if not monitor_bindings_identical(client, module): - if not module.check_mode: - sync_monitor_bindings(client, module) - - # Fallthrough to save and change status update - module_result['changed'] = True - if module.params['save_config']: - client.save_config() - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - if not gslb_service_exists(client, module): - module.fail_json(msg='GSLB service does not exist', **module_result) - if not gslb_service_identical(client, module, gslb_service_proxy): - module.fail_json( - msg='GSLB service differs from configured', - diff=diff_list(client, module, gslb_service_proxy), - **module_result - ) - if not monitor_bindings_identical(client, module): - module.fail_json( - msg='Monitor bindings differ from configured', - diff=diff_list(client, module, gslb_service_proxy), - **module_result - ) - - elif module.params['state'] == 'absent': - if gslb_service_exists(client, module): - if not module.check_mode: - gslb_service_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - if gslb_service_exists(client, module): - module.fail_json(msg='GSLB service still exists', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_gslb_site.py b/plugins/modules/network/netscaler/netscaler_gslb_site.py deleted file mode 100644 index a84ac1c781..0000000000 --- a/plugins/modules/network/netscaler/netscaler_gslb_site.py +++ /dev/null @@ -1,424 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_gslb_site -short_description: Manage gslb site entities in Netscaler. -description: - - Manage gslb site entities in Netscaler. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - sitename: - description: - - >- - Name for the GSLB site. Must begin with an ASCII alphanumeric or underscore C(_) character, and must - contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space C( ), colon C(:), at C(@), equals - C(=), and hyphen C(-) characters. Cannot be changed after the virtual server is created. - - "Minimum length = 1" - - sitetype: - choices: - - 'REMOTE' - - 'LOCAL' - description: - - >- - Type of site to create. If the type is not specified, the appliance automatically detects and sets - the type on the basis of the IP address being assigned to the site. If the specified site IP address - is owned by the appliance (for example, a MIP address or SNIP address), the site is a local site. - Otherwise, it is a remote site. - - siteipaddress: - description: - - >- - IP address for the GSLB site. The GSLB site uses this IP address to communicate with other GSLB - sites. For a local site, use any IP address that is owned by the appliance (for example, a SNIP or - MIP address, or the IP address of the ADNS service). - - "Minimum length = 1" - - publicip: - description: - - >- - Public IP address for the local site. Required only if the appliance is deployed in a private address - space and the site has a public IP address hosted on an external firewall or a NAT device. - - "Minimum length = 1" - - metricexchange: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Exchange metrics with other sites. Metrics are exchanged by using Metric Exchange Protocol (MEP). The - appliances in the GSLB setup exchange health information once every second. - - >- - If you disable metrics exchange, you can use only static load balancing methods (such as round robin, - static proximity, or the hash-based methods), and if you disable metrics exchange when a dynamic load - balancing method (such as least connection) is in operation, the appliance falls back to round robin. - Also, if you disable metrics exchange, you must use a monitor to determine the state of GSLB - services. Otherwise, the service is marked as DOWN. - - nwmetricexchange: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Exchange, with other GSLB sites, network metrics such as round-trip time (RTT), learned from - communications with various local DNS (LDNS) servers used by clients. RTT information is used in the - dynamic RTT load balancing method, and is exchanged every 5 seconds. - - sessionexchange: - choices: - - 'enabled' - - 'disabled' - description: - - "Exchange persistent session entries with other GSLB sites every five seconds." - - triggermonitor: - choices: - - 'ALWAYS' - - 'MEPDOWN' - - 'MEPDOWN_SVCDOWN' - description: - - >- - Specify the conditions under which the GSLB service must be monitored by a monitor, if one is bound. - Available settings function as follows: - - "* C(ALWAYS) - Monitor the GSLB service at all times." - - >- - * C(MEPDOWN) - Monitor the GSLB service only when the exchange of metrics through the Metrics Exchange - Protocol (MEP) is disabled. - - "C(MEPDOWN_SVCDOWN) - Monitor the service in either of the following situations:" - - "* The exchange of metrics through MEP is disabled." - - >- - * The exchange of metrics through MEP is enabled but the status of the service, learned through - metrics exchange, is DOWN. - - parentsite: - description: - - "Parent site of the GSLB site, in a parent-child topology." - - clip: - description: - - >- - Cluster IP address. Specify this parameter to connect to the remote cluster site for GSLB auto-sync. - Note: The cluster IP address is defined when creating the cluster. - - publicclip: - description: - - >- - IP address to be used to globally access the remote cluster when it is deployed behind a NAT. It can - be same as the normal cluster IP address. - - naptrreplacementsuffix: - description: - - >- - The naptr replacement suffix configured here will be used to construct the naptr replacement field in - NAPTR record. - - "Minimum length = 1" - - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -- name: Setup gslb site - delegate_to: localhost - netscaler_gslb_site: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - - sitename: gslb-site-1 - siteipaddress: 192.168.1.1 - sitetype: LOCAL - publicip: 192.168.1.1 - metricexchange: enabled - nwmetricexchange: enabled - sessionexchange: enabled - triggermonitor: ALWAYS - -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: "['message 1', 'message 2']" - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: "{ 'targetlbvserver': 'difference. ours: (str) server1 other: (str) server2' }" -''' - -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbsite import gslbsite - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import ( - ConfigProxy, - get_nitro_client, - netscaler_common_arguments, - log, - loglines, - ensure_feature_is_enabled, - get_immutables_intersection, -) - - -def gslb_site_exists(client, module): - if gslbsite.count_filtered(client, 'sitename:%s' % module.params['sitename']) > 0: - return True - else: - return False - - -def gslb_site_identical(client, module, gslb_site_proxy): - gslb_site_list = gslbsite.get_filtered(client, 'sitename:%s' % module.params['sitename']) - diff_dict = gslb_site_proxy.diff_object(gslb_site_list[0]) - if len(diff_dict) == 0: - return True - else: - return False - - -def diff_list(client, module, gslb_site_proxy): - gslb_site_list = gslbsite.get_filtered(client, 'sitename:%s' % module.params['sitename']) - return gslb_site_proxy.diff_object(gslb_site_list[0]) - - -def main(): - - module_specific_arguments = dict( - sitename=dict(type='str'), - sitetype=dict( - type='str', - choices=[ - 'REMOTE', - 'LOCAL', - ] - ), - siteipaddress=dict(type='str'), - publicip=dict(type='str'), - metricexchange=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - nwmetricexchange=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - sessionexchange=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - triggermonitor=dict( - type='str', - choices=[ - 'ALWAYS', - 'MEPDOWN', - 'MEPDOWN_SVCDOWN', - ] - ), - parentsite=dict(type='str'), - clip=dict(type='str'), - publicclip=dict(type='str'), - naptrreplacementsuffix=dict(type='str'), - ) - - hand_inserted_arguments = dict( - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - argument_spec.update(module_specific_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - readwrite_attrs = [ - 'sitename', - 'sitetype', - 'siteipaddress', - 'publicip', - 'metricexchange', - 'nwmetricexchange', - 'sessionexchange', - 'triggermonitor', - 'parentsite', - 'clip', - 'publicclip', - 'naptrreplacementsuffix', - ] - - readonly_attrs = [ - 'status', - 'persistencemepstatus', - 'version', - '__count', - ] - - immutable_attrs = [ - 'sitename', - 'sitetype', - 'siteipaddress', - 'publicip', - 'parentsite', - 'clip', - 'publicclip', - ] - - transforms = { - 'metricexchange': [lambda v: v.upper()], - 'nwmetricexchange': [lambda v: v.upper()], - 'sessionexchange': [lambda v: v.upper()], - } - - # Instantiate config proxy - gslb_site_proxy = ConfigProxy( - actual=gslbsite(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - ensure_feature_is_enabled(client, 'GSLB') - - # Apply appropriate state - if module.params['state'] == 'present': - log('Applying actions for state present') - if not gslb_site_exists(client, module): - if not module.check_mode: - gslb_site_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not gslb_site_identical(client, module, gslb_site_proxy): - - # Check if we try to change value of immutable attributes - immutables_changed = get_immutables_intersection(gslb_site_proxy, diff_list(client, module, gslb_site_proxy).keys()) - if immutables_changed != []: - module.fail_json( - msg='Cannot update immutable attributes %s' % (immutables_changed,), - diff=diff_list(client, module, gslb_site_proxy), - **module_result - ) - - if not module.check_mode: - gslb_site_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state present') - if not gslb_site_exists(client, module): - module.fail_json(msg='GSLB site does not exist', **module_result) - if not gslb_site_identical(client, module, gslb_site_proxy): - module.fail_json(msg='GSLB site differs from configured', diff=diff_list(client, module, gslb_site_proxy), **module_result) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if gslb_site_exists(client, module): - if not module.check_mode: - gslb_site_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state absent') - if gslb_site_exists(client, module): - module.fail_json(msg='GSLB site still exists', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_gslb_vserver.py b/plugins/modules/network/netscaler/netscaler_gslb_vserver.py deleted file mode 100644 index ee0c8390a7..0000000000 --- a/plugins/modules/network/netscaler/netscaler_gslb_vserver.py +++ /dev/null @@ -1,955 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_gslb_vserver -short_description: Configure gslb vserver entities in Netscaler. -description: - - Configure gslb vserver entities in Netscaler. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - name: - description: - - >- - Name for the GSLB virtual server. Must begin with an ASCII alphanumeric or underscore C(_) character, - and must contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space, colon C(:), at C(@), - equals C(=), and hyphen C(-) characters. Can be changed after the virtual server is created. - - "Minimum length = 1" - - servicetype: - choices: - - 'HTTP' - - 'FTP' - - 'TCP' - - 'UDP' - - 'SSL' - - 'SSL_BRIDGE' - - 'SSL_TCP' - - 'NNTP' - - 'ANY' - - 'SIP_UDP' - - 'SIP_TCP' - - 'SIP_SSL' - - 'RADIUS' - - 'RDP' - - 'RTSP' - - 'MYSQL' - - 'MSSQL' - - 'ORACLE' - description: - - "Protocol used by services bound to the virtual server." - - >- - - dnsrecordtype: - choices: - - 'A' - - 'AAAA' - - 'CNAME' - - 'NAPTR' - description: - - "DNS record type to associate with the GSLB virtual server's domain name." - - "Default value: A" - - "Possible values = A, AAAA, CNAME, NAPTR" - - lbmethod: - choices: - - 'ROUNDROBIN' - - 'LEASTCONNECTION' - - 'LEASTRESPONSETIME' - - 'SOURCEIPHASH' - - 'LEASTBANDWIDTH' - - 'LEASTPACKETS' - - 'STATICPROXIMITY' - - 'RTT' - - 'CUSTOMLOAD' - description: - - "Load balancing method for the GSLB virtual server." - - "Default value: LEASTCONNECTION" - - >- - Possible values = ROUNDROBIN, LEASTCONNECTION, LEASTRESPONSETIME, SOURCEIPHASH, LEASTBANDWIDTH, - LEASTPACKETS, STATICPROXIMITY, RTT, CUSTOMLOAD - - backuplbmethod: - choices: - - 'ROUNDROBIN' - - 'LEASTCONNECTION' - - 'LEASTRESPONSETIME' - - 'SOURCEIPHASH' - - 'LEASTBANDWIDTH' - - 'LEASTPACKETS' - - 'STATICPROXIMITY' - - 'RTT' - - 'CUSTOMLOAD' - description: - - >- - Backup load balancing method. Becomes operational if the primary load balancing method fails or - cannot be used. Valid only if the primary method is based on either round-trip time (RTT) or static - proximity. - - netmask: - description: - - "IPv4 network mask for use in the SOURCEIPHASH load balancing method." - - "Minimum length = 1" - - v6netmasklen: - description: - - >- - Number of bits to consider, in an IPv6 source IP address, for creating the hash that is required by - the C(SOURCEIPHASH) load balancing method. - - "Default value: C(128)" - - "Minimum value = C(1)" - - "Maximum value = C(128)" - - tolerance: - description: - - >- - Site selection tolerance, in milliseconds, for implementing the RTT load balancing method. If a - site's RTT deviates from the lowest RTT by more than the specified tolerance, the site is not - considered when the NetScaler appliance makes a GSLB decision. The appliance implements the round - robin method of global server load balancing between sites whose RTT values are within the specified - tolerance. If the tolerance is 0 (zero), the appliance always sends clients the IP address of the - site with the lowest RTT. - - "Minimum value = C(0)" - - "Maximum value = C(100)" - - persistencetype: - choices: - - 'SOURCEIP' - - 'NONE' - description: - - "Use source IP address based persistence for the virtual server." - - >- - After the load balancing method selects a service for the first packet, the IP address received in - response to the DNS query is used for subsequent requests from the same client. - - persistenceid: - description: - - >- - The persistence ID for the GSLB virtual server. The ID is a positive integer that enables GSLB sites - to identify the GSLB virtual server, and is required if source IP address based or spill over based - persistence is enabled on the virtual server. - - "Minimum value = C(0)" - - "Maximum value = C(65535)" - - persistmask: - description: - - >- - The optional IPv4 network mask applied to IPv4 addresses to establish source IP address based - persistence. - - "Minimum length = 1" - - v6persistmasklen: - description: - - >- - Number of bits to consider in an IPv6 source IP address when creating source IP address based - persistence sessions. - - "Default value: C(128)" - - "Minimum value = C(1)" - - "Maximum value = C(128)" - - timeout: - description: - - "Idle time, in minutes, after which a persistence entry is cleared." - - "Default value: C(2)" - - "Minimum value = C(2)" - - "Maximum value = C(1440)" - - mir: - choices: - - 'enabled' - - 'disabled' - description: - - "Include multiple IP addresses in the DNS responses sent to clients." - - disableprimaryondown: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Continue to direct traffic to the backup chain even after the primary GSLB virtual server returns to - the UP state. Used when spillover is configured for the virtual server. - - dynamicweight: - choices: - - 'SERVICECOUNT' - - 'SERVICEWEIGHT' - - 'DISABLED' - description: - - >- - Specify if the appliance should consider the service count, service weights, or ignore both when - using weight-based load balancing methods. The state of the number of services bound to the virtual - server help the appliance to select the service. - - considereffectivestate: - choices: - - 'NONE' - - 'STATE_ONLY' - description: - - >- - If the primary state of all bound GSLB services is DOWN, consider the effective states of all the - GSLB services, obtained through the Metrics Exchange Protocol (MEP), when determining the state of - the GSLB virtual server. To consider the effective state, set the parameter to STATE_ONLY. To - disregard the effective state, set the parameter to NONE. - - >- - The effective state of a GSLB service is the ability of the corresponding virtual server to serve - traffic. The effective state of the load balancing virtual server, which is transferred to the GSLB - service, is UP even if only one virtual server in the backup chain of virtual servers is in the UP - state. - - comment: - description: - - "Any comments that you might want to associate with the GSLB virtual server." - - somethod: - choices: - - 'CONNECTION' - - 'DYNAMICCONNECTION' - - 'BANDWIDTH' - - 'HEALTH' - - 'NONE' - description: - - "Type of threshold that, when exceeded, triggers spillover. Available settings function as follows:" - - "* C(CONNECTION) - Spillover occurs when the number of client connections exceeds the threshold." - - >- - * C(DYNAMICCONNECTION) - Spillover occurs when the number of client connections at the GSLB virtual - server exceeds the sum of the maximum client (Max Clients) settings for bound GSLB services. Do not - specify a spillover threshold for this setting, because the threshold is implied by the Max Clients - settings of the bound GSLB services. - - >- - * C(BANDWIDTH) - Spillover occurs when the bandwidth consumed by the GSLB virtual server's incoming and - outgoing traffic exceeds the threshold. - - >- - * C(HEALTH) - Spillover occurs when the percentage of weights of the GSLB services that are UP drops - below the threshold. For example, if services gslbSvc1, gslbSvc2, and gslbSvc3 are bound to a virtual - server, with weights 1, 2, and 3, and the spillover threshold is 50%, spillover occurs if gslbSvc1 - and gslbSvc3 or gslbSvc2 and gslbSvc3 transition to DOWN. - - "* C(NONE) - Spillover does not occur." - - sopersistence: - choices: - - 'enabled' - - 'disabled' - description: - - >- - If spillover occurs, maintain source IP address based persistence for both primary and backup GSLB - virtual servers. - - sopersistencetimeout: - description: - - "Timeout for spillover persistence, in minutes." - - "Default value: C(2)" - - "Minimum value = C(2)" - - "Maximum value = C(1440)" - - sothreshold: - description: - - >- - Threshold at which spillover occurs. Specify an integer for the CONNECTION spillover method, a - bandwidth value in kilobits per second for the BANDWIDTH method (do not enter the units), or a - percentage for the HEALTH method (do not enter the percentage symbol). - - "Minimum value = C(1)" - - "Maximum value = C(4294967287)" - - sobackupaction: - choices: - - 'DROP' - - 'ACCEPT' - - 'REDIRECT' - description: - - >- - Action to be performed if spillover is to take effect, but no backup chain to spillover is usable or - exists. - - appflowlog: - choices: - - 'enabled' - - 'disabled' - description: - - "Enable logging appflow flow information." - - domain_bindings: - description: - - >- - List of bindings for domains for this glsb vserver. - suboptions: - cookietimeout: - description: - - Timeout, in minutes, for the GSLB site cookie. - - domainname: - description: - - Domain name for which to change the time to live (TTL) and/or backup service IP address. - - ttl: - description: - - Time to live (TTL) for the domain. - - sitedomainttl: - description: - - >- - TTL, in seconds, for all internally created site domains (created when a site prefix is - configured on a GSLB service) that are associated with this virtual server. - - Minimum value = C(1) - - service_bindings: - description: - - List of bindings for gslb services bound to this gslb virtual server. - suboptions: - servicename: - description: - - Name of the GSLB service for which to change the weight. - weight: - description: - - Weight to assign to the GSLB service. - - disabled: - description: - - When set to C(yes) the GSLB Vserver state will be set to C(disabled). - - When set to C(no) the GSLB Vserver state will be set to C(enabled). - - >- - Note that due to limitations of the underlying NITRO API a C(disabled) state change alone - does not cause the module result to report a changed status. - type: bool - default: false - - - - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -''' - -RETURN = ''' -''' - - -import copy - -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver import gslbvserver - from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver_gslbservice_binding import gslbvserver_gslbservice_binding - from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver_domain_binding import gslbvserver_domain_binding - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import ( - ConfigProxy, - get_nitro_client, - netscaler_common_arguments, - log, - loglines, - ensure_feature_is_enabled, - get_immutables_intersection, - complete_missing_attributes -) - - -gslbvserver_domain_binding_rw_attrs = [ - 'name', - 'domainname', - 'backupipflag', - 'cookietimeout', - 'backupip', - 'ttl', - 'sitedomainttl', - 'cookie_domainflag', -] - -gslbvserver_gslbservice_binding_rw_attrs = [ - 'name', - 'servicename', - 'weight', -] - - -def get_actual_domain_bindings(client, module): - log('get_actual_domain_bindings') - # Get actual domain bindings and index them by domainname - actual_domain_bindings = {} - if gslbvserver_domain_binding.count(client, name=module.params['name']) != 0: - # Get all domain bindings associated with the named gslb vserver - fetched_domain_bindings = gslbvserver_domain_binding.get(client, name=module.params['name']) - # index by domainname - for binding in fetched_domain_bindings: - complete_missing_attributes(binding, gslbvserver_domain_binding_rw_attrs, fill_value=None) - actual_domain_bindings[binding.domainname] = binding - return actual_domain_bindings - - -def get_configured_domain_bindings_proxys(client, module): - log('get_configured_domain_bindings_proxys') - configured_domain_proxys = {} - # Get configured domain bindings and index them by domainname - if module.params['domain_bindings'] is not None: - for configured_domain_binding in module.params['domain_bindings']: - binding_values = copy.deepcopy(configured_domain_binding) - binding_values['name'] = module.params['name'] - gslbvserver_domain_binding_proxy = ConfigProxy( - actual=gslbvserver_domain_binding(), - client=client, - attribute_values_dict=binding_values, - readwrite_attrs=gslbvserver_domain_binding_rw_attrs, - readonly_attrs=[], - ) - configured_domain_proxys[configured_domain_binding['domainname']] = gslbvserver_domain_binding_proxy - return configured_domain_proxys - - -def sync_domain_bindings(client, module): - log('sync_domain_bindings') - - actual_domain_bindings = get_actual_domain_bindings(client, module) - configured_domain_proxys = get_configured_domain_bindings_proxys(client, module) - - # Delete actual bindings not in configured bindings - for domainname, actual_domain_binding in actual_domain_bindings.items(): - if domainname not in configured_domain_proxys.keys(): - log('Deleting absent binding for domain %s' % domainname) - gslbvserver_domain_binding.delete(client, actual_domain_binding) - - # Delete actual bindings that differ from configured - for proxy_key, binding_proxy in configured_domain_proxys.items(): - if proxy_key in actual_domain_bindings: - actual_binding = actual_domain_bindings[proxy_key] - if not binding_proxy.has_equal_attributes(actual_binding): - log('Deleting differing binding for domain %s' % binding_proxy.domainname) - gslbvserver_domain_binding.delete(client, actual_binding) - log('Adding anew binding for domain %s' % binding_proxy.domainname) - binding_proxy.add() - - # Add configured domains that are missing from actual - for proxy_key, binding_proxy in configured_domain_proxys.items(): - if proxy_key not in actual_domain_bindings.keys(): - log('Adding domain binding for domain %s' % binding_proxy.domainname) - binding_proxy.add() - - -def domain_bindings_identical(client, module): - log('domain_bindings_identical') - actual_domain_bindings = get_actual_domain_bindings(client, module) - configured_domain_proxys = get_configured_domain_bindings_proxys(client, module) - - actual_keyset = set(actual_domain_bindings.keys()) - configured_keyset = set(configured_domain_proxys.keys()) - - symmetric_difference = actual_keyset ^ configured_keyset - - log('symmetric difference %s' % symmetric_difference) - if len(symmetric_difference) != 0: - return False - - # Item for item equality test - for key, proxy in configured_domain_proxys.items(): - diff = proxy.diff_object(actual_domain_bindings[key]) - if 'backupipflag' in diff: - del diff['backupipflag'] - if not len(diff) == 0: - return False - # Fallthrough to True result - return True - - -def get_actual_service_bindings(client, module): - log('get_actual_service_bindings') - # Get actual domain bindings and index them by domainname - actual_bindings = {} - if gslbvserver_gslbservice_binding.count(client, name=module.params['name']) != 0: - # Get all service bindings associated with the named gslb vserver - fetched_bindings = gslbvserver_gslbservice_binding.get(client, name=module.params['name']) - # index by servicename - for binding in fetched_bindings: - complete_missing_attributes(binding, gslbvserver_gslbservice_binding_rw_attrs, fill_value=None) - actual_bindings[binding.servicename] = binding - - return actual_bindings - - -def get_configured_service_bindings(client, module): - log('get_configured_service_bindings_proxys') - configured_proxys = {} - # Get configured domain bindings and index them by domainname - if module.params['service_bindings'] is not None: - for configured_binding in module.params['service_bindings']: - binding_values = copy.deepcopy(configured_binding) - binding_values['name'] = module.params['name'] - gslbvserver_service_binding_proxy = ConfigProxy( - actual=gslbvserver_gslbservice_binding(), - client=client, - attribute_values_dict=binding_values, - readwrite_attrs=gslbvserver_gslbservice_binding_rw_attrs, - readonly_attrs=[], - ) - configured_proxys[configured_binding['servicename']] = gslbvserver_service_binding_proxy - return configured_proxys - - -def sync_service_bindings(client, module): - actual = get_actual_service_bindings(client, module) - configured = get_configured_service_bindings(client, module) - - # Delete extraneous - extraneous_service_bindings = list(set(actual.keys()) - set(configured.keys())) - for servicename in extraneous_service_bindings: - log('Deleting missing binding from service %s' % servicename) - binding = actual[servicename] - binding.name = module.params['name'] - gslbvserver_gslbservice_binding.delete(client, binding) - - # Recreate different - common_service_bindings = list(set(actual.keys()) & set(configured.keys())) - for servicename in common_service_bindings: - proxy = configured[servicename] - binding = actual[servicename] - if not proxy.has_equal_attributes(actual): - log('Recreating differing service binding %s' % servicename) - gslbvserver_gslbservice_binding.delete(client, binding) - proxy.add() - - # Add missing - missing_service_bindings = list(set(configured.keys()) - set(actual.keys())) - for servicename in missing_service_bindings: - proxy = configured[servicename] - log('Adding missing service binding %s' % servicename) - proxy.add() - - -def service_bindings_identical(client, module): - actual_bindings = get_actual_service_bindings(client, module) - configured_proxys = get_configured_service_bindings(client, module) - - actual_keyset = set(actual_bindings.keys()) - configured_keyset = set(configured_proxys.keys()) - - symmetric_difference = actual_keyset ^ configured_keyset - if len(symmetric_difference) != 0: - return False - - # Item for item equality test - for key, proxy in configured_proxys.items(): - if key in actual_bindings.keys(): - if not proxy.has_equal_attributes(actual_bindings[key]): - return False - - # Fallthrough to True result - return True - - -def gslb_vserver_exists(client, module): - if gslbvserver.count_filtered(client, 'name:%s' % module.params['name']) > 0: - return True - else: - return False - - -def gslb_vserver_identical(client, module, gslb_vserver_proxy): - gslb_vserver_list = gslbvserver.get_filtered(client, 'name:%s' % module.params['name']) - diff_dict = gslb_vserver_proxy.diff_object(gslb_vserver_list[0]) - if len(diff_dict) != 0: - return False - else: - return True - - -def all_identical(client, module, gslb_vserver_proxy): - return ( - gslb_vserver_identical(client, module, gslb_vserver_proxy) and - domain_bindings_identical(client, module) and - service_bindings_identical(client, module) - ) - - -def diff_list(client, module, gslb_vserver_proxy): - gslb_vserver_list = gslbvserver.get_filtered(client, 'name:%s' % module.params['name']) - return gslb_vserver_proxy.diff_object(gslb_vserver_list[0]) - - -def do_state_change(client, module, gslb_vserver_proxy): - if module.params['disabled']: - log('Disabling glsb_vserver') - result = gslbvserver.disable(client, gslb_vserver_proxy.actual) - else: - log('Enabling gslbvserver') - result = gslbvserver.enable(client, gslb_vserver_proxy.actual) - return result - - -def main(): - - module_specific_arguments = dict( - name=dict(type='str'), - servicetype=dict( - type='str', - choices=[ - 'HTTP', - 'FTP', - 'TCP', - 'UDP', - 'SSL', - 'SSL_BRIDGE', - 'SSL_TCP', - 'NNTP', - 'ANY', - 'SIP_UDP', - 'SIP_TCP', - 'SIP_SSL', - 'RADIUS', - 'RDP', - 'RTSP', - 'MYSQL', - 'MSSQL', - 'ORACLE', - ] - ), - dnsrecordtype=dict( - type='str', - choices=[ - 'A', - 'AAAA', - 'CNAME', - 'NAPTR', - ] - ), - lbmethod=dict( - type='str', - choices=[ - 'ROUNDROBIN', - 'LEASTCONNECTION', - 'LEASTRESPONSETIME', - 'SOURCEIPHASH', - 'LEASTBANDWIDTH', - 'LEASTPACKETS', - 'STATICPROXIMITY', - 'RTT', - 'CUSTOMLOAD', - ] - ), - backuplbmethod=dict( - type='str', - choices=[ - 'ROUNDROBIN', - 'LEASTCONNECTION', - 'LEASTRESPONSETIME', - 'SOURCEIPHASH', - 'LEASTBANDWIDTH', - 'LEASTPACKETS', - 'STATICPROXIMITY', - 'RTT', - 'CUSTOMLOAD', - ] - ), - netmask=dict(type='str'), - v6netmasklen=dict(type='float'), - tolerance=dict(type='float'), - persistencetype=dict( - type='str', - choices=[ - 'SOURCEIP', - 'NONE', - ] - ), - persistenceid=dict(type='float'), - persistmask=dict(type='str'), - v6persistmasklen=dict(type='float'), - timeout=dict(type='float'), - mir=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - disableprimaryondown=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - dynamicweight=dict( - type='str', - choices=[ - 'SERVICECOUNT', - 'SERVICEWEIGHT', - 'DISABLED', - ] - ), - considereffectivestate=dict( - type='str', - choices=[ - 'NONE', - 'STATE_ONLY', - ] - ), - comment=dict(type='str'), - somethod=dict( - type='str', - choices=[ - 'CONNECTION', - 'DYNAMICCONNECTION', - 'BANDWIDTH', - 'HEALTH', - 'NONE', - ] - ), - sopersistence=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - sopersistencetimeout=dict(type='float'), - sothreshold=dict(type='float'), - sobackupaction=dict( - type='str', - choices=[ - 'DROP', - 'ACCEPT', - 'REDIRECT', - ] - ), - appflowlog=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - domainname=dict(type='str'), - cookie_domain=dict(type='str'), - ) - - hand_inserted_arguments = dict( - domain_bindings=dict(type='list'), - service_bindings=dict(type='list'), - disabled=dict( - type='bool', - default=False, - ), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - argument_spec.update(module_specific_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - readwrite_attrs = [ - 'name', - 'servicetype', - 'dnsrecordtype', - 'lbmethod', - 'backuplbmethod', - 'netmask', - 'v6netmasklen', - 'tolerance', - 'persistencetype', - 'persistenceid', - 'persistmask', - 'v6persistmasklen', - 'timeout', - 'mir', - 'disableprimaryondown', - 'dynamicweight', - 'considereffectivestate', - 'comment', - 'somethod', - 'sopersistence', - 'sopersistencetimeout', - 'sothreshold', - 'sobackupaction', - 'appflowlog', - 'cookie_domain', - ] - - readonly_attrs = [ - 'curstate', - 'status', - 'lbrrreason', - 'iscname', - 'sitepersistence', - 'totalservices', - 'activeservices', - 'statechangetimesec', - 'statechangetimemsec', - 'tickssincelaststatechange', - 'health', - 'policyname', - 'priority', - 'gotopriorityexpression', - 'type', - 'vsvrbindsvcip', - 'vsvrbindsvcport', - '__count', - ] - - immutable_attrs = [ - 'name', - 'servicetype', - ] - - transforms = { - 'mir': [lambda v: v.upper()], - 'disableprimaryondown': [lambda v: v.upper()], - 'sopersistence': [lambda v: v.upper()], - 'appflowlog': [lambda v: v.upper()], - } - - # Instantiate config proxy - gslb_vserver_proxy = ConfigProxy( - actual=gslbvserver(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - ensure_feature_is_enabled(client, 'GSLB') - # Apply appropriate state - if module.params['state'] == 'present': - log('Applying state present') - if not gslb_vserver_exists(client, module): - log('Creating object') - if not module.check_mode: - gslb_vserver_proxy.add() - sync_domain_bindings(client, module) - sync_service_bindings(client, module) - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not all_identical(client, module, gslb_vserver_proxy): - log('Entering update actions') - - # Check if we try to change value of immutable attributes - if not gslb_vserver_identical(client, module, gslb_vserver_proxy): - log('Updating gslb vserver') - immutables_changed = get_immutables_intersection(gslb_vserver_proxy, diff_list(client, module, gslb_vserver_proxy).keys()) - if immutables_changed != []: - module.fail_json( - msg='Cannot update immutable attributes %s' % (immutables_changed,), - diff=diff_list(client, module, gslb_vserver_proxy), - **module_result - ) - if not module.check_mode: - gslb_vserver_proxy.update() - - # Update domain bindings - if not domain_bindings_identical(client, module): - if not module.check_mode: - sync_domain_bindings(client, module) - - # Update service bindings - if not service_bindings_identical(client, module): - if not module.check_mode: - sync_service_bindings(client, module) - - module_result['changed'] = True - if not module.check_mode: - if module.params['save_config']: - client.save_config() - else: - module_result['changed'] = False - - if not module.check_mode: - res = do_state_change(client, module, gslb_vserver_proxy) - if res.errorcode != 0: - msg = 'Error when setting disabled state. errorcode: %s message: %s' % (res.errorcode, res.message) - module.fail_json(msg=msg, **module_result) - - # Sanity check for state - if not module.check_mode: - if not gslb_vserver_exists(client, module): - module.fail_json(msg='GSLB Vserver does not exist', **module_result) - if not gslb_vserver_identical(client, module, gslb_vserver_proxy): - module.fail_json(msg='GSLB Vserver differs from configured', diff=diff_list(client, module, gslb_vserver_proxy), **module_result) - if not domain_bindings_identical(client, module): - module.fail_json(msg='Domain bindings differ from configured', diff=diff_list(client, module, gslb_vserver_proxy), **module_result) - if not service_bindings_identical(client, module): - module.fail_json(msg='Service bindings differ from configured', diff=diff_list(client, module, gslb_vserver_proxy), **module_result) - - elif module.params['state'] == 'absent': - - if gslb_vserver_exists(client, module): - if not module.check_mode: - gslb_vserver_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - if gslb_vserver_exists(client, module): - module.fail_json(msg='GSLB Vserver still exists', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_lb_monitor.py b/plugins/modules/network/netscaler/netscaler_lb_monitor.py deleted file mode 100644 index f2f5ad126a..0000000000 --- a/plugins/modules/network/netscaler/netscaler_lb_monitor.py +++ /dev/null @@ -1,1381 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_lb_monitor -short_description: Manage load balancing monitors -description: - - Manage load balancing monitors. - - This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - monitorname: - description: - - >- - Name for the monitor. Must begin with an ASCII alphanumeric or underscore C(_) character, and must - contain only ASCII alphanumeric, underscore, hash C(#), period C(.), space C( ), colon C(:), at C(@), equals - C(=), and hyphen C(-) characters. - - "Minimum length = 1" - - type: - choices: - - 'PING' - - 'TCP' - - 'HTTP' - - 'TCP-ECV' - - 'HTTP-ECV' - - 'UDP-ECV' - - 'DNS' - - 'FTP' - - 'LDNS-PING' - - 'LDNS-TCP' - - 'LDNS-DNS' - - 'RADIUS' - - 'USER' - - 'HTTP-INLINE' - - 'SIP-UDP' - - 'SIP-TCP' - - 'LOAD' - - 'FTP-EXTENDED' - - 'SMTP' - - 'SNMP' - - 'NNTP' - - 'MYSQL' - - 'MYSQL-ECV' - - 'MSSQL-ECV' - - 'ORACLE-ECV' - - 'LDAP' - - 'POP3' - - 'CITRIX-XML-SERVICE' - - 'CITRIX-WEB-INTERFACE' - - 'DNS-TCP' - - 'RTSP' - - 'ARP' - - 'CITRIX-AG' - - 'CITRIX-AAC-LOGINPAGE' - - 'CITRIX-AAC-LAS' - - 'CITRIX-XD-DDC' - - 'ND6' - - 'CITRIX-WI-EXTENDED' - - 'DIAMETER' - - 'RADIUS_ACCOUNTING' - - 'STOREFRONT' - - 'APPC' - - 'SMPP' - - 'CITRIX-XNC-ECV' - - 'CITRIX-XDM' - - 'CITRIX-STA-SERVICE' - - 'CITRIX-STA-SERVICE-NHOP' - description: - - "Type of monitor that you want to create." - - action: - choices: - - 'NONE' - - 'LOG' - - 'DOWN' - description: - - >- - Action to perform when the response to an inline monitor (a monitor of type C(HTTP-INLINE)) indicates - that the service is down. A service monitored by an inline monitor is considered C(DOWN) if the response - code is not one of the codes that have been specified for the Response Code parameter. - - "Available settings function as follows:" - - >- - * C(NONE) - Do not take any action. However, the show service command and the show lb monitor command - indicate the total number of responses that were checked and the number of consecutive error - responses received after the last successful probe. - - "* C(LOG) - Log the event in NSLOG or SYSLOG." - - >- - * C(DOWN) - Mark the service as being down, and then do not direct any traffic to the service until the - configured down time has expired. Persistent connections to the service are terminated as soon as the - service is marked as C(DOWN). Also, log the event in NSLOG or SYSLOG. - - respcode: - description: - - >- - Response codes for which to mark the service as UP. For any other response code, the action performed - depends on the monitor type. C(HTTP) monitors and C(RADIUS) monitors mark the service as C(DOWN), while - C(HTTP-INLINE) monitors perform the action indicated by the Action parameter. - - httprequest: - description: - - 'HTTP request to send to the server (for example, C("HEAD /file.html")).' - - rtsprequest: - description: - - 'RTSP request to send to the server (for example, C("OPTIONS *")).' - - customheaders: - description: - - "Custom header string to include in the monitoring probes." - - maxforwards: - description: - - >- - Maximum number of hops that the SIP request used for monitoring can traverse to reach the server. - Applicable only to monitors of type C(SIP-UDP). - - "Minimum value = C(0)" - - "Maximum value = C(255)" - - sipmethod: - choices: - - 'OPTIONS' - - 'INVITE' - - 'REGISTER' - description: - - "SIP method to use for the query. Applicable only to monitors of type C(SIP-UDP)." - - sipuri: - description: - - >- - SIP URI string to send to the service (for example, C(sip:sip.test)). Applicable only to monitors of - type C(SIP-UDP). - - "Minimum length = 1" - - sipreguri: - description: - - >- - SIP user to be registered. Applicable only if the monitor is of type C(SIP-UDP) and the SIP Method - parameter is set to C(REGISTER). - - "Minimum length = 1" - - send: - description: - - "String to send to the service. Applicable to C(TCP-ECV), C(HTTP-ECV), and C(UDP-ECV) monitors." - - recv: - description: - - >- - String expected from the server for the service to be marked as UP. Applicable to C(TCP-ECV), C(HTTP-ECV), - and C(UDP-ECV) monitors. - - query: - description: - - "Domain name to resolve as part of monitoring the DNS service (for example, C(example.com))." - - querytype: - choices: - - 'Address' - - 'Zone' - - 'AAAA' - description: - - >- - Type of DNS record for which to send monitoring queries. Set to C(Address) for querying A records, C(AAAA) - for querying AAAA records, and C(Zone) for querying the SOA record. - - scriptname: - description: - - >- - Path and name of the script to execute. The script must be available on the NetScaler appliance, in - the /nsconfig/monitors/ directory. - - "Minimum length = 1" - - scriptargs: - description: - - "String of arguments for the script. The string is copied verbatim into the request." - - dispatcherip: - description: - - "IP address of the dispatcher to which to send the probe." - - dispatcherport: - description: - - "Port number on which the dispatcher listens for the monitoring probe." - - username: - description: - - >- - User name with which to probe the C(RADIUS), C(NNTP), C(FTP), C(FTP-EXTENDED), C(MYSQL), C(MSSQL), C(POP3), C(CITRIX-AG), - C(CITRIX-XD-DDC), C(CITRIX-WI-EXTENDED), C(CITRIX-XNC) or C(CITRIX-XDM) server. - - "Minimum length = 1" - - password: - description: - - >- - Password that is required for logging on to the C(RADIUS), C(NNTP), C(FTP), C(FTP-EXTENDED), C(MYSQL), C(MSSQL), C(POP3), - C(CITRIX-AG), C(CITRIX-XD-DDC), C(CITRIX-WI-EXTENDED), C(CITRIX-XNC-ECV) or C(CITRIX-XDM) server. Used in - conjunction with the user name specified for the C(username) parameter. - - "Minimum length = 1" - - secondarypassword: - description: - - >- - Secondary password that users might have to provide to log on to the Access Gateway server. - Applicable to C(CITRIX-AG) monitors. - - logonpointname: - description: - - >- - Name of the logon point that is configured for the Citrix Access Gateway Advanced Access Control - software. Required if you want to monitor the associated login page or Logon Agent. Applicable to - C(CITRIX-AAC-LAS) and C(CITRIX-AAC-LOGINPAGE) monitors. - - lasversion: - description: - - >- - Version number of the Citrix Advanced Access Control Logon Agent. Required by the C(CITRIX-AAC-LAS) - monitor. - - radkey: - description: - - >- - Authentication key (shared secret text string) for RADIUS clients and servers to exchange. Applicable - to monitors of type C(RADIUS) and C(RADIUS_ACCOUNTING). - - "Minimum length = 1" - - radnasid: - description: - - "NAS-Identifier to send in the Access-Request packet. Applicable to monitors of type C(RADIUS)." - - "Minimum length = 1" - - radnasip: - description: - - >- - Network Access Server (NAS) IP address to use as the source IP address when monitoring a RADIUS - server. Applicable to monitors of type C(RADIUS) and C(RADIUS_ACCOUNTING). - - radaccounttype: - description: - - "Account Type to be used in Account Request Packet. Applicable to monitors of type C(RADIUS_ACCOUNTING)." - - "Minimum value = 0" - - "Maximum value = 15" - - radframedip: - description: - - "Source ip with which the packet will go out . Applicable to monitors of type C(RADIUS_ACCOUNTING)." - - radapn: - description: - - >- - Called Station Id to be used in Account Request Packet. Applicable to monitors of type - C(RADIUS_ACCOUNTING). - - "Minimum length = 1" - - radmsisdn: - description: - - >- - Calling Stations Id to be used in Account Request Packet. Applicable to monitors of type - C(RADIUS_ACCOUNTING). - - "Minimum length = 1" - - radaccountsession: - description: - - >- - Account Session ID to be used in Account Request Packet. Applicable to monitors of type - C(RADIUS_ACCOUNTING). - - "Minimum length = 1" - - lrtm: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Calculate the least response times for bound services. If this parameter is not enabled, the - appliance does not learn the response times of the bound services. Also used for LRTM load balancing. - - deviation: - description: - - >- - Time value added to the learned average response time in dynamic response time monitoring (DRTM). - When a deviation is specified, the appliance learns the average response time of bound services and - adds the deviation to the average. The final value is then continually adjusted to accommodate - response time variations over time. Specified in milliseconds, seconds, or minutes. - - "Minimum value = C(0)" - - "Maximum value = C(20939)" - - units1: - choices: - - 'SEC' - - 'MSEC' - - 'MIN' - description: - - "Unit of measurement for the Deviation parameter. Cannot be changed after the monitor is created." - - interval: - description: - - "Time interval between two successive probes. Must be greater than the value of Response Time-out." - - "Minimum value = C(1)" - - "Maximum value = C(20940)" - - units3: - choices: - - 'SEC' - - 'MSEC' - - 'MIN' - description: - - "monitor interval units." - - resptimeout: - description: - - >- - Amount of time for which the appliance must wait before it marks a probe as FAILED. Must be less than - the value specified for the Interval parameter. - - >- - Note: For C(UDP-ECV) monitors for which a receive string is not configured, response timeout does not - apply. For C(UDP-ECV) monitors with no receive string, probe failure is indicated by an ICMP port - unreachable error received from the service. - - "Minimum value = C(1)" - - "Maximum value = C(20939)" - - units4: - choices: - - 'SEC' - - 'MSEC' - - 'MIN' - description: - - "monitor response timeout units." - - resptimeoutthresh: - description: - - >- - Response time threshold, specified as a percentage of the Response Time-out parameter. If the - response to a monitor probe has not arrived when the threshold is reached, the appliance generates an - SNMP trap called monRespTimeoutAboveThresh. After the response time returns to a value below the - threshold, the appliance generates a monRespTimeoutBelowThresh SNMP trap. For the traps to be - generated, the "MONITOR-RTO-THRESHOLD" alarm must also be enabled. - - "Minimum value = C(0)" - - "Maximum value = C(100)" - - retries: - description: - - >- - Maximum number of probes to send to establish the state of a service for which a monitoring probe - failed. - - "Minimum value = C(1)" - - "Maximum value = C(127)" - - failureretries: - description: - - >- - Number of retries that must fail, out of the number specified for the Retries parameter, for a - service to be marked as DOWN. For example, if the Retries parameter is set to 10 and the Failure - Retries parameter is set to 6, out of the ten probes sent, at least six probes must fail if the - service is to be marked as DOWN. The default value of 0 means that all the retries must fail if the - service is to be marked as DOWN. - - "Minimum value = C(0)" - - "Maximum value = C(32)" - - alertretries: - description: - - >- - Number of consecutive probe failures after which the appliance generates an SNMP trap called - monProbeFailed. - - "Minimum value = C(0)" - - "Maximum value = C(32)" - - successretries: - description: - - "Number of consecutive successful probes required to transition a service's state from DOWN to UP." - - "Minimum value = C(1)" - - "Maximum value = C(32)" - - downtime: - description: - - >- - Time duration for which to wait before probing a service that has been marked as DOWN. Expressed in - milliseconds, seconds, or minutes. - - "Minimum value = C(1)" - - "Maximum value = C(20939)" - - units2: - choices: - - 'SEC' - - 'MSEC' - - 'MIN' - description: - - "Unit of measurement for the Down Time parameter. Cannot be changed after the monitor is created." - - destip: - description: - - >- - IP address of the service to which to send probes. If the parameter is set to 0, the IP address of - the server to which the monitor is bound is considered the destination IP address. - - destport: - description: - - >- - TCP or UDP port to which to send the probe. If the parameter is set to 0, the port number of the - service to which the monitor is bound is considered the destination port. For a monitor of type C(USER), - however, the destination port is the port number that is included in the HTTP request sent to the - dispatcher. Does not apply to monitors of type C(PING). - - state: - choices: - - 'enabled' - - 'disabled' - description: - - >- - State of the monitor. The C(disabled) setting disables not only the monitor being configured, but all - monitors of the same type, until the parameter is set to C(enabled). If the monitor is bound to a - service, the state of the monitor is not taken into account when the state of the service is - determined. - - reverse: - description: - - >- - Mark a service as DOWN, instead of UP, when probe criteria are satisfied, and as UP instead of DOWN - when probe criteria are not satisfied. - type: bool - - transparent: - description: - - >- - The monitor is bound to a transparent device such as a firewall or router. The state of a transparent - device depends on the responsiveness of the services behind it. If a transparent device is being - monitored, a destination IP address must be specified. The probe is sent to the specified IP address - by using the MAC address of the transparent device. - type: bool - - iptunnel: - description: - - >- - Send the monitoring probe to the service through an IP tunnel. A destination IP address must be - specified. - type: bool - - tos: - description: - - "Probe the service by encoding the destination IP address in the IP TOS (6) bits." - type: bool - - tosid: - description: - - "The TOS ID of the specified destination IP. Applicable only when the TOS parameter is set." - - "Minimum value = C(1)" - - "Maximum value = C(63)" - - secure: - description: - - >- - Use a secure SSL connection when monitoring a service. Applicable only to TCP based monitors. The - secure option cannot be used with a C(CITRIX-AG) monitor, because a CITRIX-AG monitor uses a secure - connection by default. - type: bool - - validatecred: - description: - - >- - Validate the credentials of the Xen Desktop DDC server user. Applicable to monitors of type - C(CITRIX-XD-DDC). - type: bool - - domain: - description: - - >- - Domain in which the XenDesktop Desktop Delivery Controller (DDC) servers or Web Interface servers are - present. Required by C(CITRIX-XD-DDC) and C(CITRIX-WI-EXTENDED) monitors for logging on to the DDC servers - and Web Interface servers, respectively. - - ipaddress: - description: - - >- - Set of IP addresses expected in the monitoring response from the DNS server, if the record type is A - or AAAA. Applicable to C(DNS) monitors. - - "Minimum length = 1" - - group: - description: - - >- - Name of a newsgroup available on the NNTP service that is to be monitored. The appliance periodically - generates an NNTP query for the name of the newsgroup and evaluates the response. If the newsgroup is - found on the server, the service is marked as UP. If the newsgroup does not exist or if the search - fails, the service is marked as DOWN. Applicable to NNTP monitors. - - "Minimum length = 1" - - filename: - description: - - >- - Name of a file on the FTP server. The appliance monitors the FTP service by periodically checking the - existence of the file on the server. Applicable to C(FTP-EXTENDED) monitors. - - "Minimum length = 1" - - basedn: - description: - - >- - The base distinguished name of the LDAP service, from where the LDAP server can begin the search for - the attributes in the monitoring query. Required for C(LDAP) service monitoring. - - "Minimum length = 1" - - binddn: - description: - - >- - The distinguished name with which an LDAP monitor can perform the Bind operation on the LDAP server. - Optional. Applicable to C(LDAP) monitors. - - "Minimum length = 1" - - filter: - description: - - "Filter criteria for the LDAP query. Optional." - - "Minimum length = 1" - - attribute: - description: - - >- - Attribute to evaluate when the LDAP server responds to the query. Success or failure of the - monitoring probe depends on whether the attribute exists in the response. Optional. - - "Minimum length = 1" - - database: - description: - - "Name of the database to connect to during authentication." - - "Minimum length = 1" - - oraclesid: - description: - - "Name of the service identifier that is used to connect to the Oracle database during authentication." - - "Minimum length = 1" - - sqlquery: - description: - - >- - SQL query for a C(MYSQL-ECV) or C(MSSQL-ECV) monitor. Sent to the database server after the server - authenticates the connection. - - "Minimum length = 1" - - evalrule: - description: - - >- - Default syntax expression that evaluates the database server's response to a MYSQL-ECV or MSSQL-ECV - monitoring query. Must produce a Boolean result. The result determines the state of the server. If - the expression returns TRUE, the probe succeeds. - - >- - For example, if you want the appliance to evaluate the error message to determine the state of the - server, use the rule C(MYSQL.RES.ROW(10) .TEXT_ELEM(2).EQ("MySQL")). - - mssqlprotocolversion: - choices: - - '70' - - '2000' - - '2000SP1' - - '2005' - - '2008' - - '2008R2' - - '2012' - - '2014' - description: - - "Version of MSSQL server that is to be monitored." - - Snmpoid: - description: - - "SNMP OID for C(SNMP) monitors." - - "Minimum length = 1" - - snmpcommunity: - description: - - "Community name for C(SNMP) monitors." - - "Minimum length = 1" - - snmpthreshold: - description: - - "Threshold for C(SNMP) monitors." - - "Minimum length = 1" - - snmpversion: - choices: - - 'V1' - - 'V2' - description: - - "SNMP version to be used for C(SNMP) monitors." - - metrictable: - description: - - "Metric table to which to bind metrics." - - "Minimum length = 1" - - "Maximum length = 99" - - application: - description: - - >- - Name of the application used to determine the state of the service. Applicable to monitors of type - C(CITRIX-XML-SERVICE). - - "Minimum length = 1" - - sitepath: - description: - - >- - URL of the logon page. For monitors of type C(CITRIX-WEB-INTERFACE), to monitor a dynamic page under the - site path, terminate the site path with a slash C(/). Applicable to C(CITRIX-WEB-INTERFACE), - C(CITRIX-WI-EXTENDED) and C(CITRIX-XDM) monitors. - - "Minimum length = 1" - - storename: - description: - - >- - Store Name. For monitors of type C(STOREFRONT), C(storename) is an optional argument defining storefront - service store name. Applicable to C(STOREFRONT) monitors. - - "Minimum length = 1" - - storefrontacctservice: - description: - - >- - Enable/Disable probing for Account Service. Applicable only to Store Front monitors. For - multi-tenancy configuration users my skip account service. - type: bool - - hostname: - description: - - "Hostname in the FQDN format (Example: C(porche.cars.org)). Applicable to C(STOREFRONT) monitors." - - "Minimum length = 1" - - netprofile: - description: - - "Name of the network profile." - - "Minimum length = 1" - - "Maximum length = 127" - - originhost: - description: - - >- - Origin-Host value for the Capabilities-Exchange-Request (CER) message to use for monitoring Diameter - servers. - - "Minimum length = 1" - - originrealm: - description: - - >- - Origin-Realm value for the Capabilities-Exchange-Request (CER) message to use for monitoring Diameter - servers. - - "Minimum length = 1" - - hostipaddress: - description: - - >- - Host-IP-Address value for the Capabilities-Exchange-Request (CER) message to use for monitoring - Diameter servers. If Host-IP-Address is not specified, the appliance inserts the mapped IP (MIP) - address or subnet IP (SNIP) address from which the CER request (the monitoring probe) is sent. - - "Minimum length = 1" - - vendorid: - description: - - >- - Vendor-Id value for the Capabilities-Exchange-Request (CER) message to use for monitoring Diameter - servers. - - productname: - description: - - >- - Product-Name value for the Capabilities-Exchange-Request (CER) message to use for monitoring Diameter - servers. - - "Minimum length = 1" - - firmwarerevision: - description: - - >- - Firmware-Revision value for the Capabilities-Exchange-Request (CER) message to use for monitoring - Diameter servers. - - authapplicationid: - description: - - >- - List of Auth-Application-Id attribute value pairs (AVPs) for the Capabilities-Exchange-Request (CER) - message to use for monitoring Diameter servers. A maximum of eight of these AVPs are supported in a - monitoring CER message. - - "Minimum value = C(0)" - - "Maximum value = C(4294967295)" - - acctapplicationid: - description: - - >- - List of Acct-Application-Id attribute value pairs (AVPs) for the Capabilities-Exchange-Request (CER) - message to use for monitoring Diameter servers. A maximum of eight of these AVPs are supported in a - monitoring message. - - "Minimum value = C(0)" - - "Maximum value = C(4294967295)" - - inbandsecurityid: - choices: - - 'NO_INBAND_SECURITY' - - 'TLS' - description: - - >- - Inband-Security-Id for the Capabilities-Exchange-Request (CER) message to use for monitoring Diameter - servers. - - supportedvendorids: - description: - - >- - List of Supported-Vendor-Id attribute value pairs (AVPs) for the Capabilities-Exchange-Request (CER) - message to use for monitoring Diameter servers. A maximum eight of these AVPs are supported in a - monitoring message. - - "Minimum value = C(1)" - - "Maximum value = C(4294967295)" - - vendorspecificvendorid: - description: - - >- - Vendor-Id to use in the Vendor-Specific-Application-Id grouped attribute-value pair (AVP) in the - monitoring CER message. To specify Auth-Application-Id or Acct-Application-Id in - Vendor-Specific-Application-Id, use vendorSpecificAuthApplicationIds or - vendorSpecificAcctApplicationIds, respectively. Only one Vendor-Id is supported for all the - Vendor-Specific-Application-Id AVPs in a CER monitoring message. - - "Minimum value = 1" - - vendorspecificauthapplicationids: - description: - - >- - List of Vendor-Specific-Auth-Application-Id attribute value pairs (AVPs) for the - Capabilities-Exchange-Request (CER) message to use for monitoring Diameter servers. A maximum of - eight of these AVPs are supported in a monitoring message. The specified value is combined with the - value of vendorSpecificVendorId to obtain the Vendor-Specific-Application-Id AVP in the CER - monitoring message. - - "Minimum value = C(0)" - - "Maximum value = C(4294967295)" - - vendorspecificacctapplicationids: - description: - - >- - List of Vendor-Specific-Acct-Application-Id attribute value pairs (AVPs) to use for monitoring - Diameter servers. A maximum of eight of these AVPs are supported in a monitoring message. The - specified value is combined with the value of vendorSpecificVendorId to obtain the - Vendor-Specific-Application-Id AVP in the CER monitoring message. - - "Minimum value = C(0)" - - "Maximum value = C(4294967295)" - - kcdaccount: - description: - - "KCD Account used by C(MSSQL) monitor." - - "Minimum length = 1" - - "Maximum length = 32" - - storedb: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Store the database list populated with the responses to monitor probes. Used in database specific - load balancing if C(MSSQL-ECV)/C(MYSQL-ECV) monitor is configured. - - storefrontcheckbackendservices: - description: - - >- - This option will enable monitoring of services running on storefront server. Storefront services are - monitored by probing to a Windows service that runs on the Storefront server and exposes details of - which storefront services are running. - type: bool - - trofscode: - description: - - "Code expected when the server is under maintenance." - - trofsstring: - description: - - >- - String expected from the server for the service to be marked as trofs. Applicable to HTTP-ECV/TCP-ECV - monitors. - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -- name: Set lb monitor - local_action: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - validate_certs: no - - - module: netscaler_lb_monitor - state: present - - monitorname: monitor_1 - type: HTTP-INLINE - action: DOWN - respcode: ['400'] -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: ['message 1', 'message 2'] - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: { 'targetlbvserver': 'difference. ours: (str) server1 other: (str) server2' } -''' - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import ( - ConfigProxy, - get_nitro_client, - netscaler_common_arguments, - log, - loglines, - ensure_feature_is_enabled, - get_immutables_intersection -) - -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbmonitor import lbmonitor - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - - -def lbmonitor_exists(client, module): - log('Checking if monitor exists') - if lbmonitor.count_filtered(client, 'monitorname:%s' % module.params['monitorname']) > 0: - return True - else: - return False - - -def lbmonitor_identical(client, module, lbmonitor_proxy): - log('Checking if monitor is identical') - - count = lbmonitor.count_filtered(client, 'monitorname:%s' % module.params['monitorname']) - if count == 0: - return False - - lbmonitor_list = lbmonitor.get_filtered(client, 'monitorname:%s' % module.params['monitorname']) - diff_dict = lbmonitor_proxy.diff_object(lbmonitor_list[0]) - - # Skipping hashed fields since the cannot be compared directly - hashed_fields = [ - 'password', - 'secondarypassword', - 'radkey', - ] - for key in hashed_fields: - if key in diff_dict: - del diff_dict[key] - - if diff_dict == {}: - return True - else: - return False - - -def diff_list(client, module, lbmonitor_proxy): - monitor_list = lbmonitor.get_filtered(client, 'monitorname:%s' % module.params['monitorname']) - return lbmonitor_proxy.diff_object(monitor_list[0]) - - -def main(): - - module_specific_arguments = dict( - - monitorname=dict(type='str'), - - type=dict( - type='str', - choices=[ - 'PING', - 'TCP', - 'HTTP', - 'TCP-ECV', - 'HTTP-ECV', - 'UDP-ECV', - 'DNS', - 'FTP', - 'LDNS-PING', - 'LDNS-TCP', - 'LDNS-DNS', - 'RADIUS', - 'USER', - 'HTTP-INLINE', - 'SIP-UDP', - 'SIP-TCP', - 'LOAD', - 'FTP-EXTENDED', - 'SMTP', - 'SNMP', - 'NNTP', - 'MYSQL', - 'MYSQL-ECV', - 'MSSQL-ECV', - 'ORACLE-ECV', - 'LDAP', - 'POP3', - 'CITRIX-XML-SERVICE', - 'CITRIX-WEB-INTERFACE', - 'DNS-TCP', - 'RTSP', - 'ARP', - 'CITRIX-AG', - 'CITRIX-AAC-LOGINPAGE', - 'CITRIX-AAC-LAS', - 'CITRIX-XD-DDC', - 'ND6', - 'CITRIX-WI-EXTENDED', - 'DIAMETER', - 'RADIUS_ACCOUNTING', - 'STOREFRONT', - 'APPC', - 'SMPP', - 'CITRIX-XNC-ECV', - 'CITRIX-XDM', - 'CITRIX-STA-SERVICE', - 'CITRIX-STA-SERVICE-NHOP', - ] - ), - - action=dict( - type='str', - choices=[ - 'NONE', - 'LOG', - 'DOWN', - ] - ), - respcode=dict(type='list'), - httprequest=dict(type='str'), - rtsprequest=dict(type='str'), - customheaders=dict(type='str'), - maxforwards=dict(type='float'), - sipmethod=dict( - type='str', - choices=[ - 'OPTIONS', - 'INVITE', - 'REGISTER', - ] - ), - sipuri=dict(type='str'), - sipreguri=dict(type='str'), - send=dict(type='str'), - recv=dict(type='str'), - query=dict(type='str'), - querytype=dict( - type='str', - choices=[ - 'Address', - 'Zone', - 'AAAA', - ] - ), - scriptname=dict(type='str'), - scriptargs=dict(type='str'), - dispatcherip=dict(type='str'), - dispatcherport=dict(type='int'), - username=dict(type='str'), - password=dict(type='str'), - secondarypassword=dict(type='str'), - logonpointname=dict(type='str'), - lasversion=dict(type='str'), - radkey=dict(type='str'), - radnasid=dict(type='str'), - radnasip=dict(type='str'), - radaccounttype=dict(type='float'), - radframedip=dict(type='str'), - radapn=dict(type='str'), - radmsisdn=dict(type='str'), - radaccountsession=dict(type='str'), - lrtm=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - deviation=dict(type='float'), - units1=dict( - type='str', - choices=[ - 'SEC', - 'MSEC', - 'MIN', - ] - ), - interval=dict(type='int'), - units3=dict( - type='str', - choices=[ - 'SEC', - 'MSEC', - 'MIN', - ] - ), - resptimeout=dict(type='int'), - units4=dict( - type='str', - choices=[ - 'SEC', - 'MSEC', - 'MIN', - ] - ), - resptimeoutthresh=dict(type='float'), - retries=dict(type='int'), - failureretries=dict(type='int'), - alertretries=dict(type='int'), - successretries=dict(type='int'), - downtime=dict(type='int'), - units2=dict( - type='str', - choices=[ - 'SEC', - 'MSEC', - 'MIN', - ] - ), - destip=dict(type='str'), - destport=dict(type='int'), - reverse=dict(type='bool'), - transparent=dict(type='bool'), - iptunnel=dict(type='bool'), - tos=dict(type='bool'), - tosid=dict(type='float'), - secure=dict(type='bool'), - validatecred=dict(type='bool'), - domain=dict(type='str'), - ipaddress=dict(type='list'), - group=dict(type='str'), - filename=dict(type='str'), - basedn=dict(type='str'), - binddn=dict(type='str'), - filter=dict(type='str'), - attribute=dict(type='str'), - database=dict(type='str'), - oraclesid=dict(type='str'), - sqlquery=dict(type='str'), - evalrule=dict(type='str'), - mssqlprotocolversion=dict( - type='str', - choices=[ - '70', - '2000', - '2000SP1', - '2005', - '2008', - '2008R2', - '2012', - '2014', - ] - ), - Snmpoid=dict(type='str'), - snmpcommunity=dict(type='str'), - snmpthreshold=dict(type='str'), - snmpversion=dict( - type='str', - choices=[ - 'V1', - 'V2', - ] - ), - application=dict(type='str'), - sitepath=dict(type='str'), - storename=dict(type='str'), - storefrontacctservice=dict(type='bool'), - hostname=dict(type='str'), - netprofile=dict(type='str'), - originhost=dict(type='str'), - originrealm=dict(type='str'), - hostipaddress=dict(type='str'), - vendorid=dict(type='float'), - productname=dict(type='str'), - firmwarerevision=dict(type='float'), - authapplicationid=dict(type='list'), - acctapplicationid=dict(type='list'), - inbandsecurityid=dict( - type='str', - choices=[ - 'NO_INBAND_SECURITY', - 'TLS', - ] - ), - supportedvendorids=dict(type='list'), - vendorspecificvendorid=dict(type='float'), - vendorspecificauthapplicationids=dict(type='list'), - vendorspecificacctapplicationids=dict(type='list'), - storedb=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - storefrontcheckbackendservices=dict(type='bool'), - trofscode=dict(type='float'), - trofsstring=dict(type='str'), - ) - - hand_inserted_arguments = dict() - - argument_spec = dict() - argument_spec.update(module_specific_arguments) - argument_spec.update(netscaler_common_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk', **module_result) - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - # Instantiate lb monitor object - readwrite_attrs = [ - 'monitorname', - 'type', - 'action', - 'respcode', - 'httprequest', - 'rtsprequest', - 'customheaders', - 'maxforwards', - 'sipmethod', - 'sipuri', - 'sipreguri', - 'send', - 'recv', - 'query', - 'querytype', - 'scriptname', - 'scriptargs', - 'dispatcherip', - 'dispatcherport', - 'username', - 'password', - 'secondarypassword', - 'logonpointname', - 'lasversion', - 'radkey', - 'radnasid', - 'radnasip', - 'radaccounttype', - 'radframedip', - 'radapn', - 'radmsisdn', - 'radaccountsession', - 'lrtm', - 'deviation', - 'units1', - 'interval', - 'units3', - 'resptimeout', - 'units4', - 'resptimeoutthresh', - 'retries', - 'failureretries', - 'alertretries', - 'successretries', - 'downtime', - 'units2', - 'destip', - 'destport', - 'reverse', - 'transparent', - 'iptunnel', - 'tos', - 'tosid', - 'secure', - 'validatecred', - 'domain', - 'ipaddress', - 'group', - 'filename', - 'basedn', - 'binddn', - 'filter', - 'attribute', - 'database', - 'oraclesid', - 'sqlquery', - 'evalrule', - 'mssqlprotocolversion', - 'Snmpoid', - 'snmpcommunity', - 'snmpthreshold', - 'snmpversion', - 'application', - 'sitepath', - 'storename', - 'storefrontacctservice', - 'netprofile', - 'originhost', - 'originrealm', - 'hostipaddress', - 'vendorid', - 'productname', - 'firmwarerevision', - 'authapplicationid', - 'acctapplicationid', - 'inbandsecurityid', - 'supportedvendorids', - 'vendorspecificvendorid', - 'vendorspecificauthapplicationids', - 'vendorspecificacctapplicationids', - 'storedb', - 'storefrontcheckbackendservices', - 'trofscode', - 'trofsstring', - ] - - readonly_attrs = [ - 'lrtmconf', - 'lrtmconfstr', - 'dynamicresponsetimeout', - 'dynamicinterval', - 'multimetrictable', - 'dup_state', - 'dup_weight', - 'weight', - ] - - immutable_attrs = [ - 'monitorname', - 'type', - 'units1', - 'units3', - 'units4', - 'units2', - 'Snmpoid', - 'hostname', - 'servicename', - 'servicegroupname', - ] - - transforms = { - 'storefrontcheckbackendservices': ['bool_yes_no'], - 'secure': ['bool_yes_no'], - 'tos': ['bool_yes_no'], - 'validatecred': ['bool_yes_no'], - 'storefrontacctservice': ['bool_yes_no'], - 'iptunnel': ['bool_yes_no'], - 'transparent': ['bool_yes_no'], - 'reverse': ['bool_yes_no'], - 'lrtm': [lambda v: v.upper()], - 'storedb': [lambda v: v.upper()], - } - - lbmonitor_proxy = ConfigProxy( - actual=lbmonitor(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - ensure_feature_is_enabled(client, 'LB') - - if module.params['state'] == 'present': - log('Applying actions for state present') - if not lbmonitor_exists(client, module): - if not module.check_mode: - log('Adding monitor') - lbmonitor_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not lbmonitor_identical(client, module, lbmonitor_proxy): - - # Check if we try to change value of immutable attributes - immutables_changed = get_immutables_intersection(lbmonitor_proxy, diff_list(client, module, lbmonitor_proxy).keys()) - if immutables_changed != []: - diff = diff_list(client, module, lbmonitor_proxy) - msg = 'Cannot update immutable attributes %s' % (immutables_changed,) - module.fail_json(msg=msg, diff=diff, **module_result) - - if not module.check_mode: - log('Updating monitor') - lbmonitor_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - log('Doing nothing for monitor') - module_result['changed'] = False - - # Sanity check for result - log('Sanity checks for state present') - if not module.check_mode: - if not lbmonitor_exists(client, module): - module.fail_json(msg='lb monitor does not exist', **module_result) - if not lbmonitor_identical(client, module, lbmonitor_proxy): - module.fail_json( - msg='lb monitor is not configured correctly', - diff=diff_list(client, module, lbmonitor_proxy), - **module_result - ) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if lbmonitor_exists(client, module): - if not module.check_mode: - lbmonitor_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for result - log('Sanity checks for state absent') - if not module.check_mode: - if lbmonitor_exists(client, module): - module.fail_json(msg='lb monitor still exists', **module_result) - - module_result['actual_attributes'] = lbmonitor_proxy.get_actual_rw_attributes(filter='monitorname') - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_lb_vserver.py b/plugins/modules/network/netscaler/netscaler_lb_vserver.py deleted file mode 100644 index 087edd42c5..0000000000 --- a/plugins/modules/network/netscaler/netscaler_lb_vserver.py +++ /dev/null @@ -1,1941 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_lb_vserver -short_description: Manage load balancing vserver configuration -description: - - Manage load balancing vserver configuration - - This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - name: - description: - - >- - Name for the virtual server. Must begin with an ASCII alphanumeric or underscore C(_) character, and - must contain only ASCII alphanumeric, underscore, hash C(#), period C(.), space C( ), colon C(:), at sign - C(@), equal sign C(=), and hyphen C(-) characters. Can be changed after the virtual server is created. - - "Minimum length = 1" - - servicetype: - choices: - - 'HTTP' - - 'FTP' - - 'TCP' - - 'UDP' - - 'SSL' - - 'SSL_BRIDGE' - - 'SSL_TCP' - - 'DTLS' - - 'NNTP' - - 'DNS' - - 'DHCPRA' - - 'ANY' - - 'SIP_UDP' - - 'SIP_TCP' - - 'SIP_SSL' - - 'DNS_TCP' - - 'RTSP' - - 'PUSH' - - 'SSL_PUSH' - - 'RADIUS' - - 'RDP' - - 'MYSQL' - - 'MSSQL' - - 'DIAMETER' - - 'SSL_DIAMETER' - - 'TFTP' - - 'ORACLE' - - 'SMPP' - - 'SYSLOGTCP' - - 'SYSLOGUDP' - - 'FIX' - - 'SSL_FIX' - description: - - "Protocol used by the service (also called the service type)." - - ipv46: - description: - - "IPv4 or IPv6 address to assign to the virtual server." - - ippattern: - description: - - >- - IP address pattern, in dotted decimal notation, for identifying packets to be accepted by the virtual - server. The IP Mask parameter specifies which part of the destination IP address is matched against - the pattern. Mutually exclusive with the IP Address parameter. - - >- - For example, if the IP pattern assigned to the virtual server is C(198.51.100.0) and the IP mask is - C(255.255.240.0) (a forward mask), the first 20 bits in the destination IP addresses are matched with - the first 20 bits in the pattern. The virtual server accepts requests with IP addresses that range - from C(198.51.96.1) to C(198.51.111.254). You can also use a pattern such as C(0.0.2.2) and a mask such as - C(0.0.255.255) (a reverse mask). - - >- - If a destination IP address matches more than one IP pattern, the pattern with the longest match is - selected, and the associated virtual server processes the request. For example, if virtual servers - C(vs1) and C(vs2) have the same IP pattern, C(0.0.100.128), but different IP masks of C(0.0.255.255) and - C(0.0.224.255), a destination IP address of C(198.51.100.128) has the longest match with the IP pattern of - vs1. If a destination IP address matches two or more virtual servers to the same extent, the request - is processed by the virtual server whose port number matches the port number in the request. - - ipmask: - description: - - >- - IP mask, in dotted decimal notation, for the IP Pattern parameter. Can have leading or trailing - non-zero octets (for example, C(255.255.240.0) or C(0.0.255.255)). Accordingly, the mask specifies whether - the first n bits or the last n bits of the destination IP address in a client request are to be - matched with the corresponding bits in the IP pattern. The former is called a forward mask. The - latter is called a reverse mask. - - port: - description: - - "Port number for the virtual server." - - "Range C(1) - C(65535)" - - "* in CLI is represented as C(65535) in NITRO API" - - range: - description: - - >- - Number of IP addresses that the appliance must generate and assign to the virtual server. The virtual - server then functions as a network virtual server, accepting traffic on any of the generated IP - addresses. The IP addresses are generated automatically, as follows: - - >- - * For a range of n, the last octet of the address specified by the IP Address parameter increments - n-1 times. - - "* If the last octet exceeds 255, it rolls over to 0 and the third octet increments by 1." - - >- - Note: The Range parameter assigns multiple IP addresses to one virtual server. To generate an array - of virtual servers, each of which owns only one IP address, use brackets in the IP Address and Name - parameters to specify the range. For example: - - "add lb vserver my_vserver[1-3] HTTP 192.0.2.[1-3] 80." - - "Minimum value = C(1)" - - "Maximum value = C(254)" - - persistencetype: - choices: - - 'SOURCEIP' - - 'COOKIEINSERT' - - 'SSLSESSION' - - 'RULE' - - 'URLPASSIVE' - - 'CUSTOMSERVERID' - - 'DESTIP' - - 'SRCIPDESTIP' - - 'CALLID' - - 'RTSPSID' - - 'DIAMETER' - - 'FIXSESSION' - - 'NONE' - description: - - "Type of persistence for the virtual server. Available settings function as follows:" - - "* C(SOURCEIP) - Connections from the same client IP address belong to the same persistence session." - - >- - * C(COOKIEINSERT) - Connections that have the same HTTP Cookie, inserted by a Set-Cookie directive from - a server, belong to the same persistence session. - - "* C(SSLSESSION) - Connections that have the same SSL Session ID belong to the same persistence session." - - >- - * C(CUSTOMSERVERID) - Connections with the same server ID form part of the same session. For this - persistence type, set the Server ID (CustomServerID) parameter for each service and configure the - Rule parameter to identify the server ID in a request. - - "* C(RULE) - All connections that match a user defined rule belong to the same persistence session." - - >- - * C(URLPASSIVE) - Requests that have the same server ID in the URL query belong to the same persistence - session. The server ID is the hexadecimal representation of the IP address and port of the service to - which the request must be forwarded. This persistence type requires a rule to identify the server ID - in the request. - - "* C(DESTIP) - Connections to the same destination IP address belong to the same persistence session." - - >- - * C(SRCIPDESTIP) - Connections that have the same source IP address and destination IP address belong to - the same persistence session. - - "* C(CALLID) - Connections that have the same CALL-ID SIP header belong to the same persistence session." - - "* C(RTSPSID) - Connections that have the same RTSP Session ID belong to the same persistence session." - - >- - * FIXSESSION - Connections that have the same SenderCompID and TargetCompID values belong to the same - persistence session. - - timeout: - description: - - "Time period for which a persistence session is in effect." - - "Minimum value = C(0)" - - "Maximum value = C(1440)" - - persistencebackup: - choices: - - 'SOURCEIP' - - 'NONE' - description: - - >- - Backup persistence type for the virtual server. Becomes operational if the primary persistence - mechanism fails. - - backuppersistencetimeout: - description: - - "Time period for which backup persistence is in effect." - - "Minimum value = C(2)" - - "Maximum value = C(1440)" - - lbmethod: - choices: - - 'ROUNDROBIN' - - 'LEASTCONNECTION' - - 'LEASTRESPONSETIME' - - 'URLHASH' - - 'DOMAINHASH' - - 'DESTINATIONIPHASH' - - 'SOURCEIPHASH' - - 'SRCIPDESTIPHASH' - - 'LEASTBANDWIDTH' - - 'LEASTPACKETS' - - 'TOKEN' - - 'SRCIPSRCPORTHASH' - - 'LRTM' - - 'CALLIDHASH' - - 'CUSTOMLOAD' - - 'LEASTREQUEST' - - 'AUDITLOGHASH' - - 'STATICPROXIMITY' - description: - - "Load balancing method. The available settings function as follows:" - - >- - * C(ROUNDROBIN) - Distribute requests in rotation, regardless of the load. Weights can be assigned to - services to enforce weighted round robin distribution. - - "* C(LEASTCONNECTION) (default) - Select the service with the fewest connections." - - "* C(LEASTRESPONSETIME) - Select the service with the lowest average response time." - - "* C(LEASTBANDWIDTH) - Select the service currently handling the least traffic." - - "* C(LEASTPACKETS) - Select the service currently serving the lowest number of packets per second." - - "* C(CUSTOMLOAD) - Base service selection on the SNMP metrics obtained by custom load monitors." - - >- - * C(LRTM) - Select the service with the lowest response time. Response times are learned through - monitoring probes. This method also takes the number of active connections into account. - - >- - Also available are a number of hashing methods, in which the appliance extracts a predetermined - portion of the request, creates a hash of the portion, and then checks whether any previous requests - had the same hash value. If it finds a match, it forwards the request to the service that served - those previous requests. Following are the hashing methods: - - "* C(URLHASH) - Create a hash of the request URL (or part of the URL)." - - >- - * C(DOMAINHASH) - Create a hash of the domain name in the request (or part of the domain name). The - domain name is taken from either the URL or the Host header. If the domain name appears in both - locations, the URL is preferred. If the request does not contain a domain name, the load balancing - method defaults to C(LEASTCONNECTION). - - "* C(DESTINATIONIPHASH) - Create a hash of the destination IP address in the IP header." - - "* C(SOURCEIPHASH) - Create a hash of the source IP address in the IP header." - - >- - * C(TOKEN) - Extract a token from the request, create a hash of the token, and then select the service - to which any previous requests with the same token hash value were sent. - - >- - * C(SRCIPDESTIPHASH) - Create a hash of the string obtained by concatenating the source IP address and - destination IP address in the IP header. - - "* C(SRCIPSRCPORTHASH) - Create a hash of the source IP address and source port in the IP header." - - "* C(CALLIDHASH) - Create a hash of the SIP Call-ID header." - - hashlength: - description: - - >- - Number of bytes to consider for the hash value used in the URLHASH and DOMAINHASH load balancing - methods. - - "Minimum value = C(1)" - - "Maximum value = C(4096)" - - netmask: - description: - - >- - IPv4 subnet mask to apply to the destination IP address or source IP address when the load balancing - method is C(DESTINATIONIPHASH) or C(SOURCEIPHASH). - - "Minimum length = 1" - - v6netmasklen: - description: - - >- - Number of bits to consider in an IPv6 destination or source IP address, for creating the hash that is - required by the C(DESTINATIONIPHASH) and C(SOURCEIPHASH) load balancing methods. - - "Minimum value = C(1)" - - "Maximum value = C(128)" - - backuplbmethod: - choices: - - 'ROUNDROBIN' - - 'LEASTCONNECTION' - - 'LEASTRESPONSETIME' - - 'SOURCEIPHASH' - - 'LEASTBANDWIDTH' - - 'LEASTPACKETS' - - 'CUSTOMLOAD' - description: - - "Backup load balancing method. Becomes operational if the primary load balancing me" - - "thod fails or cannot be used." - - "Valid only if the primary method is based on static proximity." - - cookiename: - description: - - >- - Use this parameter to specify the cookie name for C(COOKIE) persistence type. It specifies the name of - cookie with a maximum of 32 characters. If not specified, cookie name is internally generated. - - - listenpolicy: - description: - - >- - Default syntax expression identifying traffic accepted by the virtual server. Can be either an - expression (for example, C(CLIENT.IP.DST.IN_SUBNET(192.0.2.0/24)) or the name of a named expression. In - the above example, the virtual server accepts all requests whose destination IP address is in the - 192.0.2.0/24 subnet. - - listenpriority: - description: - - >- - Integer specifying the priority of the listen policy. A higher number specifies a lower priority. If - a request matches the listen policies of more than one virtual server the virtual server whose listen - policy has the highest priority (the lowest priority number) accepts the request. - - "Minimum value = C(0)" - - "Maximum value = C(101)" - - resrule: - description: - - >- - Default syntax expression specifying which part of a server's response to use for creating rule based - persistence sessions (persistence type RULE). Can be either an expression or the name of a named - expression. - - "Example:" - - 'C(HTTP.RES.HEADER("setcookie").VALUE(0).TYPECAST_NVLIST_T("=",";").VALUE("server1")).' - - persistmask: - description: - - "Persistence mask for IP based persistence types, for IPv4 virtual servers." - - "Minimum length = 1" - - v6persistmasklen: - description: - - "Persistence mask for IP based persistence types, for IPv6 virtual servers." - - "Minimum value = C(1)" - - "Maximum value = C(128)" - - rtspnat: - description: - - "Use network address translation (NAT) for RTSP data connections." - type: bool - - m: - choices: - - 'IP' - - 'MAC' - - 'IPTUNNEL' - - 'TOS' - description: - - "Redirection mode for load balancing. Available settings function as follows:" - - >- - * C(IP) - Before forwarding a request to a server, change the destination IP address to the server's IP - address. - - >- - * C(MAC) - Before forwarding a request to a server, change the destination MAC address to the server's - MAC address. The destination IP address is not changed. MAC-based redirection mode is used mostly in - firewall load balancing deployments. - - >- - * C(IPTUNNEL) - Perform IP-in-IP encapsulation for client IP packets. In the outer IP headers, set the - destination IP address to the IP address of the server and the source IP address to the subnet IP - (SNIP). The client IP packets are not modified. Applicable to both IPv4 and IPv6 packets. - - "* C(TOS) - Encode the virtual server's TOS ID in the TOS field of the IP header." - - "You can use either the C(IPTUNNEL) or the C(TOS) option to implement Direct Server Return (DSR)." - - tosid: - description: - - >- - TOS ID of the virtual server. Applicable only when the load balancing redirection mode is set to TOS. - - "Minimum value = C(1)" - - "Maximum value = C(63)" - - datalength: - description: - - >- - Length of the token to be extracted from the data segment of an incoming packet, for use in the token - method of load balancing. The length of the token, specified in bytes, must not be greater than 24 - KB. Applicable to virtual servers of type TCP. - - "Minimum value = C(1)" - - "Maximum value = C(100)" - - dataoffset: - description: - - >- - Offset to be considered when extracting a token from the TCP payload. Applicable to virtual servers, - of type TCP, using the token method of load balancing. Must be within the first 24 KB of the TCP - payload. - - "Minimum value = C(0)" - - "Maximum value = C(25400)" - - sessionless: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Perform load balancing on a per-packet basis, without establishing sessions. Recommended for load - balancing of intrusion detection system (IDS) servers and scenarios involving direct server return - (DSR), where session information is unnecessary. - - connfailover: - choices: - - 'DISABLED' - - 'STATEFUL' - - 'STATELESS' - description: - - >- - Mode in which the connection failover feature must operate for the virtual server. After a failover, - established TCP connections and UDP packet flows are kept active and resumed on the secondary - appliance. Clients remain connected to the same servers. Available settings function as follows: - - >- - * C(STATEFUL) - The primary appliance shares state information with the secondary appliance, in real - time, resulting in some runtime processing overhead. - - >- - * C(STATELESS) - State information is not shared, and the new primary appliance tries to re-create the - packet flow on the basis of the information contained in the packets it receives. - - "* C(DISABLED) - Connection failover does not occur." - - redirurl: - description: - - "URL to which to redirect traffic if the virtual server becomes unavailable." - - >- - WARNING! Make sure that the domain in the URL does not match the domain specified for a content - switching policy. If it does, requests are continuously redirected to the unavailable virtual server. - - "Minimum length = 1" - - cacheable: - description: - - >- - Route cacheable requests to a cache redirection virtual server. The load balancing virtual server can - forward requests only to a transparent cache redirection virtual server that has an IP address and - port combination of *:80, so such a cache redirection virtual server must be configured on the - appliance. - type: bool - - clttimeout: - description: - - "Idle time, in seconds, after which a client connection is terminated." - - "Minimum value = C(0)" - - "Maximum value = C(31536000)" - - somethod: - choices: - - 'CONNECTION' - - 'DYNAMICCONNECTION' - - 'BANDWIDTH' - - 'HEALTH' - - 'NONE' - description: - - "Type of threshold that, when exceeded, triggers spillover. Available settings function as follows:" - - "* C(CONNECTION) - Spillover occurs when the number of client connections exceeds the threshold." - - >- - * DYNAMICCONNECTION - Spillover occurs when the number of client connections at the virtual server - exceeds the sum of the maximum client (Max Clients) settings for bound services. Do not specify a - spillover threshold for this setting, because the threshold is implied by the Max Clients settings of - bound services. - - >- - * C(BANDWIDTH) - Spillover occurs when the bandwidth consumed by the virtual server's incoming and - outgoing traffic exceeds the threshold. - - >- - * C(HEALTH) - Spillover occurs when the percentage of weights of the services that are UP drops below - the threshold. For example, if services svc1, svc2, and svc3 are bound to a virtual server, with - weights 1, 2, and 3, and the spillover threshold is 50%, spillover occurs if svc1 and svc3 or svc2 - and svc3 transition to DOWN. - - "* C(NONE) - Spillover does not occur." - - sopersistence: - choices: - - 'enabled' - - 'disabled' - description: - - >- - If spillover occurs, maintain source IP address based persistence for both primary and backup virtual - servers. - - sopersistencetimeout: - description: - - "Timeout for spillover persistence, in minutes." - - "Minimum value = C(2)" - - "Maximum value = C(1440)" - - healththreshold: - description: - - >- - Threshold in percent of active services below which vserver state is made down. If this threshold is - 0, vserver state will be up even if one bound service is up. - - "Minimum value = C(0)" - - "Maximum value = C(100)" - - sothreshold: - description: - - >- - Threshold at which spillover occurs. Specify an integer for the C(CONNECTION) spillover method, a - bandwidth value in kilobits per second for the C(BANDWIDTH) method (do not enter the units), or a - percentage for the C(HEALTH) method (do not enter the percentage symbol). - - "Minimum value = C(1)" - - "Maximum value = C(4294967287)" - - sobackupaction: - choices: - - 'DROP' - - 'ACCEPT' - - 'REDIRECT' - description: - - >- - Action to be performed if spillover is to take effect, but no backup chain to spillover is usable or - exists. - - redirectportrewrite: - choices: - - 'enabled' - - 'disabled' - description: - - "Rewrite the port and change the protocol to ensure successful HTTP redirects from services." - - downstateflush: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Flush all active transactions associated with a virtual server whose state transitions from UP to - DOWN. Do not enable this option for applications that must complete their transactions. - - disableprimaryondown: - choices: - - 'enabled' - - 'disabled' - description: - - >- - If the primary virtual server goes down, do not allow it to return to primary status until manually - enabled. - - insertvserveripport: - choices: - - 'OFF' - - 'VIPADDR' - - 'V6TOV4MAPPING' - description: - - >- - Insert an HTTP header, whose value is the IP address and port number of the virtual server, before - forwarding a request to the server. The format of the header is : _, where vipHeader is the name that you specify for the header. If the virtual - server has an IPv6 address, the address in the header is enclosed in brackets ([ and ]) to separate - it from the port number. If you have mapped an IPv4 address to a virtual server's IPv6 address, the - value of this parameter determines which IP address is inserted in the header, as follows: - - >- - * C(VIPADDR) - Insert the IP address of the virtual server in the HTTP header regardless of whether the - virtual server has an IPv4 address or an IPv6 address. A mapped IPv4 address, if configured, is - ignored. - - >- - * C(V6TOV4MAPPING) - Insert the IPv4 address that is mapped to the virtual server's IPv6 address. If a - mapped IPv4 address is not configured, insert the IPv6 address. - - "* C(OFF) - Disable header insertion." - - vipheader: - description: - - "Name for the inserted header. The default name is vip-header." - - "Minimum length = 1" - - authenticationhost: - description: - - >- - Fully qualified domain name (FQDN) of the authentication virtual server to which the user must be - redirected for authentication. Make sure that the Authentication parameter is set to C(yes). - - "Minimum length = 3" - - "Maximum length = 252" - - authentication: - description: - - "Enable or disable user authentication." - type: bool - - authn401: - description: - - "Enable or disable user authentication with HTTP 401 responses." - type: bool - - authnvsname: - description: - - "Name of an authentication virtual server with which to authenticate users." - - "Minimum length = 1" - - "Maximum length = 252" - - push: - choices: - - 'enabled' - - 'disabled' - description: - - "Process traffic with the push virtual server that is bound to this load balancing virtual server." - - pushvserver: - description: - - >- - Name of the load balancing virtual server, of type PUSH or SSL_PUSH, to which the server pushes - updates received on the load balancing virtual server that you are configuring. - - "Minimum length = 1" - - pushlabel: - description: - - >- - Expression for extracting a label from the server's response. Can be either an expression or the name - of a named expression. - - pushmulticlients: - description: - - >- - Allow multiple Web 2.0 connections from the same client to connect to the virtual server and expect - updates. - type: bool - - tcpprofilename: - description: - - "Name of the TCP profile whose settings are to be applied to the virtual server." - - "Minimum length = 1" - - "Maximum length = 127" - - httpprofilename: - description: - - "Name of the HTTP profile whose settings are to be applied to the virtual server." - - "Minimum length = 1" - - "Maximum length = 127" - - dbprofilename: - description: - - "Name of the DB profile whose settings are to be applied to the virtual server." - - "Minimum length = 1" - - "Maximum length = 127" - - comment: - description: - - "Any comments that you might want to associate with the virtual server." - - l2conn: - description: - - >- - Use Layer 2 parameters (channel number, MAC address, and VLAN ID) in addition to the 4-tuple (::::) that is used to identify a connection. Allows - multiple TCP and non-TCP connections with the same 4-tuple to co-exist on the NetScaler appliance. - type: bool - - oracleserverversion: - choices: - - '10G' - - '11G' - description: - - "Oracle server version." - - mssqlserverversion: - choices: - - '70' - - '2000' - - '2000SP1' - - '2005' - - '2008' - - '2008R2' - - '2012' - - '2014' - description: - - >- - For a load balancing virtual server of type C(MSSQL), the Microsoft SQL Server version. Set this - parameter if you expect some clients to run a version different from the version of the database. - This setting provides compatibility between the client-side and server-side connections by ensuring - that all communication conforms to the server's version. - - mysqlprotocolversion: - description: - - "MySQL protocol version that the virtual server advertises to clients." - - mysqlserverversion: - description: - - "MySQL server version string that the virtual server advertises to clients." - - "Minimum length = 1" - - "Maximum length = 31" - - mysqlcharacterset: - description: - - "Character set that the virtual server advertises to clients." - - mysqlservercapabilities: - description: - - "Server capabilities that the virtual server advertises to clients." - - appflowlog: - choices: - - 'enabled' - - 'disabled' - description: - - "Apply AppFlow logging to the virtual server." - - netprofile: - description: - - >- - Name of the network profile to associate with the virtual server. If you set this parameter, the - virtual server uses only the IP addresses in the network profile as source IP addresses when - initiating connections with servers. - - "Minimum length = 1" - - "Maximum length = 127" - - icmpvsrresponse: - choices: - - 'PASSIVE' - - 'ACTIVE' - description: - - >- - How the NetScaler appliance responds to ping requests received for an IP address that is common to - one or more virtual servers. Available settings function as follows: - - >- - * If set to C(PASSIVE) on all the virtual servers that share the IP address, the appliance always - responds to the ping requests. - - >- - * If set to C(ACTIVE) on all the virtual servers that share the IP address, the appliance responds to - the ping requests if at least one of the virtual servers is UP. Otherwise, the appliance does not - respond. - - >- - * If set to C(ACTIVE) on some virtual servers and PASSIVE on the others, the appliance responds if at - least one virtual server with the ACTIVE setting is UP. Otherwise, the appliance does not respond. - - >- - Note: This parameter is available at the virtual server level. A similar parameter, ICMP Response, is - available at the IP address level, for IPv4 addresses of type VIP. To set that parameter, use the add - ip command in the CLI or the Create IP dialog box in the GUI. - - rhistate: - choices: - - 'PASSIVE' - - 'ACTIVE' - description: - - >- - Route Health Injection (RHI) functionality of the NetSaler appliance for advertising the route of the - VIP address associated with the virtual server. When Vserver RHI Level (RHI) parameter is set to - VSVR_CNTRLD, the following are different RHI behaviors for the VIP address on the basis of RHIstate - (RHI STATE) settings on the virtual servers associated with the VIP address: - - >- - * If you set C(rhistate) to C(PASSIVE) on all virtual servers, the NetScaler ADC always advertises the - route for the VIP address. - - >- - * If you set C(rhistate) to C(ACTIVE) on all virtual servers, the NetScaler ADC advertises the route for - the VIP address if at least one of the associated virtual servers is in UP state. - - >- - * If you set C(rhistate) to C(ACTIVE) on some and PASSIVE on others, the NetScaler ADC advertises the - route for the VIP address if at least one of the associated virtual servers, whose C(rhistate) set to - C(ACTIVE), is in UP state. - - newservicerequest: - description: - - >- - Number of requests, or percentage of the load on existing services, by which to increase the load on - a new service at each interval in slow-start mode. A non-zero value indicates that slow-start is - applicable. A zero value indicates that the global RR startup parameter is applied. Changing the - value to zero will cause services currently in slow start to take the full traffic as determined by - the LB method. Subsequently, any new services added will use the global RR factor. - - newservicerequestunit: - choices: - - 'PER_SECOND' - - 'PERCENT' - description: - - "Units in which to increment load at each interval in slow-start mode." - - newservicerequestincrementinterval: - description: - - >- - Interval, in seconds, between successive increments in the load on a new service or a service whose - state has just changed from DOWN to UP. A value of 0 (zero) specifies manual slow start. - - "Minimum value = C(0)" - - "Maximum value = C(3600)" - - minautoscalemembers: - description: - - "Minimum number of members expected to be present when vserver is used in Autoscale." - - "Minimum value = C(0)" - - "Maximum value = C(5000)" - - maxautoscalemembers: - description: - - "Maximum number of members expected to be present when vserver is used in Autoscale." - - "Minimum value = C(0)" - - "Maximum value = C(5000)" - - persistavpno: - description: - - "Persist AVP number for Diameter Persistency." - - "In case this AVP is not defined in Base RFC 3588 and it is nested inside a Grouped AVP," - - "define a sequence of AVP numbers (max 3) in order of parent to child. So say persist AVP number X" - - "is nested inside AVP Y which is nested in Z, then define the list as Z Y X." - - "Minimum value = C(1)" - - skippersistency: - choices: - - 'Bypass' - - 'ReLb' - - 'None' - description: - - >- - This argument decides the behavior incase the service which is selected from an existing persistence - session has reached threshold. - - td: - description: - - >- - Integer value that uniquely identifies the traffic domain in which you want to configure the entity. - If you do not specify an ID, the entity becomes part of the default traffic domain, which has an ID - of 0. - - "Minimum value = C(0)" - - "Maximum value = C(4094)" - - authnprofile: - description: - - "Name of the authentication profile to be used when authentication is turned on." - - macmoderetainvlan: - choices: - - 'enabled' - - 'disabled' - description: - - "This option is used to retain vlan information of incoming packet when macmode is enabled." - - dbslb: - choices: - - 'enabled' - - 'disabled' - description: - - "Enable database specific load balancing for MySQL and MSSQL service types." - - dns64: - choices: - - 'enabled' - - 'disabled' - description: - - "This argument is for enabling/disabling the C(dns64) on lbvserver." - - bypassaaaa: - description: - - >- - If this option is enabled while resolving DNS64 query AAAA queries are not sent to back end dns - server. - type: bool - - recursionavailable: - description: - - >- - When set to YES, this option causes the DNS replies from this vserver to have the RA bit turned on. - Typically one would set this option to YES, when the vserver is load balancing a set of DNS servers - thatsupport recursive queries. - type: bool - - processlocal: - choices: - - 'enabled' - - 'disabled' - description: - - >- - By turning on this option packets destined to a vserver in a cluster will not under go any steering. - Turn this option for single packet request response mode or when the upstream device is performing a - proper RSS for connection based distribution. - - dnsprofilename: - description: - - >- - Name of the DNS profile to be associated with the VServer. DNS profile properties will be applied to - the transactions processed by a VServer. This parameter is valid only for DNS and DNS-TCP VServers. - - "Minimum length = 1" - - "Maximum length = 127" - - servicebindings: - description: - - List of services along with the weights that are load balanced. - - The following suboptions are available. - suboptions: - servicename: - description: - - "Service to bind to the virtual server." - - "Minimum length = 1" - weight: - description: - - "Weight to assign to the specified service." - - "Minimum value = C(1)" - - "Maximum value = C(100)" - - servicegroupbindings: - description: - - List of service groups along with the weights that are load balanced. - - The following suboptions are available. - suboptions: - servicegroupname: - description: - - "The service group name bound to the selected load balancing virtual server." - weight: - description: - - >- - Integer specifying the weight of the service. A larger number specifies a greater weight. Defines the - capacity of the service relative to the other services in the load balancing configuration. - Determines the priority given to the service in load balancing decisions. - - "Minimum value = C(1)" - - "Maximum value = C(100)" - - ssl_certkey: - description: - - The name of the ssl certificate that is bound to this service. - - The ssl certificate must already exist. - - Creating the certificate can be done with the M(netscaler_ssl_certkey) module. - - This option is only applicable only when C(servicetype) is C(SSL). - - disabled: - description: - - When set to C(yes) the lb vserver will be disabled. - - When set to C(no) the lb vserver will be enabled. - - >- - Note that due to limitations of the underlying NITRO API a C(disabled) state change alone - does not cause the module result to report a changed status. - type: bool - default: 'no' - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -# Netscaler services service-http-1, service-http-2 must have been already created with the netscaler_service module - -- name: Create a load balancing vserver bound to services - delegate_to: localhost - netscaler_lb_vserver: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - validate_certs: no - - state: present - - name: lb_vserver_1 - servicetype: HTTP - timeout: 12 - ipv46: 6.93.3.3 - port: 80 - servicebindings: - - servicename: service-http-1 - weight: 80 - - servicename: service-http-2 - weight: 20 - -# Service group service-group-1 must have been already created with the netscaler_servicegroup module - -- name: Create load balancing vserver bound to servicegroup - delegate_to: localhost - netscaler_lb_vserver: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - validate_certs: no - state: present - - name: lb_vserver_2 - servicetype: HTTP - ipv46: 6.92.2.2 - port: 80 - timeout: 10 - servicegroupbindings: - - servicegroupname: service-group-1 -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: ['message 1', 'message 2'] - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: { 'clttimeout': 'difference. ours: (float) 10.0 other: (float) 20.0' } -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import ( - ConfigProxy, - get_nitro_client, - netscaler_common_arguments, - log, - loglines, - get_immutables_intersection, - ensure_feature_is_enabled -) -import copy - -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver import lbvserver - from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver_servicegroup_binding import lbvserver_servicegroup_binding - from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver_service_binding import lbvserver_service_binding - from nssrc.com.citrix.netscaler.nitro.resource.config.ssl.sslvserver_sslcertkey_binding import sslvserver_sslcertkey_binding - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - - PYTHON_SDK_IMPORTED = True -except ImportError as e: - IMPORT_ERROR = str(e) - PYTHON_SDK_IMPORTED = False - - -def lb_vserver_exists(client, module): - log('Checking if lb vserver exists') - if lbvserver.count_filtered(client, 'name:%s' % module.params['name']) > 0: - return True - else: - return False - - -def lb_vserver_identical(client, module, lbvserver_proxy): - log('Checking if configured lb vserver is identical') - lbvserver_list = lbvserver.get_filtered(client, 'name:%s' % module.params['name']) - if lbvserver_proxy.has_equal_attributes(lbvserver_list[0]): - return True - else: - return False - - -def lb_vserver_diff(client, module, lbvserver_proxy): - lbvserver_list = lbvserver.get_filtered(client, 'name:%s' % module.params['name']) - return lbvserver_proxy.diff_object(lbvserver_list[0]) - - -def get_configured_service_bindings(client, module): - log('Getting configured service bindings') - - readwrite_attrs = [ - 'weight', - 'name', - 'servicename', - 'servicegroupname' - ] - readonly_attrs = [ - 'preferredlocation', - 'vserverid', - 'vsvrbindsvcip', - 'servicetype', - 'cookieipport', - 'port', - 'vsvrbindsvcport', - 'curstate', - 'ipv46', - 'dynamicweight', - ] - - configured_bindings = {} - if 'servicebindings' in module.params and module.params['servicebindings'] is not None: - for binding in module.params['servicebindings']: - attribute_values_dict = copy.deepcopy(binding) - attribute_values_dict['name'] = module.params['name'] - key = binding['servicename'].strip() - configured_bindings[key] = ConfigProxy( - actual=lbvserver_service_binding(), - client=client, - attribute_values_dict=attribute_values_dict, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - ) - return configured_bindings - - -def get_configured_servicegroup_bindings(client, module): - log('Getting configured service group bindings') - readwrite_attrs = [ - 'weight', - 'name', - 'servicename', - 'servicegroupname', - ] - readonly_attrs = [] - - configured_bindings = {} - - if 'servicegroupbindings' in module.params and module.params['servicegroupbindings'] is not None: - for binding in module.params['servicegroupbindings']: - attribute_values_dict = copy.deepcopy(binding) - attribute_values_dict['name'] = module.params['name'] - key = binding['servicegroupname'].strip() - configured_bindings[key] = ConfigProxy( - actual=lbvserver_servicegroup_binding(), - client=client, - attribute_values_dict=attribute_values_dict, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - ) - - return configured_bindings - - -def get_actual_service_bindings(client, module): - log('Getting actual service bindings') - bindings = {} - try: - if lbvserver_service_binding.count(client, module.params['name']) == 0: - return bindings - except nitro_exception as e: - if e.errorcode == 258: - return bindings - else: - raise - - bindigs_list = lbvserver_service_binding.get(client, module.params['name']) - - for item in bindigs_list: - key = item.servicename - bindings[key] = item - - return bindings - - -def get_actual_servicegroup_bindings(client, module): - log('Getting actual service group bindings') - bindings = {} - - try: - if lbvserver_servicegroup_binding.count(client, module.params['name']) == 0: - return bindings - except nitro_exception as e: - if e.errorcode == 258: - return bindings - else: - raise - - bindigs_list = lbvserver_servicegroup_binding.get(client, module.params['name']) - - for item in bindigs_list: - key = item.servicegroupname - bindings[key] = item - - return bindings - - -def service_bindings_identical(client, module): - log('service_bindings_identical') - - # Compare service keysets - configured_service_bindings = get_configured_service_bindings(client, module) - service_bindings = get_actual_service_bindings(client, module) - configured_keyset = set(configured_service_bindings.keys()) - service_keyset = set(service_bindings.keys()) - if len(configured_keyset ^ service_keyset) > 0: - return False - - # Compare service item to item - for key in configured_service_bindings.keys(): - conf = configured_service_bindings[key] - serv = service_bindings[key] - log('s diff %s' % conf.diff_object(serv)) - if not conf.has_equal_attributes(serv): - return False - - # Fallthrough to success - return True - - -def servicegroup_bindings_identical(client, module): - log('servicegroup_bindings_identical') - - # Compare servicegroup keysets - configured_servicegroup_bindings = get_configured_servicegroup_bindings(client, module) - servicegroup_bindings = get_actual_servicegroup_bindings(client, module) - configured_keyset = set(configured_servicegroup_bindings.keys()) - service_keyset = set(servicegroup_bindings.keys()) - log('len %s' % len(configured_keyset ^ service_keyset)) - if len(configured_keyset ^ service_keyset) > 0: - return False - - # Compare servicegroup item to item - for key in configured_servicegroup_bindings.keys(): - conf = configured_servicegroup_bindings[key] - serv = servicegroup_bindings[key] - log('sg diff %s' % conf.diff_object(serv)) - if not conf.has_equal_attributes(serv): - return False - - # Fallthrough to success - return True - - -def sync_service_bindings(client, module): - log('sync_service_bindings') - - actual_bindings = get_actual_service_bindings(client, module) - configured_bindigns = get_configured_service_bindings(client, module) - - # Delete actual but not configured - delete_keys = list(set(actual_bindings.keys()) - set(configured_bindigns.keys())) - for key in delete_keys: - log('Deleting service binding %s' % key) - actual_bindings[key].servicegroupname = '' - actual_bindings[key].delete(client, actual_bindings[key]) - - # Add configured but not in actual - add_keys = list(set(configured_bindigns.keys()) - set(actual_bindings.keys())) - for key in add_keys: - log('Adding service binding %s' % key) - configured_bindigns[key].add() - - # Update existing if changed - modify_keys = list(set(configured_bindigns.keys()) & set(actual_bindings.keys())) - for key in modify_keys: - if not configured_bindigns[key].has_equal_attributes(actual_bindings[key]): - log('Updating service binding %s' % key) - actual_bindings[key].servicegroupname = '' - actual_bindings[key].delete(client, actual_bindings[key]) - configured_bindigns[key].add() - - -def sync_servicegroup_bindings(client, module): - log('sync_servicegroup_bindings') - - actual_bindings = get_actual_servicegroup_bindings(client, module) - configured_bindigns = get_configured_servicegroup_bindings(client, module) - - # Delete actual but not configured - delete_keys = list(set(actual_bindings.keys()) - set(configured_bindigns.keys())) - for key in delete_keys: - log('Deleting servicegroup binding %s' % key) - actual_bindings[key].servicename = None - actual_bindings[key].delete(client, actual_bindings[key]) - - # Add configured but not in actual - add_keys = list(set(configured_bindigns.keys()) - set(actual_bindings.keys())) - for key in add_keys: - log('Adding servicegroup binding %s' % key) - configured_bindigns[key].add() - - # Update existing if changed - modify_keys = list(set(configured_bindigns.keys()) & set(actual_bindings.keys())) - for key in modify_keys: - if not configured_bindigns[key].has_equal_attributes(actual_bindings[key]): - log('Updating servicegroup binding %s' % key) - actual_bindings[key].servicename = None - actual_bindings[key].delete(client, actual_bindings[key]) - configured_bindigns[key].add() - - -def ssl_certkey_bindings_identical(client, module): - log('Entering ssl_certkey_bindings_identical') - vservername = module.params['name'] - - if sslvserver_sslcertkey_binding.count(client, vservername) == 0: - bindings = [] - else: - bindings = sslvserver_sslcertkey_binding.get(client, vservername) - - log('Existing certs %s' % bindings) - - if module.params['ssl_certkey'] is None: - if len(bindings) == 0: - return True - else: - return False - else: - certificate_list = [item.certkeyname for item in bindings] - log('certificate_list %s' % certificate_list) - if certificate_list == [module.params['ssl_certkey']]: - return True - else: - return False - - -def ssl_certkey_bindings_sync(client, module): - log('Syncing ssl certificates') - vservername = module.params['name'] - if sslvserver_sslcertkey_binding.count(client, vservername) == 0: - bindings = [] - else: - bindings = sslvserver_sslcertkey_binding.get(client, vservername) - log('bindings len is %s' % len(bindings)) - - # Delete existing bindings - for binding in bindings: - sslvserver_sslcertkey_binding.delete(client, binding) - - # Add binding if appropriate - if module.params['ssl_certkey'] is not None: - binding = sslvserver_sslcertkey_binding() - binding.vservername = module.params['name'] - binding.certkeyname = module.params['ssl_certkey'] - sslvserver_sslcertkey_binding.add(client, binding) - - -def do_state_change(client, module, lbvserver_proxy): - if module.params['disabled']: - log('Disabling lb server') - result = lbvserver.disable(client, lbvserver_proxy.actual) - else: - log('Enabling lb server') - result = lbvserver.enable(client, lbvserver_proxy.actual) - return result - - -def main(): - - module_specific_arguments = dict( - name=dict(type='str'), - servicetype=dict( - type='str', - choices=[ - 'HTTP', - 'FTP', - 'TCP', - 'UDP', - 'SSL', - 'SSL_BRIDGE', - 'SSL_TCP', - 'DTLS', - 'NNTP', - 'DNS', - 'DHCPRA', - 'ANY', - 'SIP_UDP', - 'SIP_TCP', - 'SIP_SSL', - 'DNS_TCP', - 'RTSP', - 'PUSH', - 'SSL_PUSH', - 'RADIUS', - 'RDP', - 'MYSQL', - 'MSSQL', - 'DIAMETER', - 'SSL_DIAMETER', - 'TFTP', - 'ORACLE', - 'SMPP', - 'SYSLOGTCP', - 'SYSLOGUDP', - 'FIX', - 'SSL_FIX', - ] - ), - ipv46=dict(type='str'), - ippattern=dict(type='str'), - ipmask=dict(type='str'), - port=dict(type='int'), - range=dict(type='float'), - persistencetype=dict( - type='str', - choices=[ - 'SOURCEIP', - 'COOKIEINSERT', - 'SSLSESSION', - 'RULE', - 'URLPASSIVE', - 'CUSTOMSERVERID', - 'DESTIP', - 'SRCIPDESTIP', - 'CALLID', - 'RTSPSID', - 'DIAMETER', - 'FIXSESSION', - 'NONE', - ] - ), - timeout=dict(type='float'), - persistencebackup=dict( - type='str', - choices=[ - 'SOURCEIP', - 'NONE', - ] - ), - backuppersistencetimeout=dict(type='float'), - lbmethod=dict( - type='str', - choices=[ - 'ROUNDROBIN', - 'LEASTCONNECTION', - 'LEASTRESPONSETIME', - 'URLHASH', - 'DOMAINHASH', - 'DESTINATIONIPHASH', - 'SOURCEIPHASH', - 'SRCIPDESTIPHASH', - 'LEASTBANDWIDTH', - 'LEASTPACKETS', - 'TOKEN', - 'SRCIPSRCPORTHASH', - 'LRTM', - 'CALLIDHASH', - 'CUSTOMLOAD', - 'LEASTREQUEST', - 'AUDITLOGHASH', - 'STATICPROXIMITY', - ] - ), - hashlength=dict(type='float'), - netmask=dict(type='str'), - v6netmasklen=dict(type='float'), - backuplbmethod=dict( - type='str', - choices=[ - 'ROUNDROBIN', - 'LEASTCONNECTION', - 'LEASTRESPONSETIME', - 'SOURCEIPHASH', - 'LEASTBANDWIDTH', - 'LEASTPACKETS', - 'CUSTOMLOAD', - ] - ), - cookiename=dict(type='str'), - listenpolicy=dict(type='str'), - listenpriority=dict(type='float'), - persistmask=dict(type='str'), - v6persistmasklen=dict(type='float'), - rtspnat=dict(type='bool'), - m=dict( - type='str', - choices=[ - 'IP', - 'MAC', - 'IPTUNNEL', - 'TOS', - ] - ), - tosid=dict(type='float'), - datalength=dict(type='float'), - dataoffset=dict(type='float'), - sessionless=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - connfailover=dict( - type='str', - choices=[ - 'DISABLED', - 'STATEFUL', - 'STATELESS', - ] - ), - redirurl=dict(type='str'), - cacheable=dict(type='bool'), - clttimeout=dict(type='float'), - somethod=dict( - type='str', - choices=[ - 'CONNECTION', - 'DYNAMICCONNECTION', - 'BANDWIDTH', - 'HEALTH', - 'NONE', - ] - ), - sopersistence=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - sopersistencetimeout=dict(type='float'), - healththreshold=dict(type='float'), - sothreshold=dict(type='float'), - sobackupaction=dict( - type='str', - choices=[ - 'DROP', - 'ACCEPT', - 'REDIRECT', - ] - ), - redirectportrewrite=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - downstateflush=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - disableprimaryondown=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - insertvserveripport=dict( - type='str', - choices=[ - 'OFF', - 'VIPADDR', - 'V6TOV4MAPPING', - ] - ), - vipheader=dict(type='str'), - authenticationhost=dict(type='str'), - authentication=dict(type='bool'), - authn401=dict(type='bool'), - authnvsname=dict(type='str'), - push=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - pushvserver=dict(type='str'), - pushlabel=dict(type='str'), - pushmulticlients=dict(type='bool'), - tcpprofilename=dict(type='str'), - httpprofilename=dict(type='str'), - dbprofilename=dict(type='str'), - comment=dict(type='str'), - l2conn=dict(type='bool'), - oracleserverversion=dict( - type='str', - choices=[ - '10G', - '11G', - ] - ), - mssqlserverversion=dict( - type='str', - choices=[ - '70', - '2000', - '2000SP1', - '2005', - '2008', - '2008R2', - '2012', - '2014', - ] - ), - mysqlprotocolversion=dict(type='float'), - mysqlserverversion=dict(type='str'), - mysqlcharacterset=dict(type='float'), - mysqlservercapabilities=dict(type='float'), - appflowlog=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - netprofile=dict(type='str'), - icmpvsrresponse=dict( - type='str', - choices=[ - 'PASSIVE', - 'ACTIVE', - ] - ), - rhistate=dict( - type='str', - choices=[ - 'PASSIVE', - 'ACTIVE', - ] - ), - newservicerequest=dict(type='float'), - newservicerequestunit=dict( - type='str', - choices=[ - 'PER_SECOND', - 'PERCENT', - ] - ), - newservicerequestincrementinterval=dict(type='float'), - minautoscalemembers=dict(type='float'), - maxautoscalemembers=dict(type='float'), - skippersistency=dict( - type='str', - choices=[ - 'Bypass', - 'ReLb', - 'None', - ] - ), - authnprofile=dict(type='str'), - macmoderetainvlan=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - dbslb=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - dns64=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - bypassaaaa=dict(type='bool'), - recursionavailable=dict(type='bool'), - processlocal=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - dnsprofilename=dict(type='str'), - ) - - hand_inserted_arguments = dict( - servicebindings=dict(type='list'), - servicegroupbindings=dict(type='list'), - ssl_certkey=dict(type='str'), - disabled=dict( - type='bool', - default=False - ), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - argument_spec.update(module_specific_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - readwrite_attrs = [ - 'name', - 'servicetype', - 'ipv46', - 'ippattern', - 'ipmask', - 'port', - 'range', - 'persistencetype', - 'timeout', - 'persistencebackup', - 'backuppersistencetimeout', - 'lbmethod', - 'hashlength', - 'netmask', - 'v6netmasklen', - 'backuplbmethod', - 'cookiename', - 'listenpolicy', - 'listenpriority', - 'persistmask', - 'v6persistmasklen', - 'rtspnat', - 'm', - 'tosid', - 'datalength', - 'dataoffset', - 'sessionless', - 'connfailover', - 'redirurl', - 'cacheable', - 'clttimeout', - 'somethod', - 'sopersistence', - 'sopersistencetimeout', - 'healththreshold', - 'sothreshold', - 'sobackupaction', - 'redirectportrewrite', - 'downstateflush', - 'disableprimaryondown', - 'insertvserveripport', - 'vipheader', - 'authenticationhost', - 'authentication', - 'authn401', - 'authnvsname', - 'push', - 'pushvserver', - 'pushlabel', - 'pushmulticlients', - 'tcpprofilename', - 'httpprofilename', - 'dbprofilename', - 'comment', - 'l2conn', - 'oracleserverversion', - 'mssqlserverversion', - 'mysqlprotocolversion', - 'mysqlserverversion', - 'mysqlcharacterset', - 'mysqlservercapabilities', - 'appflowlog', - 'netprofile', - 'icmpvsrresponse', - 'rhistate', - 'newservicerequest', - 'newservicerequestunit', - 'newservicerequestincrementinterval', - 'minautoscalemembers', - 'maxautoscalemembers', - 'skippersistency', - 'authnprofile', - 'macmoderetainvlan', - 'dbslb', - 'dns64', - 'bypassaaaa', - 'recursionavailable', - 'processlocal', - 'dnsprofilename', - ] - - readonly_attrs = [ - 'value', - 'ipmapping', - 'ngname', - 'type', - 'curstate', - 'effectivestate', - 'status', - 'lbrrreason', - 'redirect', - 'precedence', - 'homepage', - 'dnsvservername', - 'domain', - 'policyname', - 'cachevserver', - 'health', - 'gotopriorityexpression', - 'ruletype', - 'groupname', - 'cookiedomain', - 'map', - 'gt2gb', - 'consolidatedlconn', - 'consolidatedlconngbl', - 'thresholdvalue', - 'bindpoint', - 'invoke', - 'labeltype', - 'labelname', - 'version', - 'totalservices', - 'activeservices', - 'statechangetimesec', - 'statechangetimeseconds', - 'statechangetimemsec', - 'tickssincelaststatechange', - 'isgslb', - 'vsvrdynconnsothreshold', - 'backupvserverstatus', - '__count', - ] - - immutable_attrs = [ - 'name', - 'servicetype', - 'ipv46', - 'port', - 'range', - 'state', - 'redirurl', - 'vipheader', - 'newservicerequestunit', - 'td', - ] - - transforms = { - 'rtspnat': ['bool_on_off'], - 'authn401': ['bool_on_off'], - 'bypassaaaa': ['bool_yes_no'], - 'authentication': ['bool_on_off'], - 'cacheable': ['bool_yes_no'], - 'l2conn': ['bool_on_off'], - 'pushmulticlients': ['bool_yes_no'], - 'recursionavailable': ['bool_yes_no'], - 'sessionless': [lambda v: v.upper()], - 'sopersistence': [lambda v: v.upper()], - 'redirectportrewrite': [lambda v: v.upper()], - 'downstateflush': [lambda v: v.upper()], - 'disableprimaryondown': [lambda v: v.upper()], - 'push': [lambda v: v.upper()], - 'appflowlog': [lambda v: v.upper()], - 'macmoderetainvlan': [lambda v: v.upper()], - 'dbslb': [lambda v: v.upper()], - 'dns64': [lambda v: v.upper()], - 'processlocal': [lambda v: v.upper()], - } - - lbvserver_proxy = ConfigProxy( - actual=lbvserver(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - ensure_feature_is_enabled(client, 'LB') - if module.params['state'] == 'present': - log('Applying actions for state present') - - if not lb_vserver_exists(client, module): - log('Add lb vserver') - if not module.check_mode: - lbvserver_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not lb_vserver_identical(client, module, lbvserver_proxy): - - # Check if we try to change value of immutable attributes - diff_dict = lb_vserver_diff(client, module, lbvserver_proxy) - immutables_changed = get_immutables_intersection(lbvserver_proxy, diff_dict.keys()) - if immutables_changed != []: - msg = 'Cannot update immutable attributes %s. Must delete and recreate entity.' % (immutables_changed,) - module.fail_json(msg=msg, diff=diff_dict, **module_result) - - log('Update lb vserver') - if not module.check_mode: - lbvserver_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - log('Present noop') - - if not service_bindings_identical(client, module): - if not module.check_mode: - sync_service_bindings(client, module) - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - - if not servicegroup_bindings_identical(client, module): - if not module.check_mode: - sync_servicegroup_bindings(client, module) - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - - if module.params['servicetype'] != 'SSL' and module.params['ssl_certkey'] is not None: - module.fail_json(msg='ssl_certkey is applicable only to SSL vservers', **module_result) - - # Check if SSL certkey is sane - if module.params['servicetype'] == 'SSL': - if not ssl_certkey_bindings_identical(client, module): - if not module.check_mode: - ssl_certkey_bindings_sync(client, module) - - module_result['changed'] = True - - if not module.check_mode: - res = do_state_change(client, module, lbvserver_proxy) - if res.errorcode != 0: - msg = 'Error when setting disabled state. errorcode: %s message: %s' % (res.errorcode, res.message) - module.fail_json(msg=msg, **module_result) - - # Sanity check - log('Sanity checks for state present') - if not module.check_mode: - if not lb_vserver_exists(client, module): - module.fail_json(msg='Did not create lb vserver', **module_result) - - if not lb_vserver_identical(client, module, lbvserver_proxy): - msg = 'lb vserver is not configured correctly' - module.fail_json(msg=msg, diff=lb_vserver_diff(client, module, lbvserver_proxy), **module_result) - - if not service_bindings_identical(client, module): - module.fail_json(msg='service bindings are not identical', **module_result) - - if not servicegroup_bindings_identical(client, module): - module.fail_json(msg='servicegroup bindings are not identical', **module_result) - - if module.params['servicetype'] == 'SSL': - if not ssl_certkey_bindings_identical(client, module): - module.fail_json(msg='sll certkey bindings not identical', **module_result) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if lb_vserver_exists(client, module): - if not module.check_mode: - log('Delete lb vserver') - lbvserver_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - log('Absent noop') - module_result['changed'] = False - - # Sanity check - log('Sanity checks for state absent') - if not module.check_mode: - if lb_vserver_exists(client, module): - module.fail_json(msg='lb vserver still exists', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_nitro_request.py b/plugins/modules/network/netscaler/netscaler_nitro_request.py deleted file mode 100644 index 73e608bfec..0000000000 --- a/plugins/modules/network/netscaler/netscaler_nitro_request.py +++ /dev/null @@ -1,909 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_nitro_request -short_description: Issue Nitro API requests to a Netscaler instance. -description: - - Issue Nitro API requests to a Netscaler instance. - - This is intended to be a short hand for using the uri Ansible module to issue the raw HTTP requests directly. - - It provides consistent return values and has no other dependencies apart from the base Ansible runtime environment. - - This module is intended to run either on the Ansible control node or a bastion (jumpserver) with access to the actual Netscaler instance - - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - nsip: - description: - - The IP address of the Netscaler or MAS instance where the Nitro API calls will be made. - - "The port can be specified with the colon C(:). E.g. C(192.168.1.1:555)." - - nitro_user: - description: - - The username with which to authenticate to the Netscaler node. - required: true - - nitro_pass: - description: - - The password with which to authenticate to the Netscaler node. - required: true - - nitro_protocol: - choices: [ 'http', 'https' ] - default: http - description: - - Which protocol to use when accessing the Nitro API objects. - - validate_certs: - description: - - If C(no), SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates. - default: 'yes' - type: bool - - nitro_auth_token: - description: - - The authentication token provided by the C(mas_login) operation. It is required when issuing Nitro API calls through a MAS proxy. - - resource: - description: - - The type of resource we are operating on. - - It is required for all I(operation) values except C(mas_login) and C(save_config). - - name: - description: - - The name of the resource we are operating on. - - "It is required for the following I(operation) values: C(update), C(get), C(delete)." - - attributes: - description: - - The attributes of the Nitro object we are operating on. - - "It is required for the following I(operation) values: C(add), C(update), C(action)." - - args: - description: - - A dictionary which defines the key arguments by which we will select the Nitro object to operate on. - - "It is required for the following I(operation) values: C(get_by_args), C('delete_by_args')." - - filter: - description: - - A dictionary which defines the filter with which to refine the Nitro objects returned by the C(get_filtered) I(operation). - - operation: - description: - - Define the Nitro operation that we want to perform. - choices: - - add - - update - - get - - get_by_args - - get_filtered - - get_all - - delete - - delete_by_args - - count - - mas_login - - save_config - - action - - expected_nitro_errorcode: - description: - - A list of numeric values that signify that the operation was successful. - default: [0] - required: true - - action: - description: - - The action to perform when the I(operation) value is set to C(action). - - Some common values for this parameter are C(enable), C(disable), C(rename). - - instance_ip: - description: - - The IP address of the target Netscaler instance when issuing a Nitro request through a MAS proxy. - - instance_name: - description: - - The name of the target Netscaler instance when issuing a Nitro request through a MAS proxy. - - instance_id: - description: - - The id of the target Netscaler instance when issuing a Nitro request through a MAS proxy. -''' - -EXAMPLES = ''' -- name: Add a server - delegate_to: localhost - netscaler_nitro_request: - nsip: "{{ nsip }}" - nitro_user: "{{ nitro_user }}" - nitro_pass: "{{ nitro_pass }}" - operation: add - resource: server - name: test-server-1 - attributes: - name: test-server-1 - ipaddress: 192.168.1.1 - -- name: Update server - delegate_to: localhost - netscaler_nitro_request: - nsip: "{{ nsip }}" - nitro_user: "{{ nitro_user }}" - nitro_pass: "{{ nitro_pass }}" - operation: update - resource: server - name: test-server-1 - attributes: - name: test-server-1 - ipaddress: 192.168.1.2 - -- name: Get server - delegate_to: localhost - register: result - netscaler_nitro_request: - nsip: "{{ nsip }}" - nitro_user: "{{ nitro_user }}" - nitro_pass: "{{ nitro_pass }}" - operation: get - resource: server - name: test-server-1 - -- name: Delete server - delegate_to: localhost - register: result - netscaler_nitro_request: - nsip: "{{ nsip }}" - nitro_user: "{{ nitro_user }}" - nitro_pass: "{{ nitro_pass }}" - operation: delete - resource: server - name: test-server-1 - -- name: Rename server - delegate_to: localhost - netscaler_nitro_request: - nsip: "{{ nsip }}" - nitro_user: "{{ nitro_user }}" - nitro_pass: "{{ nitro_pass }}" - operation: action - action: rename - resource: server - attributes: - name: test-server-1 - newname: test-server-2 - -- name: Get server by args - delegate_to: localhost - register: result - netscaler_nitro_request: - nsip: "{{ nsip }}" - nitro_user: "{{ nitro_user }}" - nitro_pass: "{{ nitro_pass }}" - operation: get_by_args - resource: server - args: - name: test-server-1 - -- name: Get server by filter - delegate_to: localhost - register: result - netscaler_nitro_request: - nsip: "{{ nsip }}" - nitro_user: "{{ nitro_user }}" - nitro_pass: "{{ nitro_pass }}" - operation: get_filtered - resource: server - filter: - ipaddress: 192.168.1.2 - -# Doing a NITRO request through MAS. -# Requires to have an authentication token from the mas_login and used as the nitro_auth_token parameter -# Also nsip is the MAS address and the target Netscaler IP must be defined with instance_ip -# The rest of the task arguments remain the same as when issuing the NITRO request directly to a Netscaler instance. - -- name: Do mas login - delegate_to: localhost - register: login_result - netscaler_nitro_request: - nsip: "{{ mas_ip }}" - nitro_user: "{{ nitro_user }}" - nitro_pass: "{{ nitro_pass }}" - operation: mas_login - -- name: Add resource through MAS proxy - delegate_to: localhost - netscaler_nitro_request: - nsip: "{{ mas_ip }}" - nitro_auth_token: "{{ login_result.nitro_auth_token }}" - instance_ip: "{{ nsip }}" - operation: add - resource: server - name: test-server-1 - attributes: - name: test-server-1 - ipaddress: 192.168.1.7 -''' - -RETURN = ''' -nitro_errorcode: - description: A numeric value containing the return code of the NITRO operation. When 0 the operation is successful. Any non zero value indicates an error. - returned: always - type: int - sample: 0 - -nitro_message: - description: A string containing a human readable explanation for the NITRO operation result. - returned: always - type: str - sample: Success - -nitro_severity: - description: A string describing the severity of the NITRO operation error or NONE. - returned: always - type: str - sample: NONE - -http_response_data: - description: A dictionary that contains all the HTTP response's data. - returned: always - type: dict - sample: "status: 200" - -http_response_body: - description: A string with the actual HTTP response body content if existent. If there is no HTTP response body it is an empty string. - returned: always - type: str - sample: "{ errorcode: 0, message: Done, severity: NONE }" - -nitro_object: - description: The object returned from the NITRO operation. This is applicable to the various get operations which return an object. - returned: when applicable - type: list - sample: - - - ipaddress: "192.168.1.8" - ipv6address: "NO" - maxbandwidth: "0" - name: "test-server-1" - port: 0 - sp: "OFF" - state: "ENABLED" - -nitro_auth_token: - description: The token returned by the C(mas_login) operation when successful. - returned: when applicable - type: str - sample: "##E8D7D74DDBD907EE579E8BB8FF4529655F22227C1C82A34BFC93C9539D66" -''' - - -from ansible.module_utils.urls import fetch_url -from ansible.module_utils.basic import env_fallback -from ansible.module_utils.basic import AnsibleModule -import codecs - - -class NitroAPICaller(object): - - _argument_spec = dict( - nsip=dict( - fallback=(env_fallback, ['NETSCALER_NSIP']), - ), - nitro_user=dict( - fallback=(env_fallback, ['NETSCALER_NITRO_USER']), - ), - nitro_pass=dict( - fallback=(env_fallback, ['NETSCALER_NITRO_PASS']), - no_log=True - ), - nitro_protocol=dict( - choices=['http', 'https'], - fallback=(env_fallback, ['NETSCALER_NITRO_PROTOCOL']), - default='http' - ), - validate_certs=dict( - default=True, - type='bool' - ), - nitro_auth_token=dict( - type='str', - no_log=True - ), - resource=dict(type='str'), - name=dict(type='str'), - attributes=dict(type='dict'), - - args=dict(type='dict'), - filter=dict(type='dict'), - - operation=dict( - type='str', - required=True, - choices=[ - 'add', - 'update', - 'get', - 'get_by_args', - 'get_filtered', - 'get_all', - 'delete', - 'delete_by_args', - 'count', - - 'mas_login', - - # Actions - 'save_config', - - # Generic action handler - 'action', - ] - ), - expected_nitro_errorcode=dict( - type='list', - default=[0], - ), - action=dict(type='str'), - instance_ip=dict(type='str'), - instance_name=dict(type='str'), - instance_id=dict(type='str'), - ) - - def __init__(self): - - self._module = AnsibleModule( - argument_spec=self._argument_spec, - supports_check_mode=False, - ) - - self._module_result = dict( - failed=False, - ) - - # Prepare the http headers according to module arguments - self._headers = {} - self._headers['Content-Type'] = 'application/json' - - # Check for conflicting authentication methods - have_token = self._module.params['nitro_auth_token'] is not None - have_userpass = None not in (self._module.params['nitro_user'], self._module.params['nitro_pass']) - login_operation = self._module.params['operation'] == 'mas_login' - - if have_token and have_userpass: - self.fail_module(msg='Cannot define both authentication token and username/password') - - if have_token: - self._headers['Cookie'] = "NITRO_AUTH_TOKEN=%s" % self._module.params['nitro_auth_token'] - - if have_userpass and not login_operation: - self._headers['X-NITRO-USER'] = self._module.params['nitro_user'] - self._headers['X-NITRO-PASS'] = self._module.params['nitro_pass'] - - # Do header manipulation when doing a MAS proxy call - if self._module.params['instance_ip'] is not None: - self._headers['_MPS_API_PROXY_MANAGED_INSTANCE_IP'] = self._module.params['instance_ip'] - elif self._module.params['instance_name'] is not None: - self._headers['_MPS_API_PROXY_MANAGED_INSTANCE_NAME'] = self._module.params['instance_name'] - elif self._module.params['instance_id'] is not None: - self._headers['_MPS_API_PROXY_MANAGED_INSTANCE_ID'] = self._module.params['instance_id'] - - def edit_response_data(self, r, info, result, success_status): - # Search for body in both http body and http data - if r is not None: - result['http_response_body'] = codecs.decode(r.read(), 'utf-8') - elif 'body' in info: - result['http_response_body'] = codecs.decode(info['body'], 'utf-8') - del info['body'] - else: - result['http_response_body'] = '' - - result['http_response_data'] = info - - # Update the nitro_* parameters according to expected success_status - # Use explicit return values from http response or deduce from http status code - - # Nitro return code in http data - result['nitro_errorcode'] = None - result['nitro_message'] = None - result['nitro_severity'] = None - - if result['http_response_body'] != '': - try: - data = self._module.from_json(result['http_response_body']) - except ValueError: - data = {} - result['nitro_errorcode'] = data.get('errorcode') - result['nitro_message'] = data.get('message') - result['nitro_severity'] = data.get('severity') - - # If we do not have the nitro errorcode from body deduce it from the http status - if result['nitro_errorcode'] is None: - # HTTP status failed - if result['http_response_data'].get('status') != success_status: - result['nitro_errorcode'] = -1 - result['nitro_message'] = result['http_response_data'].get('msg', 'HTTP status %s' % result['http_response_data']['status']) - result['nitro_severity'] = 'ERROR' - # HTTP status succeeded - else: - result['nitro_errorcode'] = 0 - result['nitro_message'] = 'Success' - result['nitro_severity'] = 'NONE' - - def handle_get_return_object(self, result): - result['nitro_object'] = [] - if result['nitro_errorcode'] == 0: - if result['http_response_body'] != '': - data = self._module.from_json(result['http_response_body']) - if self._module.params['resource'] in data: - result['nitro_object'] = data[self._module.params['resource']] - else: - del result['nitro_object'] - - def fail_module(self, msg, **kwargs): - self._module_result['failed'] = True - self._module_result['changed'] = False - self._module_result.update(kwargs) - self._module_result['msg'] = msg - self._module.fail_json(**self._module_result) - - def main(self): - if self._module.params['operation'] == 'add': - result = self.add() - - if self._module.params['operation'] == 'update': - result = self.update() - - if self._module.params['operation'] == 'delete': - result = self.delete() - - if self._module.params['operation'] == 'delete_by_args': - result = self.delete_by_args() - - if self._module.params['operation'] == 'get': - result = self.get() - - if self._module.params['operation'] == 'get_by_args': - result = self.get_by_args() - - if self._module.params['operation'] == 'get_filtered': - result = self.get_filtered() - - if self._module.params['operation'] == 'get_all': - result = self.get_all() - - if self._module.params['operation'] == 'count': - result = self.count() - - if self._module.params['operation'] == 'mas_login': - result = self.mas_login() - - if self._module.params['operation'] == 'action': - result = self.action() - - if self._module.params['operation'] == 'save_config': - result = self.save_config() - - if result['nitro_errorcode'] not in self._module.params['expected_nitro_errorcode']: - self.fail_module(msg='NITRO Failure', **result) - - self._module_result.update(result) - self._module.exit_json(**self._module_result) - - def exit_module(self): - self._module.exit_json() - - def add(self): - # Check if required attributes are present - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - if self._module.params['attributes'] is None: - self.fail_module(msg='NITRO resource attributes are undefined.') - - url = '%s://%s/nitro/v1/config/%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - ) - - data = self._module.jsonify({self._module.params['resource']: self._module.params['attributes']}) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - data=data, - method='POST', - ) - - result = {} - - self.edit_response_data(r, info, result, success_status=201) - - if result['nitro_errorcode'] == 0: - self._module_result['changed'] = True - else: - self._module_result['changed'] = False - - return result - - def update(self): - # Check if required attributes are arguments present - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - if self._module.params['name'] is None: - self.fail_module(msg='NITRO resource name is undefined.') - - if self._module.params['attributes'] is None: - self.fail_module(msg='NITRO resource attributes are undefined.') - - url = '%s://%s/nitro/v1/config/%s/%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - self._module.params['name'], - ) - - data = self._module.jsonify({self._module.params['resource']: self._module.params['attributes']}) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - data=data, - method='PUT', - ) - - result = {} - self.edit_response_data(r, info, result, success_status=200) - - if result['nitro_errorcode'] == 0: - self._module_result['changed'] = True - else: - self._module_result['changed'] = False - - return result - - def get(self): - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - if self._module.params['name'] is None: - self.fail_module(msg='NITRO resource name is undefined.') - - url = '%s://%s/nitro/v1/config/%s/%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - self._module.params['name'], - ) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - method='GET', - ) - - result = {} - self.edit_response_data(r, info, result, success_status=200) - - self.handle_get_return_object(result) - self._module_result['changed'] = False - - return result - - def get_by_args(self): - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - - if self._module.params['args'] is None: - self.fail_module(msg='NITRO args is undefined.') - - url = '%s://%s/nitro/v1/config/%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - ) - - args_dict = self._module.params['args'] - args = ','.join(['%s:%s' % (k, args_dict[k]) for k in args_dict]) - - args = 'args=' + args - - url = '?'.join([url, args]) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - method='GET', - ) - result = {} - self.edit_response_data(r, info, result, success_status=200) - - self.handle_get_return_object(result) - self._module_result['changed'] = False - - return result - - def get_filtered(self): - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - - if self._module.params['filter'] is None: - self.fail_module(msg='NITRO filter is undefined.') - - keys = list(self._module.params['filter'].keys()) - filter_key = keys[0] - filter_value = self._module.params['filter'][filter_key] - filter_str = '%s:%s' % (filter_key, filter_value) - - url = '%s://%s/nitro/v1/config/%s?filter=%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - filter_str, - ) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - method='GET', - ) - - result = {} - self.edit_response_data(r, info, result, success_status=200) - self.handle_get_return_object(result) - self._module_result['changed'] = False - - return result - - def get_all(self): - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - - url = '%s://%s/nitro/v1/config/%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - ) - - print('headers %s' % self._headers) - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - method='GET', - ) - - result = {} - self.edit_response_data(r, info, result, success_status=200) - self.handle_get_return_object(result) - self._module_result['changed'] = False - - return result - - def delete(self): - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - - if self._module.params['name'] is None: - self.fail_module(msg='NITRO resource is undefined.') - - # Deletion by name takes precedence over deletion by attributes - - url = '%s://%s/nitro/v1/config/%s/%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - self._module.params['name'], - ) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - method='DELETE', - ) - - result = {} - self.edit_response_data(r, info, result, success_status=200) - - if result['nitro_errorcode'] == 0: - self._module_result['changed'] = True - else: - self._module_result['changed'] = False - - return result - - def delete_by_args(self): - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - - if self._module.params['args'] is None: - self.fail_module(msg='NITRO args is undefined.') - - url = '%s://%s/nitro/v1/config/%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - ) - - args_dict = self._module.params['args'] - args = ','.join(['%s:%s' % (k, args_dict[k]) for k in args_dict]) - - args = 'args=' + args - - url = '?'.join([url, args]) - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - method='DELETE', - ) - result = {} - self.edit_response_data(r, info, result, success_status=200) - - if result['nitro_errorcode'] == 0: - self._module_result['changed'] = True - else: - self._module_result['changed'] = False - - return result - - def count(self): - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - - url = '%s://%s/nitro/v1/config/%s?count=yes' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - ) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - method='GET', - ) - - result = {} - self.edit_response_data(r, info, result) - - if result['http_response_body'] != '': - data = self._module.from_json(result['http_response_body']) - - result['nitro_errorcode'] = data['errorcode'] - result['nitro_message'] = data['message'] - result['nitro_severity'] = data['severity'] - if self._module.params['resource'] in data: - result['nitro_count'] = data[self._module.params['resource']][0]['__count'] - - self._module_result['changed'] = False - - return result - - def action(self): - # Check if required attributes are present - if self._module.params['resource'] is None: - self.fail_module(msg='NITRO resource is undefined.') - if self._module.params['attributes'] is None: - self.fail_module(msg='NITRO resource attributes are undefined.') - if self._module.params['action'] is None: - self.fail_module(msg='NITRO action is undefined.') - - url = '%s://%s/nitro/v1/config/%s?action=%s' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - self._module.params['resource'], - self._module.params['action'], - ) - - data = self._module.jsonify({self._module.params['resource']: self._module.params['attributes']}) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - data=data, - method='POST', - ) - - result = {} - - self.edit_response_data(r, info, result, success_status=200) - - if result['nitro_errorcode'] == 0: - self._module_result['changed'] = True - else: - self._module_result['changed'] = False - - return result - - def mas_login(self): - url = '%s://%s/nitro/v1/config/login' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - ) - - login_credentials = { - 'login': { - - 'username': self._module.params['nitro_user'], - 'password': self._module.params['nitro_pass'], - } - } - - data = 'object=\n%s' % self._module.jsonify(login_credentials) - - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - data=data, - method='POST', - ) - print(r, info) - - result = {} - self.edit_response_data(r, info, result, success_status=200) - - if result['nitro_errorcode'] == 0: - body_data = self._module.from_json(result['http_response_body']) - result['nitro_auth_token'] = body_data['login'][0]['sessionid'] - - self._module_result['changed'] = False - - return result - - def save_config(self): - - url = '%s://%s/nitro/v1/config/nsconfig?action=save' % ( - self._module.params['nitro_protocol'], - self._module.params['nsip'], - ) - - data = self._module.jsonify( - { - 'nsconfig': {}, - } - ) - r, info = fetch_url( - self._module, - url=url, - headers=self._headers, - data=data, - method='POST', - ) - - result = {} - - self.edit_response_data(r, info, result, success_status=200) - self._module_result['changed'] = False - - return result - - -def main(): - - nitro_api_caller = NitroAPICaller() - nitro_api_caller.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netscaler/netscaler_save_config.py b/plugins/modules/network/netscaler/netscaler_save_config.py deleted file mode 100644 index 6b4ec1ff5a..0000000000 --- a/plugins/modules/network/netscaler/netscaler_save_config.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_save_config -short_description: Save Netscaler configuration. -description: - - This module unconditionally saves the configuration on the target netscaler node. - - This module does not support check mode. - - This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - nsip: - description: - - The ip address of the netscaler appliance where the nitro API calls will be made. - - "The port can be specified with the colon (:). E.g. C(192.168.1.1:555)." - required: True - - nitro_user: - description: - - The username with which to authenticate to the netscaler node. - required: True - - nitro_pass: - description: - - The password with which to authenticate to the netscaler node. - required: True - - nitro_protocol: - choices: [ 'http', 'https' ] - default: http - description: - - Which protocol to use when accessing the nitro API objects. - - validate_certs: - description: - - If C(no), SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates. - required: false - default: 'yes' - type: bool - - nitro_timeout: - description: - - Time in seconds until a timeout error is thrown when establishing a new session with Netscaler. - default: 310 - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' ---- -- name: Save netscaler configuration - delegate_to: localhost - netscaler_save_config: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - -- name: Setup server without saving configuration - delegate_to: localhost - notify: Save configuration - netscaler_server: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - - save_config: no - - name: server-1 - ipaddress: 192.168.1.1 - -# Under playbook's handlers - -- name: Save configuration - delegate_to: localhost - netscaler_save_config: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: ['message 1', 'message 2'] - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -''' - -import copy - -try: - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import get_nitro_client, log, loglines, netscaler_common_arguments - - -def main(): - - argument_spec = copy.deepcopy(netscaler_common_arguments) - - # Delete common arguments irrelevant to this module - del argument_spec['state'] - del argument_spec['save_config'] - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=False, - ) - - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - try: - log('Saving configuration') - client.save_config() - except nitro_exception as e: - msg = "nitro exception errorcode=" + str(e.errorcode) + ",message=" + e.message - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_server.py b/plugins/modules/network/netscaler/netscaler_server.py deleted file mode 100644 index e431b450bc..0000000000 --- a/plugins/modules/network/netscaler/netscaler_server.py +++ /dev/null @@ -1,403 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_server -short_description: Manage server configuration -description: - - Manage server entities configuration. - - This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - name: - description: - - "Name for the server." - - >- - Must begin with an ASCII alphabetic or underscore C(_) character, and must contain only ASCII - alphanumeric, underscore C(_), hash C(#), period C(.), space C( ), colon C(:), at C(@), equals C(=), and hyphen C(-) - characters. - - "Can be changed after the name is created." - - "Minimum length = 1" - - ipaddress: - description: - - >- - IPv4 or IPv6 address of the server. If you create an IP address based server, you can specify the - name of the server, instead of its IP address, when creating a service. Note: If you do not create a - server entry, the server IP address that you enter when you create a service becomes the name of the - server. - - domain: - description: - - "Domain name of the server. For a domain based configuration, you must create the server first." - - "Minimum length = 1" - - translationip: - description: - - "IP address used to transform the server's DNS-resolved IP address." - - translationmask: - description: - - "The netmask of the translation ip." - - domainresolveretry: - description: - - >- - Time, in seconds, for which the NetScaler appliance must wait, after DNS resolution fails, before - sending the next DNS query to resolve the domain name. - - "Minimum value = C(5)" - - "Maximum value = C(20939)" - default: 5 - - ipv6address: - description: - - >- - Support IPv6 addressing mode. If you configure a server with the IPv6 addressing mode, you cannot use - the server in the IPv4 addressing mode. - default: false - type: bool - - comment: - description: - - "Any information about the server." - - td: - description: - - >- - Integer value that uniquely identifies the traffic domain in which you want to configure the entity. - If you do not specify an ID, the entity becomes part of the default traffic domain, which has an ID - of 0. - - "Minimum value = C(0)" - - "Maximum value = C(4094)" - - graceful: - description: - - >- - Shut down gracefully, without accepting any new connections, and disabling each service when all of - its connections are closed. - - This option is meaningful only when setting the I(disabled) option to C(true) - type: bool - - delay: - description: - - Time, in seconds, after which all the services configured on the server are disabled. - - This option is meaningful only when setting the I(disabled) option to C(true) - - disabled: - description: - - When set to C(true) the server state will be set to C(disabled). - - When set to C(false) the server state will be set to C(enabled). - - >- - Note that due to limitations of the underlying NITRO API a C(disabled) state change alone - does not cause the module result to report a changed status. - type: bool - default: false - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -- name: Setup server - delegate_to: localhost - netscaler_server: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - - state: present - - name: server-1 - ipaddress: 192.168.1.1 -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: ['message 1', 'message 2'] - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: { 'targetlbvserver': 'difference. ours: (str) server1 other: (str) server2' } -''' - -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.basic.server import server - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import (ConfigProxy, get_nitro_client, - netscaler_common_arguments, - log, loglines, - get_immutables_intersection) - - -def server_exists(client, module): - log('Checking if server exists') - if server.count_filtered(client, 'name:%s' % module.params['name']) > 0: - return True - else: - return False - - -def server_identical(client, module, server_proxy): - log('Checking if configured server is identical') - if server.count_filtered(client, 'name:%s' % module.params['name']) == 0: - return False - diff = diff_list(client, module, server_proxy) - - # Remove options that are not present in nitro server object - # These are special options relevant to the disabled action - for option in ['graceful', 'delay']: - if option in diff: - del diff[option] - - if diff == {}: - return True - else: - return False - - -def diff_list(client, module, server_proxy): - ret_val = server_proxy.diff_object(server.get_filtered(client, 'name:%s' % module.params['name'])[0]), - return ret_val[0] - - -def do_state_change(client, module, server_proxy): - if module.params['disabled']: - log('Disabling server') - result = server.disable(client, server_proxy.actual) - else: - log('Enabling server') - result = server.enable(client, server_proxy.actual) - return result - - -def main(): - - module_specific_arguments = dict( - name=dict(type='str'), - ipaddress=dict(type='str'), - domain=dict(type='str'), - translationip=dict(type='str'), - translationmask=dict(type='str'), - domainresolveretry=dict(type='int'), - ipv6address=dict( - type='bool', - default=False - ), - comment=dict(type='str'), - td=dict(type='float'), - graceful=dict(type='bool'), - delay=dict(type='float') - ) - - hand_inserted_arguments = dict( - disabled=dict( - type='bool', - default=False, - ), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - argument_spec.update(module_specific_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - - client = get_nitro_client(module) - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - # Instantiate Server Config object - readwrite_attrs = [ - 'name', - 'ipaddress', - 'domain', - 'translationip', - 'translationmask', - 'domainresolveretry', - 'ipv6address', - 'graceful', - 'delay', - 'comment', - 'td', - ] - - readonly_attrs = [ - 'statechangetimesec', - 'tickssincelaststatechange', - 'autoscale', - 'customserverid', - 'monthreshold', - 'maxclient', - 'maxreq', - 'maxbandwidth', - 'usip', - 'cka', - 'tcpb', - 'cmp', - 'clttimeout', - 'svrtimeout', - 'cipheader', - 'cip', - 'cacheable', - 'sc', - 'sp', - 'downstateflush', - 'appflowlog', - 'boundtd', - '__count', - ] - - immutable_attrs = [ - 'name', - 'domain', - 'ipv6address', - 'td', - ] - - transforms = { - 'graceful': ['bool_yes_no'], - 'ipv6address': ['bool_yes_no'], - } - - server_proxy = ConfigProxy( - actual=server(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - - # Apply appropriate state - if module.params['state'] == 'present': - log('Applying actions for state present') - if not server_exists(client, module): - if not module.check_mode: - server_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not server_identical(client, module, server_proxy): - - # Check if we try to change value of immutable attributes - immutables_changed = get_immutables_intersection(server_proxy, diff_list(client, module, server_proxy).keys()) - if immutables_changed != []: - msg = 'Cannot update immutable attributes %s' % (immutables_changed,) - module.fail_json(msg=msg, diff=diff_list(client, module, server_proxy), **module_result) - if not module.check_mode: - server_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - if not module.check_mode: - res = do_state_change(client, module, server_proxy) - if res.errorcode != 0: - msg = 'Error when setting disabled state. errorcode: %s message: %s' % (res.errorcode, res.message) - module.fail_json(msg=msg, **module_result) - - # Sanity check for result - log('Sanity checks for state present') - if not module.check_mode: - if not server_exists(client, module): - module.fail_json(msg='Server does not seem to exist', **module_result) - if not server_identical(client, module, server_proxy): - module.fail_json( - msg='Server is not configured according to parameters given', - diff=diff_list(client, module, server_proxy), - **module_result - ) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if server_exists(client, module): - if not module.check_mode: - server_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for result - log('Sanity checks for state absent') - if not module.check_mode: - if server_exists(client, module): - module.fail_json(msg='Server seems to be present', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_service.py b/plugins/modules/network/netscaler/netscaler_service.py deleted file mode 100644 index 85e1ca14b0..0000000000 --- a/plugins/modules/network/netscaler/netscaler_service.py +++ /dev/null @@ -1,964 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_service -short_description: Manage service configuration in Netscaler -description: - - Manage service configuration in Netscaler. - - This module allows the creation, deletion and modification of Netscaler services. - - This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance. - - This module supports check mode. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - name: - description: - - >- - Name for the service. Must begin with an ASCII alphabetic or underscore C(_) character, and must - contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space C( ), colon C(:), at C(@), equals - C(=), and hyphen C(-) characters. Cannot be changed after the service has been created. - - "Minimum length = 1" - - ip: - description: - - "IP to assign to the service." - - "Minimum length = 1" - - servername: - description: - - "Name of the server that hosts the service." - - "Minimum length = 1" - - servicetype: - choices: - - 'HTTP' - - 'FTP' - - 'TCP' - - 'UDP' - - 'SSL' - - 'SSL_BRIDGE' - - 'SSL_TCP' - - 'DTLS' - - 'NNTP' - - 'RPCSVR' - - 'DNS' - - 'ADNS' - - 'SNMP' - - 'RTSP' - - 'DHCPRA' - - 'ANY' - - 'SIP_UDP' - - 'SIP_TCP' - - 'SIP_SSL' - - 'DNS_TCP' - - 'ADNS_TCP' - - 'MYSQL' - - 'MSSQL' - - 'ORACLE' - - 'RADIUS' - - 'RADIUSListener' - - 'RDP' - - 'DIAMETER' - - 'SSL_DIAMETER' - - 'TFTP' - - 'SMPP' - - 'PPTP' - - 'GRE' - - 'SYSLOGTCP' - - 'SYSLOGUDP' - - 'FIX' - - 'SSL_FIX' - description: - - "Protocol in which data is exchanged with the service." - - port: - description: - - "Port number of the service." - - "Range 1 - 65535" - - "* in CLI is represented as 65535 in NITRO API" - - cleartextport: - description: - - >- - Port to which clear text data must be sent after the appliance decrypts incoming SSL traffic. - Applicable to transparent SSL services. - - "Minimum value = 1" - - cachetype: - choices: - - 'TRANSPARENT' - - 'REVERSE' - - 'FORWARD' - description: - - "Cache type supported by the cache server." - - maxclient: - description: - - "Maximum number of simultaneous open connections to the service." - - "Minimum value = 0" - - "Maximum value = 4294967294" - - healthmonitor: - description: - - "Monitor the health of this service" - default: yes - type: bool - - maxreq: - description: - - "Maximum number of requests that can be sent on a persistent connection to the service." - - "Note: Connection requests beyond this value are rejected." - - "Minimum value = 0" - - "Maximum value = 65535" - - cacheable: - description: - - "Use the transparent cache redirection virtual server to forward requests to the cache server." - - "Note: Do not specify this parameter if you set the Cache Type parameter." - default: no - type: bool - - cip: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Before forwarding a request to the service, insert an HTTP header with the client's IPv4 or IPv6 - address as its value. Used if the server needs the client's IP address for security, accounting, or - other purposes, and setting the Use Source IP parameter is not a viable option. - - cipheader: - description: - - >- - Name for the HTTP header whose value must be set to the IP address of the client. Used with the - Client IP parameter. If you set the Client IP parameter, and you do not specify a name for the - header, the appliance uses the header name specified for the global Client IP Header parameter (the - cipHeader parameter in the set ns param CLI command or the Client IP Header parameter in the - Configure HTTP Parameters dialog box at System > Settings > Change HTTP parameters). If the global - Client IP Header parameter is not specified, the appliance inserts a header with the name - "client-ip.". - - "Minimum length = 1" - - usip: - description: - - >- - Use the client's IP address as the source IP address when initiating a connection to the server. When - creating a service, if you do not set this parameter, the service inherits the global Use Source IP - setting (available in the enable ns mode and disable ns mode CLI commands, or in the System > - Settings > Configure modes > Configure Modes dialog box). However, you can override this setting - after you create the service. - type: bool - - pathmonitor: - description: - - "Path monitoring for clustering." - - pathmonitorindv: - description: - - "Individual Path monitoring decisions." - - useproxyport: - description: - - >- - Use the proxy port as the source port when initiating connections with the server. With the NO - setting, the client-side connection port is used as the source port for the server-side connection. - - "Note: This parameter is available only when the Use Source IP (USIP) parameter is set to YES." - type: bool - - sp: - description: - - "Enable surge protection for the service." - type: bool - - rtspsessionidremap: - description: - - "Enable RTSP session ID mapping for the service." - default: off - type: bool - - clttimeout: - description: - - "Time, in seconds, after which to terminate an idle client connection." - - "Minimum value = 0" - - "Maximum value = 31536000" - - svrtimeout: - description: - - "Time, in seconds, after which to terminate an idle server connection." - - "Minimum value = 0" - - "Maximum value = 31536000" - - customserverid: - description: - - >- - Unique identifier for the service. Used when the persistency type for the virtual server is set to - Custom Server ID. - default: 'None' - - serverid: - description: - - "The identifier for the service. This is used when the persistency type is set to Custom Server ID." - - cka: - description: - - "Enable client keep-alive for the service." - type: bool - - tcpb: - description: - - "Enable TCP buffering for the service." - type: bool - - cmp: - description: - - "Enable compression for the service." - type: bool - - maxbandwidth: - description: - - "Maximum bandwidth, in Kbps, allocated to the service." - - "Minimum value = 0" - - "Maximum value = 4294967287" - - accessdown: - description: - - >- - Use Layer 2 mode to bridge the packets sent to this service if it is marked as DOWN. If the service - is DOWN, and this parameter is disabled, the packets are dropped. - default: no - type: bool - monthreshold: - description: - - >- - Minimum sum of weights of the monitors that are bound to this service. Used to determine whether to - mark a service as UP or DOWN. - - "Minimum value = 0" - - "Maximum value = 65535" - - downstateflush: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Flush all active transactions associated with a service whose state transitions from UP to DOWN. Do - not enable this option for applications that must complete their transactions. - - tcpprofilename: - description: - - "Name of the TCP profile that contains TCP configuration settings for the service." - - "Minimum length = 1" - - "Maximum length = 127" - - httpprofilename: - description: - - "Name of the HTTP profile that contains HTTP configuration settings for the service." - - "Minimum length = 1" - - "Maximum length = 127" - - hashid: - description: - - >- - A numerical identifier that can be used by hash based load balancing methods. Must be unique for each - service. - - "Minimum value = 1" - - comment: - description: - - "Any information about the service." - - appflowlog: - choices: - - 'enabled' - - 'disabled' - description: - - "Enable logging of AppFlow information." - - netprofile: - description: - - "Network profile to use for the service." - - "Minimum length = 1" - - "Maximum length = 127" - - td: - description: - - >- - Integer value that uniquely identifies the traffic domain in which you want to configure the entity. - If you do not specify an ID, the entity becomes part of the default traffic domain, which has an ID - of 0. - - "Minimum value = 0" - - "Maximum value = 4094" - - processlocal: - choices: - - 'enabled' - - 'disabled' - description: - - >- - By turning on this option packets destined to a service in a cluster will not under go any steering. - Turn this option for single packet request response mode or when the upstream device is performing a - proper RSS for connection based distribution. - - dnsprofilename: - description: - - >- - Name of the DNS profile to be associated with the service. DNS profile properties will applied to the - transactions processed by a service. This parameter is valid only for ADNS and ADNS-TCP services. - - "Minimum length = 1" - - "Maximum length = 127" - - ipaddress: - description: - - "The new IP address of the service." - - graceful: - description: - - >- - Shut down gracefully, not accepting any new connections, and disabling the service when all of its - connections are closed. - default: no - type: bool - - monitor_bindings: - description: - - A list of load balancing monitors to bind to this service. - - Each monitor entry is a dictionary which may contain the following options. - - Note that if not using the built in monitors they must first be setup. - suboptions: - monitorname: - description: - - Name of the monitor. - weight: - description: - - Weight to assign to the binding between the monitor and service. - dup_state: - choices: - - 'enabled' - - 'disabled' - description: - - State of the monitor. - - The state setting for a monitor of a given type affects all monitors of that type. - - For example, if an HTTP monitor is enabled, all HTTP monitors on the appliance are (or remain) enabled. - - If an HTTP monitor is disabled, all HTTP monitors on the appliance are disabled. - dup_weight: - description: - - Weight to assign to the binding between the monitor and service. - - disabled: - description: - - When set to C(yes) the service state will be set to DISABLED. - - When set to C(no) the service state will be set to ENABLED. - - >- - Note that due to limitations of the underlying NITRO API a C(disabled) state change alone - does not cause the module result to report a changed status. - type: bool - default: false - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -# Monitor monitor-1 must have been already setup - -- name: Setup http service - gather_facts: False - delegate_to: localhost - netscaler_service: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - - state: present - - name: service-http-1 - servicetype: HTTP - ipaddress: 10.78.0.1 - port: 80 - - monitor_bindings: - - monitor-1 -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: "['message 1', 'message 2']" - -diff: - description: A dictionary with a list of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: "{ 'clttimeout': 'difference. ours: (float) 10.0 other: (float) 20.0' }" -''' - -import copy - -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.basic.service import service - from nssrc.com.citrix.netscaler.nitro.resource.config.basic.service_lbmonitor_binding import service_lbmonitor_binding - from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbmonitor_service_binding import lbmonitor_service_binding - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import (ConfigProxy, get_nitro_client, netscaler_common_arguments, - log, loglines, get_immutables_intersection) - - -def service_exists(client, module): - if service.count_filtered(client, 'name:%s' % module.params['name']) > 0: - return True - else: - return False - - -def service_identical(client, module, service_proxy): - service_list = service.get_filtered(client, 'name:%s' % module.params['name']) - diff_dict = service_proxy.diff_object(service_list[0]) - # the actual ip address is stored in the ipaddress attribute - # of the retrieved object - if 'ip' in diff_dict: - del diff_dict['ip'] - if 'graceful' in diff_dict: - del diff_dict['graceful'] - if len(diff_dict) == 0: - return True - else: - return False - - -def diff(client, module, service_proxy): - service_list = service.get_filtered(client, 'name:%s' % module.params['name']) - diff_object = service_proxy.diff_object(service_list[0]) - if 'ip' in diff_object: - del diff_object['ip'] - return diff_object - - -def get_configured_monitor_bindings(client, module, monitor_bindings_rw_attrs): - bindings = {} - if module.params['monitor_bindings'] is not None: - for binding in module.params['monitor_bindings']: - attribute_values_dict = copy.deepcopy(binding) - # attribute_values_dict['servicename'] = module.params['name'] - attribute_values_dict['servicegroupname'] = module.params['name'] - binding_proxy = ConfigProxy( - actual=lbmonitor_service_binding(), - client=client, - attribute_values_dict=attribute_values_dict, - readwrite_attrs=monitor_bindings_rw_attrs, - ) - key = binding_proxy.monitorname - bindings[key] = binding_proxy - return bindings - - -def get_actual_monitor_bindings(client, module): - bindings = {} - if service_lbmonitor_binding.count(client, module.params['name']) == 0: - return bindings - - # Fallthrough to rest of execution - for binding in service_lbmonitor_binding.get(client, module.params['name']): - # Excluding default monitors since we cannot operate on them - if binding.monitor_name in ('tcp-default', 'ping-default'): - continue - key = binding.monitor_name - actual = lbmonitor_service_binding() - actual.weight = binding.weight - actual.monitorname = binding.monitor_name - actual.dup_weight = binding.dup_weight - actual.servicename = module.params['name'] - bindings[key] = actual - - return bindings - - -def monitor_bindings_identical(client, module, monitor_bindings_rw_attrs): - configured_proxys = get_configured_monitor_bindings(client, module, monitor_bindings_rw_attrs) - actual_bindings = get_actual_monitor_bindings(client, module) - - configured_key_set = set(configured_proxys.keys()) - actual_key_set = set(actual_bindings.keys()) - symmetrical_diff = configured_key_set ^ actual_key_set - if len(symmetrical_diff) > 0: - return False - - # Compare key to key - for monitor_name in configured_key_set: - proxy = configured_proxys[monitor_name] - actual = actual_bindings[monitor_name] - diff_dict = proxy.diff_object(actual) - if 'servicegroupname' in diff_dict: - if proxy.servicegroupname == actual.servicename: - del diff_dict['servicegroupname'] - if len(diff_dict) > 0: - return False - - # Fallthrought to success - return True - - -def sync_monitor_bindings(client, module, monitor_bindings_rw_attrs): - configured_proxys = get_configured_monitor_bindings(client, module, monitor_bindings_rw_attrs) - actual_bindings = get_actual_monitor_bindings(client, module) - configured_keyset = set(configured_proxys.keys()) - actual_keyset = set(actual_bindings.keys()) - - # Delete extra - delete_keys = list(actual_keyset - configured_keyset) - for monitor_name in delete_keys: - log('Deleting binding for monitor %s' % monitor_name) - lbmonitor_service_binding.delete(client, actual_bindings[monitor_name]) - - # Delete and re-add modified - common_keyset = list(configured_keyset & actual_keyset) - for monitor_name in common_keyset: - proxy = configured_proxys[monitor_name] - actual = actual_bindings[monitor_name] - if not proxy.has_equal_attributes(actual): - log('Deleting and re adding binding for monitor %s' % monitor_name) - lbmonitor_service_binding.delete(client, actual) - proxy.add() - - # Add new - new_keys = list(configured_keyset - actual_keyset) - for monitor_name in new_keys: - log('Adding binding for monitor %s' % monitor_name) - configured_proxys[monitor_name].add() - - -def all_identical(client, module, service_proxy, monitor_bindings_rw_attrs): - return service_identical(client, module, service_proxy) and monitor_bindings_identical(client, module, monitor_bindings_rw_attrs) - - -def do_state_change(client, module, service_proxy): - if module.params['disabled']: - log('Disabling service') - result = service.disable(client, service_proxy.actual) - else: - log('Enabling service') - result = service.enable(client, service_proxy.actual) - return result - - -def main(): - - module_specific_arguments = dict( - name=dict(type='str'), - ip=dict(type='str'), - servername=dict(type='str'), - servicetype=dict( - type='str', - choices=[ - 'HTTP', - 'FTP', - 'TCP', - 'UDP', - 'SSL', - 'SSL_BRIDGE', - 'SSL_TCP', - 'DTLS', - 'NNTP', - 'RPCSVR', - 'DNS', - 'ADNS', - 'SNMP', - 'RTSP', - 'DHCPRA', - 'ANY', - 'SIP_UDP', - 'SIP_TCP', - 'SIP_SSL', - 'DNS_TCP', - 'ADNS_TCP', - 'MYSQL', - 'MSSQL', - 'ORACLE', - 'RADIUS', - 'RADIUSListener', - 'RDP', - 'DIAMETER', - 'SSL_DIAMETER', - 'TFTP', - 'SMPP', - 'PPTP', - 'GRE', - 'SYSLOGTCP', - 'SYSLOGUDP', - 'FIX', - 'SSL_FIX' - ] - ), - port=dict(type='int'), - cleartextport=dict(type='int'), - cachetype=dict( - type='str', - choices=[ - 'TRANSPARENT', - 'REVERSE', - 'FORWARD', - ] - ), - maxclient=dict(type='float'), - healthmonitor=dict( - type='bool', - default=True, - ), - maxreq=dict(type='float'), - cacheable=dict( - type='bool', - default=False, - ), - cip=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - cipheader=dict(type='str'), - usip=dict(type='bool'), - useproxyport=dict(type='bool'), - sp=dict(type='bool'), - rtspsessionidremap=dict( - type='bool', - default=False, - ), - clttimeout=dict(type='float'), - svrtimeout=dict(type='float'), - customserverid=dict( - type='str', - default='None', - ), - cka=dict(type='bool'), - tcpb=dict(type='bool'), - cmp=dict(type='bool'), - maxbandwidth=dict(type='float'), - accessdown=dict( - type='bool', - default=False - ), - monthreshold=dict(type='float'), - downstateflush=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ], - ), - tcpprofilename=dict(type='str'), - httpprofilename=dict(type='str'), - hashid=dict(type='float'), - comment=dict(type='str'), - appflowlog=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ], - ), - netprofile=dict(type='str'), - processlocal=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ], - ), - dnsprofilename=dict(type='str'), - ipaddress=dict(type='str'), - graceful=dict( - type='bool', - default=False, - ), - ) - - hand_inserted_arguments = dict( - monitor_bindings=dict(type='list'), - disabled=dict( - type='bool', - default=False, - ), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - - argument_spec.update(module_specific_arguments) - - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - # Fallthrough to rest of execution - - # Instantiate Service Config object - readwrite_attrs = [ - 'name', - 'ip', - 'servername', - 'servicetype', - 'port', - 'cleartextport', - 'cachetype', - 'maxclient', - 'healthmonitor', - 'maxreq', - 'cacheable', - 'cip', - 'cipheader', - 'usip', - 'useproxyport', - 'sp', - 'rtspsessionidremap', - 'clttimeout', - 'svrtimeout', - 'customserverid', - 'cka', - 'tcpb', - 'cmp', - 'maxbandwidth', - 'accessdown', - 'monthreshold', - 'downstateflush', - 'tcpprofilename', - 'httpprofilename', - 'hashid', - 'comment', - 'appflowlog', - 'netprofile', - 'processlocal', - 'dnsprofilename', - 'ipaddress', - 'graceful', - ] - - readonly_attrs = [ - 'numofconnections', - 'policyname', - 'serviceconftype', - 'serviceconftype2', - 'value', - 'gslb', - 'dup_state', - 'publicip', - 'publicport', - 'svrstate', - 'monitor_state', - 'monstatcode', - 'lastresponse', - 'responsetime', - 'riseapbrstatsmsgcode2', - 'monstatparam1', - 'monstatparam2', - 'monstatparam3', - 'statechangetimesec', - 'statechangetimemsec', - 'tickssincelaststatechange', - 'stateupdatereason', - 'clmonowner', - 'clmonview', - 'serviceipstr', - 'oracleserverversion', - ] - - immutable_attrs = [ - 'name', - 'ip', - 'servername', - 'servicetype', - 'port', - 'cleartextport', - 'cachetype', - 'cipheader', - 'serverid', - 'state', - 'td', - 'monitor_name_svc', - 'riseapbrstatsmsgcode', - 'all', - 'Internal', - 'newname', - ] - - transforms = { - 'pathmonitorindv': ['bool_yes_no'], - 'cacheable': ['bool_yes_no'], - 'cka': ['bool_yes_no'], - 'pathmonitor': ['bool_yes_no'], - 'tcpb': ['bool_yes_no'], - 'sp': ['bool_on_off'], - 'graceful': ['bool_yes_no'], - 'usip': ['bool_yes_no'], - 'healthmonitor': ['bool_yes_no'], - 'useproxyport': ['bool_yes_no'], - 'rtspsessionidremap': ['bool_on_off'], - 'accessdown': ['bool_yes_no'], - 'cmp': ['bool_yes_no'], - 'cip': [lambda v: v.upper()], - 'downstateflush': [lambda v: v.upper()], - 'appflowlog': [lambda v: v.upper()], - 'processlocal': [lambda v: v.upper()], - } - - monitor_bindings_rw_attrs = [ - 'servicename', - 'servicegroupname', - 'dup_state', - 'dup_weight', - 'monitorname', - 'weight', - ] - - # Translate module arguments to correspondign config object attributes - if module.params['ip'] is None: - module.params['ip'] = module.params['ipaddress'] - - service_proxy = ConfigProxy( - actual=service(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - - # Apply appropriate state - if module.params['state'] == 'present': - log('Applying actions for state present') - if not service_exists(client, module): - if not module.check_mode: - service_proxy.add() - sync_monitor_bindings(client, module, monitor_bindings_rw_attrs) - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not all_identical(client, module, service_proxy, monitor_bindings_rw_attrs): - - # Check if we try to change value of immutable attributes - diff_dict = diff(client, module, service_proxy) - immutables_changed = get_immutables_intersection(service_proxy, diff_dict.keys()) - if immutables_changed != []: - msg = 'Cannot update immutable attributes %s. Must delete and recreate entity.' % (immutables_changed,) - module.fail_json(msg=msg, diff=diff_dict, **module_result) - - # Service sync - if not service_identical(client, module, service_proxy): - if not module.check_mode: - service_proxy.update() - - # Monitor bindings sync - if not monitor_bindings_identical(client, module, monitor_bindings_rw_attrs): - if not module.check_mode: - sync_monitor_bindings(client, module, monitor_bindings_rw_attrs) - - module_result['changed'] = True - if not module.check_mode: - if module.params['save_config']: - client.save_config() - else: - module_result['changed'] = False - - if not module.check_mode: - res = do_state_change(client, module, service_proxy) - if res.errorcode != 0: - msg = 'Error when setting disabled state. errorcode: %s message: %s' % (res.errorcode, res.message) - module.fail_json(msg=msg, **module_result) - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state present') - if not service_exists(client, module): - module.fail_json(msg='Service does not exist', **module_result) - - if not service_identical(client, module, service_proxy): - module.fail_json(msg='Service differs from configured', diff=diff(client, module, service_proxy), **module_result) - - if not monitor_bindings_identical(client, module, monitor_bindings_rw_attrs): - module.fail_json(msg='Monitor bindings are not identical', **module_result) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if service_exists(client, module): - if not module.check_mode: - service_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state absent') - if service_exists(client, module): - module.fail_json(msg='Service still exists', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_servicegroup.py b/plugins/modules/network/netscaler/netscaler_servicegroup.py deleted file mode 100644 index 932d41bcbb..0000000000 --- a/plugins/modules/network/netscaler/netscaler_servicegroup.py +++ /dev/null @@ -1,1046 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_servicegroup -short_description: Manage service group configuration in Netscaler -description: - - Manage service group configuration in Netscaler. - - This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - servicegroupname: - description: - - >- - Name of the service group. Must begin with an ASCII alphabetic or underscore C(_) character, and must - contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space C( ), colon C(:), at C(@), equals - C(=), and hyphen C(-) characters. Can be changed after the name is created. - - "Minimum length = 1" - - servicetype: - choices: - - 'HTTP' - - 'FTP' - - 'TCP' - - 'UDP' - - 'SSL' - - 'SSL_BRIDGE' - - 'SSL_TCP' - - 'DTLS' - - 'NNTP' - - 'RPCSVR' - - 'DNS' - - 'ADNS' - - 'SNMP' - - 'RTSP' - - 'DHCPRA' - - 'ANY' - - 'SIP_UDP' - - 'SIP_TCP' - - 'SIP_SSL' - - 'DNS_TCP' - - 'ADNS_TCP' - - 'MYSQL' - - 'MSSQL' - - 'ORACLE' - - 'RADIUS' - - 'RADIUSListener' - - 'RDP' - - 'DIAMETER' - - 'SSL_DIAMETER' - - 'TFTP' - - 'SMPP' - - 'PPTP' - - 'GRE' - - 'SYSLOGTCP' - - 'SYSLOGUDP' - - 'FIX' - - 'SSL_FIX' - description: - - "Protocol used to exchange data with the service." - - cachetype: - choices: - - 'TRANSPARENT' - - 'REVERSE' - - 'FORWARD' - description: - - "Cache type supported by the cache server." - - maxclient: - description: - - "Maximum number of simultaneous open connections for the service group." - - "Minimum value = C(0)" - - "Maximum value = C(4294967294)" - - maxreq: - description: - - "Maximum number of requests that can be sent on a persistent connection to the service group." - - "Note: Connection requests beyond this value are rejected." - - "Minimum value = C(0)" - - "Maximum value = C(65535)" - - cacheable: - description: - - "Use the transparent cache redirection virtual server to forward the request to the cache server." - - "Note: Do not set this parameter if you set the Cache Type." - type: bool - - cip: - choices: - - 'enabled' - - 'disabled' - description: - - "Insert the Client IP header in requests forwarded to the service." - - cipheader: - description: - - >- - Name of the HTTP header whose value must be set to the IP address of the client. Used with the Client - IP parameter. If client IP insertion is enabled, and the client IP header is not specified, the value - of Client IP Header parameter or the value set by the set ns config command is used as client's IP - header name. - - "Minimum length = 1" - - usip: - description: - - >- - Use client's IP address as the source IP address when initiating connection to the server. With the - NO setting, which is the default, a mapped IP (MIP) address or subnet IP (SNIP) address is used as - the source IP address to initiate server side connections. - type: bool - - pathmonitor: - description: - - "Path monitoring for clustering." - type: bool - - pathmonitorindv: - description: - - "Individual Path monitoring decisions." - type: bool - - useproxyport: - description: - - >- - Use the proxy port as the source port when initiating connections with the server. With the NO - setting, the client-side connection port is used as the source port for the server-side connection. - - "Note: This parameter is available only when the Use Source IP C(usip) parameter is set to C(yes)." - type: bool - - healthmonitor: - description: - - "Monitor the health of this service. Available settings function as follows:" - - "C(yes) - Send probes to check the health of the service." - - >- - C(no) - Do not send probes to check the health of the service. With the NO option, the appliance shows - the service as UP at all times. - type: bool - - sp: - description: - - "Enable surge protection for the service group." - type: bool - - rtspsessionidremap: - description: - - "Enable RTSP session ID mapping for the service group." - type: bool - - clttimeout: - description: - - "Time, in seconds, after which to terminate an idle client connection." - - "Minimum value = C(0)" - - "Maximum value = C(31536000)" - - svrtimeout: - description: - - "Time, in seconds, after which to terminate an idle server connection." - - "Minimum value = C(0)" - - "Maximum value = C(31536000)" - - cka: - description: - - "Enable client keep-alive for the service group." - type: bool - - tcpb: - description: - - "Enable TCP buffering for the service group." - type: bool - - cmp: - description: - - "Enable compression for the specified service." - type: bool - - maxbandwidth: - description: - - "Maximum bandwidth, in Kbps, allocated for all the services in the service group." - - "Minimum value = C(0)" - - "Maximum value = C(4294967287)" - - monthreshold: - description: - - >- - Minimum sum of weights of the monitors that are bound to this service. Used to determine whether to - mark a service as UP or DOWN. - - "Minimum value = C(0)" - - "Maximum value = C(65535)" - - downstateflush: - choices: - - 'enabled' - - 'disabled' - description: - - >- - Flush all active transactions associated with all the services in the service group whose state - transitions from UP to DOWN. Do not enable this option for applications that must complete their - transactions. - - tcpprofilename: - description: - - "Name of the TCP profile that contains TCP configuration settings for the service group." - - "Minimum length = 1" - - "Maximum length = 127" - - httpprofilename: - description: - - "Name of the HTTP profile that contains HTTP configuration settings for the service group." - - "Minimum length = 1" - - "Maximum length = 127" - - comment: - description: - - "Any information about the service group." - - appflowlog: - choices: - - 'enabled' - - 'disabled' - description: - - "Enable logging of AppFlow information for the specified service group." - - netprofile: - description: - - "Network profile for the service group." - - "Minimum length = 1" - - "Maximum length = 127" - - autoscale: - choices: - - 'DISABLED' - - 'DNS' - - 'POLICY' - description: - - "Auto scale option for a servicegroup." - - memberport: - description: - - "member port." - - graceful: - description: - - "Wait for all existing connections to the service to terminate before shutting down the service." - type: bool - - servicemembers: - description: - - A list of dictionaries describing each service member of the service group. - suboptions: - ip: - description: - - IP address of the service. Must not overlap with an existing server entity defined by name. - - port: - description: - - Server port number. - - Range C(1) - C(65535) - - "* in CLI is represented as 65535 in NITRO API" - state: - choices: - - 'enabled' - - 'disabled' - description: - - Initial state of the service after binding. - hashid: - description: - - The hash identifier for the service. - - This must be unique for each service. - - This parameter is used by hash based load balancing methods. - - Minimum value = C(1) - - serverid: - description: - - The identifier for the service. - - This is used when the persistency type is set to Custom Server ID. - - servername: - description: - - Name of the server to which to bind the service group. - - The server must already be configured as a named server. - - Minimum length = 1 - - customserverid: - description: - - The identifier for this IP:Port pair. - - Used when the persistency type is set to Custom Server ID. - - weight: - description: - - Weight to assign to the servers in the service group. - - Specifies the capacity of the servers relative to the other servers in the load balancing configuration. - - The higher the weight, the higher the percentage of requests sent to the service. - - Minimum value = C(1) - - Maximum value = C(100) - - monitorbindings: - description: - - A list of monitornames to bind to this service - - Note that the monitors must have already been setup possibly using the M(netscaler_lb_monitor) module or some other method - suboptions: - monitorname: - description: - - The monitor name to bind to this servicegroup. - weight: - description: - - Weight to assign to the binding between the monitor and servicegroup. - - disabled: - description: - - When set to C(yes) the service group state will be set to DISABLED. - - When set to C(no) the service group state will be set to ENABLED. - - >- - Note that due to limitations of the underlying NITRO API a C(disabled) state change alone - does not cause the module result to report a changed status. - type: bool - default: false - - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' -# The LB Monitors monitor-1 and monitor-2 must already exist -# Service members defined by C(ip) must not redefine an existing server's ip address. -# Service members defined by C(servername) must already exist. - -- name: Setup http service with ip members - delegate_to: localhost - netscaler_servicegroup: - nsip: 172.18.0.2 - nitro_user: nsroot - nitro_pass: nsroot - - state: present - - servicegroupname: service-group-1 - servicetype: HTTP - servicemembers: - - ip: 10.78.78.78 - port: 80 - weight: 50 - - ip: 10.79.79.79 - port: 80 - weight: 40 - - servername: server-1 - port: 80 - weight: 10 - - monitorbindings: - - monitorname: monitor-1 - weight: 50 - - monitorname: monitor-2 - weight: 50 - -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: ['message 1', 'message 2'] - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: { 'clttimeout': 'difference. ours: (float) 10.0 other: (float) 20.0' } -''' - -from ansible.module_utils.basic import AnsibleModule -import copy - -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import (ConfigProxy, get_nitro_client, netscaler_common_arguments, - log, loglines, get_immutables_intersection) -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.basic.servicegroup import servicegroup - from nssrc.com.citrix.netscaler.nitro.resource.config.basic.servicegroup_servicegroupmember_binding import servicegroup_servicegroupmember_binding - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - - from nssrc.com.citrix.netscaler.nitro.resource.config.basic.servicegroup_lbmonitor_binding import servicegroup_lbmonitor_binding - from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbmonitor_servicegroup_binding import lbmonitor_servicegroup_binding - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - - -def servicegroup_exists(client, module): - log('Checking if service group exists') - count = servicegroup.count_filtered(client, 'servicegroupname:%s' % module.params['servicegroupname']) - log('count is %s' % count) - if count > 0: - return True - else: - return False - - -def servicegroup_identical(client, module, servicegroup_proxy): - log('Checking if service group is identical') - servicegroups = servicegroup.get_filtered(client, 'servicegroupname:%s' % module.params['servicegroupname']) - if servicegroup_proxy.has_equal_attributes(servicegroups[0]): - return True - else: - return False - - -def get_configured_service_members(client, module): - log('get_configured_service_members') - readwrite_attrs = [ - 'servicegroupname', - 'ip', - 'port', - 'state', - 'hashid', - 'serverid', - 'servername', - 'customserverid', - 'weight' - ] - readonly_attrs = [ - 'delay', - 'statechangetimesec', - 'svrstate', - 'tickssincelaststatechange', - 'graceful', - ] - - members = [] - if module.params['servicemembers'] is None: - return members - - for config in module.params['servicemembers']: - # Make a copy to update - config = copy.deepcopy(config) - config['servicegroupname'] = module.params['servicegroupname'] - member_proxy = ConfigProxy( - actual=servicegroup_servicegroupmember_binding(), - client=client, - attribute_values_dict=config, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs - ) - members.append(member_proxy) - return members - - -def get_actual_service_members(client, module): - try: - # count() raises nitro exception instead of returning 0 - count = servicegroup_servicegroupmember_binding.count(client, module.params['servicegroupname']) - if count > 0: - servicegroup_members = servicegroup_servicegroupmember_binding.get(client, module.params['servicegroupname']) - else: - servicegroup_members = [] - except nitro_exception as e: - if e.errorcode == 258: - servicegroup_members = [] - else: - raise - return servicegroup_members - - -def servicemembers_identical(client, module): - log('servicemembers_identical') - - servicegroup_members = get_actual_service_members(client, module) - log('servicemembers %s' % servicegroup_members) - module_servicegroups = get_configured_service_members(client, module) - log('Number of service group members %s' % len(servicegroup_members)) - if len(servicegroup_members) != len(module_servicegroups): - return False - - # Fallthrough to member evaluation - identical_count = 0 - for actual_member in servicegroup_members: - for member in module_servicegroups: - if member.has_equal_attributes(actual_member): - identical_count += 1 - break - if identical_count != len(servicegroup_members): - return False - - # Fallthrough to success - return True - - -def sync_service_members(client, module): - log('sync_service_members') - configured_service_members = get_configured_service_members(client, module) - actual_service_members = get_actual_service_members(client, module) - skip_add = [] - skip_delete = [] - - # Find positions of identical service members - for (configured_index, configured_service) in enumerate(configured_service_members): - for (actual_index, actual_service) in enumerate(actual_service_members): - if configured_service.has_equal_attributes(actual_service): - skip_add.append(configured_index) - skip_delete.append(actual_index) - - # Delete actual that are not identical to any configured - for (actual_index, actual_service) in enumerate(actual_service_members): - # Skip identical - if actual_index in skip_delete: - log('Skipping actual delete at index %s' % actual_index) - continue - - # Fallthrouth to deletion - if all([ - hasattr(actual_service, 'ip'), - actual_service.ip is not None, - hasattr(actual_service, 'servername'), - actual_service.servername is not None, - ]): - actual_service.ip = None - - actual_service.servicegroupname = module.params['servicegroupname'] - servicegroup_servicegroupmember_binding.delete(client, actual_service) - - # Add configured that are not already present in actual - for (configured_index, configured_service) in enumerate(configured_service_members): - - # Skip identical - if configured_index in skip_add: - log('Skipping configured add at index %s' % configured_index) - continue - - # Fallthrough to addition - configured_service.add() - - -def monitor_binding_equal(configured, actual): - if any([configured.monitorname != actual.monitor_name, - configured.servicegroupname != actual.servicegroupname, - configured.weight != float(actual.weight)]): - return False - return True - - -def get_configured_monitor_bindings(client, module): - log('Entering get_configured_monitor_bindings') - bindings = {} - if 'monitorbindings' in module.params and module.params['monitorbindings'] is not None: - for binding in module.params['monitorbindings']: - readwrite_attrs = [ - 'monitorname', - 'servicegroupname', - 'weight', - ] - readonly_attrs = [] - attribute_values_dict = copy.deepcopy(binding) - attribute_values_dict['servicegroupname'] = module.params['servicegroupname'] - binding_proxy = ConfigProxy( - actual=lbmonitor_servicegroup_binding(), - client=client, - attribute_values_dict=attribute_values_dict, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - ) - key = attribute_values_dict['monitorname'] - bindings[key] = binding_proxy - return bindings - - -def get_actual_monitor_bindings(client, module): - log('Entering get_actual_monitor_bindings') - bindings = {} - try: - # count() raises nitro exception instead of returning 0 - count = servicegroup_lbmonitor_binding.count(client, module.params['servicegroupname']) - except nitro_exception as e: - if e.errorcode == 258: - return bindings - else: - raise - - if count == 0: - return bindings - - # Fallthrough to rest of execution - for binding in servicegroup_lbmonitor_binding.get(client, module.params['servicegroupname']): - log('Gettign actual monitor with name %s' % binding.monitor_name) - key = binding.monitor_name - bindings[key] = binding - - return bindings - - -def monitor_bindings_identical(client, module): - log('Entering monitor_bindings_identical') - configured_bindings = get_configured_monitor_bindings(client, module) - actual_bindings = get_actual_monitor_bindings(client, module) - - configured_key_set = set(configured_bindings.keys()) - actual_key_set = set(actual_bindings.keys()) - symmetrical_diff = configured_key_set ^ actual_key_set - for default_monitor in ('tcp-default', 'ping-default'): - if default_monitor in symmetrical_diff: - log('Excluding %s monitor from key comparison' % default_monitor) - symmetrical_diff.remove(default_monitor) - if len(symmetrical_diff) > 0: - return False - - # Compare key to key - for key in configured_key_set: - configured_proxy = configured_bindings[key] - - # Follow nscli convention for missing weight value - if not hasattr(configured_proxy, 'weight'): - configured_proxy.weight = 1 - log('configured_proxy %s' % [configured_proxy.monitorname, configured_proxy.servicegroupname, configured_proxy.weight]) - log('actual_bindings %s' % [actual_bindings[key].monitor_name, actual_bindings[key].servicegroupname, actual_bindings[key].weight]) - if not monitor_binding_equal(configured_proxy, actual_bindings[key]): - return False - - # Fallthrought to success - return True - - -def sync_monitor_bindings(client, module): - log('Entering sync_monitor_bindings') - - actual_bindings = get_actual_monitor_bindings(client, module) - - # Exclude default monitors from deletion - for monitorname in ('tcp-default', 'ping-default'): - if monitorname in actual_bindings: - del actual_bindings[monitorname] - - configured_bindings = get_configured_monitor_bindings(client, module) - - to_remove = list(set(actual_bindings.keys()) - set(configured_bindings.keys())) - to_add = list(set(configured_bindings.keys()) - set(actual_bindings.keys())) - to_modify = list(set(configured_bindings.keys()) & set(actual_bindings.keys())) - - # Delete existing and modifiable bindings - for key in to_remove + to_modify: - binding = actual_bindings[key] - b = lbmonitor_servicegroup_binding() - b.monitorname = binding.monitor_name - b.servicegroupname = module.params['servicegroupname'] - # Cannot remove default monitor bindings - if b.monitorname in ('tcp-default', 'ping-default'): - continue - lbmonitor_servicegroup_binding.delete(client, b) - - # Add new and modified bindings - for key in to_add + to_modify: - binding = configured_bindings[key] - log('Adding %s' % binding.monitorname) - binding.add() - - -def diff(client, module, servicegroup_proxy): - servicegroup_list = servicegroup.get_filtered(client, 'servicegroupname:%s' % module.params['servicegroupname']) - diff_object = servicegroup_proxy.diff_object(servicegroup_list[0]) - return diff_object - - -def do_state_change(client, module, servicegroup_proxy): - if module.params['disabled']: - log('Disabling service') - result = servicegroup.disable(client, servicegroup_proxy.actual) - else: - log('Enabling service') - result = servicegroup.enable(client, servicegroup_proxy.actual) - return result - - -def main(): - - module_specific_arguments = dict( - servicegroupname=dict(type='str'), - servicetype=dict( - type='str', - choices=[ - 'HTTP', - 'FTP', - 'TCP', - 'UDP', - 'SSL', - 'SSL_BRIDGE', - 'SSL_TCP', - 'DTLS', - 'NNTP', - 'RPCSVR', - 'DNS', - 'ADNS', - 'SNMP', - 'RTSP', - 'DHCPRA', - 'ANY', - 'SIP_UDP', - 'SIP_TCP', - 'SIP_SSL', - 'DNS_TCP', - 'ADNS_TCP', - 'MYSQL', - 'MSSQL', - 'ORACLE', - 'RADIUS', - 'RADIUSListener', - 'RDP', - 'DIAMETER', - 'SSL_DIAMETER', - 'TFTP', - 'SMPP', - 'PPTP', - 'GRE', - 'SYSLOGTCP', - 'SYSLOGUDP', - 'FIX', - 'SSL_FIX', - ] - ), - cachetype=dict( - type='str', - choices=[ - 'TRANSPARENT', - 'REVERSE', - 'FORWARD', - ] - ), - maxclient=dict(type='float'), - maxreq=dict(type='float'), - cacheable=dict(type='bool'), - cip=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - cipheader=dict(type='str'), - usip=dict(type='bool'), - pathmonitor=dict(type='bool'), - pathmonitorindv=dict(type='bool'), - useproxyport=dict(type='bool'), - healthmonitor=dict(type='bool'), - sp=dict(type='bool'), - rtspsessionidremap=dict(type='bool'), - clttimeout=dict(type='float'), - svrtimeout=dict(type='float'), - cka=dict(type='bool'), - tcpb=dict(type='bool'), - cmp=dict(type='bool'), - maxbandwidth=dict(type='float'), - monthreshold=dict(type='float'), - downstateflush=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - tcpprofilename=dict(type='str'), - httpprofilename=dict(type='str'), - comment=dict(type='str'), - appflowlog=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - netprofile=dict(type='str'), - autoscale=dict( - type='str', - choices=[ - 'DISABLED', - 'DNS', - 'POLICY', - ] - ), - memberport=dict(type='int'), - graceful=dict(type='bool'), - ) - - hand_inserted_arguments = dict( - servicemembers=dict(type='list'), - monitorbindings=dict(type='list'), - disabled=dict( - type='bool', - default=False, - ), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - argument_spec.update(module_specific_arguments) - argument_spec.update(hand_inserted_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - # Instantiate service group configuration object - readwrite_attrs = [ - 'servicegroupname', - 'servicetype', - 'cachetype', - 'maxclient', - 'maxreq', - 'cacheable', - 'cip', - 'cipheader', - 'usip', - 'pathmonitor', - 'pathmonitorindv', - 'useproxyport', - 'healthmonitor', - 'sp', - 'rtspsessionidremap', - 'clttimeout', - 'svrtimeout', - 'cka', - 'tcpb', - 'cmp', - 'maxbandwidth', - 'monthreshold', - 'downstateflush', - 'tcpprofilename', - 'httpprofilename', - 'comment', - 'appflowlog', - 'netprofile', - 'autoscale', - 'memberport', - 'graceful', - ] - - readonly_attrs = [ - 'numofconnections', - 'serviceconftype', - 'value', - 'svrstate', - 'ip', - 'monstatcode', - 'monstatparam1', - 'monstatparam2', - 'monstatparam3', - 'statechangetimemsec', - 'stateupdatereason', - 'clmonowner', - 'clmonview', - 'groupcount', - 'riseapbrstatsmsgcode2', - 'serviceipstr', - 'servicegroupeffectivestate' - ] - - immutable_attrs = [ - 'servicegroupname', - 'servicetype', - 'cachetype', - 'td', - 'cipheader', - 'state', - 'autoscale', - 'memberport', - 'servername', - 'port', - 'serverid', - 'monitor_name_svc', - 'dup_weight', - 'riseapbrstatsmsgcode', - 'delay', - 'graceful', - 'includemembers', - 'newname', - ] - - transforms = { - 'pathmonitorindv': ['bool_yes_no'], - 'cacheable': ['bool_yes_no'], - 'cka': ['bool_yes_no'], - 'pathmonitor': ['bool_yes_no'], - 'tcpb': ['bool_yes_no'], - 'sp': ['bool_on_off'], - 'usip': ['bool_yes_no'], - 'healthmonitor': ['bool_yes_no'], - 'useproxyport': ['bool_yes_no'], - 'rtspsessionidremap': ['bool_on_off'], - 'graceful': ['bool_yes_no'], - 'cmp': ['bool_yes_no'], - 'cip': [lambda v: v.upper()], - 'downstateflush': [lambda v: v.upper()], - 'appflowlog': [lambda v: v.upper()], - } - - # Instantiate config proxy - servicegroup_proxy = ConfigProxy( - actual=servicegroup(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - if module.params['state'] == 'present': - log('Applying actions for state present') - if not servicegroup_exists(client, module): - if not module.check_mode: - log('Adding service group') - servicegroup_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not servicegroup_identical(client, module, servicegroup_proxy): - - # Check if we try to change value of immutable attributes - diff_dict = diff(client, module, servicegroup_proxy) - immutables_changed = get_immutables_intersection(servicegroup_proxy, diff_dict.keys()) - if immutables_changed != []: - msg = 'Cannot update immutable attributes %s. Must delete and recreate entity.' % (immutables_changed,) - module.fail_json(msg=msg, diff=diff_dict, **module_result) - - if not module.check_mode: - servicegroup_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Check bindings - if not monitor_bindings_identical(client, module): - if not module.check_mode: - sync_monitor_bindings(client, module) - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - - if not servicemembers_identical(client, module): - if not module.check_mode: - sync_service_members(client, module) - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - - if not module.check_mode: - res = do_state_change(client, module, servicegroup_proxy) - if res.errorcode != 0: - msg = 'Error when setting disabled state. errorcode: %s message: %s' % (res.errorcode, res.message) - module.fail_json(msg=msg, **module_result) - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state present') - if not servicegroup_exists(client, module): - module.fail_json(msg='Service group is not present', **module_result) - if not servicegroup_identical(client, module, servicegroup_proxy): - module.fail_json( - msg='Service group is not identical to configuration', - diff=diff(client, module, servicegroup_proxy), - **module_result - ) - if not servicemembers_identical(client, module): - module.fail_json(msg='Service group members differ from configuration', **module_result) - if not monitor_bindings_identical(client, module): - module.fail_json(msg='Monitor bindings are not identical', **module_result) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if servicegroup_exists(client, module): - if not module.check_mode: - servicegroup_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state absent') - if servicegroup_exists(client, module): - module.fail_json(msg='Service group is present', **module_result) - - except nitro_exception as e: - msg = "nitro exception errorcode=" + str(e.errorcode) + ",message=" + e.message - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netscaler/netscaler_ssl_certkey.py b/plugins/modules/network/netscaler/netscaler_ssl_certkey.py deleted file mode 100644 index 743c3b453f..0000000000 --- a/plugins/modules/network/netscaler/netscaler_ssl_certkey.py +++ /dev/null @@ -1,372 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Citrix Systems -# 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: netscaler_ssl_certkey -short_description: Manage ssl certificate keys. -description: - - Manage ssl certificate keys. - - -author: George Nikolopoulos (@giorgos-nikolopoulos) - -options: - - certkey: - description: - - >- - Name for the certificate and private-key pair. Must begin with an ASCII alphanumeric or underscore - C(_) character, and must contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space C( ), - colon C(:), at C(@), equals C(=), and hyphen C(-) characters. Cannot be changed after the certificate-key - pair is created. - - "The following requirement applies only to the NetScaler CLI:" - - >- - If the name includes one or more spaces, enclose the name in double or single quotation marks (for - example, "my cert" or 'my cert'). - - "Minimum length = 1" - - cert: - description: - - >- - Name of and, optionally, path to the X509 certificate file that is used to form the certificate-key - pair. The certificate file should be present on the appliance's hard-disk drive or solid-state drive. - Storing a certificate in any location other than the default might cause inconsistency in a high - availability setup. /nsconfig/ssl/ is the default path. - - "Minimum length = 1" - - key: - description: - - >- - Name of and, optionally, path to the private-key file that is used to form the certificate-key pair. - The certificate file should be present on the appliance's hard-disk drive or solid-state drive. - Storing a certificate in any location other than the default might cause inconsistency in a high - availability setup. /nsconfig/ssl/ is the default path. - - "Minimum length = 1" - - password: - description: - - >- - Passphrase that was used to encrypt the private-key. Use this option to load encrypted private-keys - in PEM format. - - inform: - choices: - - 'DER' - - 'PEM' - - 'PFX' - description: - - >- - Input format of the certificate and the private-key files. The three formats supported by the - appliance are: - - "PEM - Privacy Enhanced Mail" - - "DER - Distinguished Encoding Rule" - - "PFX - Personal Information Exchange." - - passplain: - description: - - >- - Pass phrase used to encrypt the private-key. Required when adding an encrypted private-key in PEM - format. - - "Minimum length = 1" - - expirymonitor: - choices: - - 'enabled' - - 'disabled' - description: - - "Issue an alert when the certificate is about to expire." - - notificationperiod: - description: - - >- - Time, in number of days, before certificate expiration, at which to generate an alert that the - certificate is about to expire. - - "Minimum value = C(10)" - - "Maximum value = C(100)" - - -extends_documentation_fragment: -- community.general.netscaler - -requirements: - - nitro python sdk -''' - -EXAMPLES = ''' - -- name: Setup ssl certkey - delegate_to: localhost - netscaler_ssl_certkey: - nitro_user: nsroot - nitro_pass: nsroot - nsip: 172.18.0.2 - - certkey: certirificate_1 - cert: server.crt - key: server.key - expirymonitor: enabled - notificationperiod: 30 - inform: PEM - password: False - passplain: somesecret -''' - -RETURN = ''' -loglines: - description: list of logged messages by the module - returned: always - type: list - sample: "['message 1', 'message 2']" - -msg: - description: Message detailing the failure reason - returned: failure - type: str - sample: "Action does not exist" - -diff: - description: List of differences between the actual configured object and the configuration specified in the module - returned: failure - type: dict - sample: "{ 'targetlbvserver': 'difference. ours: (str) server1 other: (str) server2' }" -''' - -try: - from nssrc.com.citrix.netscaler.nitro.resource.config.ssl.sslcertkey import sslcertkey - from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception - PYTHON_SDK_IMPORTED = True -except ImportError as e: - PYTHON_SDK_IMPORTED = False - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import (ConfigProxy, get_nitro_client, netscaler_common_arguments, - log, loglines, get_immutables_intersection) - - -def key_exists(client, module): - log('Checking if key exists') - log('certkey is %s' % module.params['certkey']) - all_certificates = sslcertkey.get(client) - certkeys = [item.certkey for item in all_certificates] - if module.params['certkey'] in certkeys: - return True - else: - return False - - -def key_identical(client, module, sslcertkey_proxy): - log('Checking if configured key is identical') - sslcertkey_list = sslcertkey.get_filtered(client, 'certkey:%s' % module.params['certkey']) - diff_dict = sslcertkey_proxy.diff_object(sslcertkey_list[0]) - if 'password' in diff_dict: - del diff_dict['password'] - if 'passplain' in diff_dict: - del diff_dict['passplain'] - if len(diff_dict) == 0: - return True - else: - return False - - -def diff_list(client, module, sslcertkey_proxy): - sslcertkey_list = sslcertkey.get_filtered(client, 'certkey:%s' % module.params['certkey']) - return sslcertkey_proxy.diff_object(sslcertkey_list[0]) - - -def main(): - - module_specific_arguments = dict( - certkey=dict(type='str'), - cert=dict(type='str'), - key=dict(type='str'), - password=dict(type='bool'), - inform=dict( - type='str', - choices=[ - 'DER', - 'PEM', - 'PFX', - ] - ), - passplain=dict( - type='str', - no_log=True, - ), - expirymonitor=dict( - type='str', - choices=[ - 'enabled', - 'disabled', - ] - ), - notificationperiod=dict(type='float'), - ) - - argument_spec = dict() - - argument_spec.update(netscaler_common_arguments) - - argument_spec.update(module_specific_arguments) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - module_result = dict( - changed=False, - failed=False, - loglines=loglines, - ) - - # Fail the module if imports failed - if not PYTHON_SDK_IMPORTED: - module.fail_json(msg='Could not load nitro python sdk') - - # Fallthrough to rest of execution - client = get_nitro_client(module) - - try: - client.login() - except nitro_exception as e: - msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg) - except Exception as e: - if str(type(e)) == "": - module.fail_json(msg='Connection error %s' % str(e)) - elif str(type(e)) == "": - module.fail_json(msg='SSL Error %s' % str(e)) - else: - module.fail_json(msg='Unexpected error during login %s' % str(e)) - - readwrite_attrs = [ - 'certkey', - 'cert', - 'key', - 'password', - 'inform', - 'passplain', - 'expirymonitor', - 'notificationperiod', - ] - - readonly_attrs = [ - 'signaturealg', - 'certificatetype', - 'serial', - 'issuer', - 'clientcertnotbefore', - 'clientcertnotafter', - 'daystoexpiration', - 'subject', - 'publickey', - 'publickeysize', - 'version', - 'priority', - 'status', - 'passcrypt', - 'data', - 'servicename', - ] - - immutable_attrs = [ - 'certkey', - 'cert', - 'key', - 'password', - 'inform', - 'passplain', - ] - - transforms = { - 'expirymonitor': [lambda v: v.upper()], - } - - # Instantiate config proxy - sslcertkey_proxy = ConfigProxy( - actual=sslcertkey(), - client=client, - attribute_values_dict=module.params, - readwrite_attrs=readwrite_attrs, - readonly_attrs=readonly_attrs, - immutable_attrs=immutable_attrs, - transforms=transforms, - ) - - try: - - if module.params['state'] == 'present': - log('Applying actions for state present') - if not key_exists(client, module): - if not module.check_mode: - log('Adding certificate key') - sslcertkey_proxy.add() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - elif not key_identical(client, module, sslcertkey_proxy): - - # Check if we try to change value of immutable attributes - immutables_changed = get_immutables_intersection(sslcertkey_proxy, diff_list(client, module, sslcertkey_proxy).keys()) - if immutables_changed != []: - module.fail_json( - msg='Cannot update immutable attributes %s' % (immutables_changed,), - diff=diff_list(client, module, sslcertkey_proxy), - **module_result - ) - - if not module.check_mode: - sslcertkey_proxy.update() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state present') - if not key_exists(client, module): - module.fail_json(msg='SSL certkey does not exist') - if not key_identical(client, module, sslcertkey_proxy): - module.fail_json(msg='SSL certkey differs from configured', diff=diff_list(client, module, sslcertkey_proxy)) - - elif module.params['state'] == 'absent': - log('Applying actions for state absent') - if key_exists(client, module): - if not module.check_mode: - sslcertkey_proxy.delete() - if module.params['save_config']: - client.save_config() - module_result['changed'] = True - else: - module_result['changed'] = False - - # Sanity check for state - if not module.check_mode: - log('Sanity checks for state absent') - if key_exists(client, module): - module.fail_json(msg='SSL certkey still exists') - - except nitro_exception as e: - msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message) - module.fail_json(msg=msg, **module_result) - - client.logout() - module.exit_json(**module_result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/netvisor/pn_access_list.py b/plugins/modules/network/netvisor/pn_access_list.py deleted file mode 100644 index c6fa6dc5d3..0000000000 --- a/plugins/modules/network/netvisor/pn_access_list.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_access_list -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to create/delete access-list -description: - - This module can be used to create and delete an access list. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use 'present' to create access-list and - 'absent' to delete access-list. - required: True - choices: [ "present", "absent"] - pn_name: - description: - - Access List Name. - required: false - type: str - pn_scope: - description: - - 'scope. Available valid values - local or fabric.' - required: false - choices: ['local', 'fabric'] -''' - -EXAMPLES = """ -- name: access list functionality - pn_access_list: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_scope: "local" - state: "present" - -- name: access list functionality - pn_access_list: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_scope: "local" - state: "absent" - -- name: access list functionality - pn_access_list: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_scope: "fabric" - state: "present" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the access-list command. - returned: always - type: list -stderr: - description: set of error responses from the access-list command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the access-list-show command. - If a list with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - list_name = module.params['pn_name'] - - cli += ' access-list-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if list_name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='access-list-create', - absent='access-list-delete', - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_name=dict(required=False, type='str'), - pn_scope=dict(required=False, type='str', - choices=['local', 'fabric']), - ), - required_if=( - ["state", "present", ["pn_name", "pn_scope"]], - ["state", "absent", ["pn_name"]], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - list_name = module.params['pn_name'] - scope = module.params['pn_scope'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - ACC_LIST_EXISTS = check_cli(module, cli) - cli += ' %s name %s ' % (command, list_name) - - if command == 'access-list-delete': - if ACC_LIST_EXISTS is False: - module.exit_json( - skipped=True, - msg='access-list with name %s does not exist' % list_name - ) - else: - if command == 'access-list-create': - if ACC_LIST_EXISTS is True: - module.exit_json( - skipped=True, - msg='access list with name %s already exists' % list_name - ) - cli += ' scope %s ' % scope - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_access_list_ip.py b/plugins/modules/network/netvisor/pn_access_list_ip.py deleted file mode 100644 index bab79d106c..0000000000 --- a/plugins/modules/network/netvisor/pn_access_list_ip.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_access_list_ip -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove access-list-ip -description: - - This modules can be used to add and remove IPs associated with access list. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use 'present' to add access-list-ip and - 'absent' to remove access-list-ip. - required: True - choices: ["present", "absent"] - pn_ip: - description: - - IP associated with the access list. - required: False - default: '::' - type: str - pn_name: - description: - - Access List Name. - required: False - type: str -''' - -EXAMPLES = """ -- name: access list ip functionality - pn_access_list_ip: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_ip: "172.16.3.1" - state: "present" - -- name: access list ip functionality - pn_access_list_ip: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_ip: "172.16.3.1" - state: "absent" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the access-list-ip command. - returned: always - type: list -stderr: - description: set of error responses from the access-list-ip command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the access-list-ip-show command. - If ip exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - ip = module.params['pn_ip'] - clicopy = cli - - cli += ' access-list-show name %s no-show-headers ' % name - out = run_commands(module, cli)[1] - - if name not in out: - module.fail_json( - failed=True, - msg='access-list with name %s does not exist' % name - ) - - cli = clicopy - cli += ' access-list-ip-show name %s format ip no-show-headers' % name - - out = run_commands(module, cli)[1] - out = out.split() - return True if ip in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='access-list-ip-add', - absent='access-list-ip-remove', - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_ip=dict(required=False, type='str', default='::'), - pn_name=dict(required=False, type='str'), - ), - required_if=( - ["state", "present", ["pn_name"]], - ["state", "absent", ["pn_name", "pn_ip"]], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - ip = module.params['pn_ip'] - name = module.params['pn_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - IP_EXISTS = check_cli(module, cli) - cli += ' %s name %s ' % (command, name) - - if command == 'access-list-ip-remove': - if IP_EXISTS is False: - module.exit_json( - skipped=True, - msg='access-list with ip %s does not exist' % ip - ) - if ip: - cli += ' ip ' + ip - else: - if command == 'access-list-ip-add': - if IP_EXISTS is True: - module.exit_json( - skipped=True, - msg='access list with ip %s already exists' % ip - ) - if ip: - cli += ' ip ' + ip - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_admin_service.py b/plugins/modules/network/netvisor/pn_admin_service.py deleted file mode 100644 index 578e89f8d8..0000000000 --- a/plugins/modules/network/netvisor/pn_admin_service.py +++ /dev/null @@ -1,207 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_admin_service -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify admin-service -description: - - This module is used to modify services on the server-switch. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(update) to modify the admin-service. - required: True - type: str - choices: ['update'] - pn_web: - description: - - Web (HTTP) to enable or disable. - required: False - type: bool - pn_web_ssl: - description: - - Web SSL (HTTPS) to enable or disable. - required: False - type: bool - pn_snmp: - description: - - Simple Network Monitoring Protocol (SNMP) to enable or disable. - required: False - type: bool - pn_web_port: - description: - - Web (HTTP) port to enable or disable. - required: False - type: str - pn_web_ssl_port: - description: - - Web SSL (HTTPS) port to enable or disable. - required: False - type: str - pn_nfs: - description: - - Network File System (NFS) to enable or disable. - required: False - type: bool - pn_ssh: - description: - - Secure Shell to enable or disable. - required: False - type: bool - pn_web_log: - description: - - Web logging to enable or disable. - required: False - type: bool - pn__if: - description: - - administrative service interface. - required: False - type: str - choices: ['mgmt', 'data'] - pn_icmp: - description: - - Internet Message Control Protocol (ICMP) to enable or disable. - required: False - type: bool - pn_net_api: - description: - - Netvisor API to enable or disable APIs. - required: False - type: bool -''' - -EXAMPLES = """ -- name: admin service functionality - pn_admin_service: - pn_cliswitch: "sw01" - state: "update" - pn__if: "mgmt" - pn_web: False - pn_icmp: True - -- name: admin service functionality - pn_admin_service: - pn_cliswitch: "sw01" - state: "update" - pn_web: False - pn__if: "mgmt" - pn_snmp: True - pn_net_api: True - pn_ssh: True -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the admin-service command. - returned: always - type: list -stderr: - description: set of error responses from the admin-service command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, booleanArgs, run_cli - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='admin-service-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_web=dict(required=False, type='bool'), - pn_web_ssl=dict(required=False, type='bool'), - pn_snmp=dict(required=False, type='bool'), - pn_web_port=dict(required=False, type='str'), - pn_web_ssl_port=dict(required=False, type='str'), - pn_nfs=dict(required=False, type='bool'), - pn_ssh=dict(required=False, type='bool'), - pn_web_log=dict(required=False, type='bool'), - pn__if=dict(required=False, type='str', choices=['mgmt', 'data']), - pn_icmp=dict(required=False, type='bool'), - pn_net_api=dict(required=False, type='bool'), - ), - required_if=([['state', 'update', ['pn__if']]]), - required_one_of=[['pn_web', 'pn_web_ssl', 'pn_snmp', - 'pn_web_port', 'pn_web_ssl_port', 'pn_nfs', - 'pn_ssh', 'pn_web_log', 'pn_icmp', 'pn_net_api']] - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - web = module.params['pn_web'] - web_ssl = module.params['pn_web_ssl'] - snmp = module.params['pn_snmp'] - web_port = module.params['pn_web_port'] - web_ssl_port = module.params['pn_web_ssl_port'] - nfs = module.params['pn_nfs'] - ssh = module.params['pn_ssh'] - web_log = module.params['pn_web_log'] - _if = module.params['pn__if'] - icmp = module.params['pn_icmp'] - net_api = module.params['pn_net_api'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'admin-service-modify': - cli += ' %s ' % command - - if _if: - cli += ' if ' + _if - if web_port: - cli += ' web-port ' + web_port - if web_ssl_port: - cli += ' web-ssl-port ' + web_ssl_port - - cli += booleanArgs(web, 'web', 'no-web') - cli += booleanArgs(web_ssl, 'web-ssl', 'no-web-ssl') - cli += booleanArgs(snmp, 'snmp', 'no-snmp') - cli += booleanArgs(nfs, 'nfs', 'no-nfs') - cli += booleanArgs(ssh, 'ssh', 'no-ssh') - cli += booleanArgs(icmp, 'icmp', 'no-icmp') - cli += booleanArgs(net_api, 'net-api', 'no-net-api') - cli += booleanArgs(web_log, 'web-log', 'no-web-log') - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_admin_session_timeout.py b/plugins/modules/network/netvisor/pn_admin_session_timeout.py deleted file mode 100644 index 59e4cc1db8..0000000000 --- a/plugins/modules/network/netvisor/pn_admin_session_timeout.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_admin_session_timeout -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify admin-session-timeout -description: - - This module can be used to modify admin session timeout. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. - C(update) to modify the admin-session-timeout. - required: True - type: str - choices: ['update'] - pn_timeout: - description: - - Maximum time to wait for user activity before - terminating login session. Minimum should be 60s. - required: False - type: str -''' - -EXAMPLES = """ -- name: admin session timeout functionality - pn_admin_session_timeout: - pn_cliswitch: "sw01" - state: "update" - pn_timeout: "61s" - -- name: admin session timeout functionality - pn_admin_session_timeout: - pn_cliswitch: "sw01" - state: "update" - pn_timeout: "1d" - -- name: admin session timeout functionality - pn_admin_session_timeout: - pn_cliswitch: "sw01" - state: "update" - pn_timeout: "10d20m3h15s" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the admin-session-timeout command. - returned: always - type: list -stderr: - description: set of error responses from the admin-session-timeout command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='admin-session-timeout-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_timeout=dict(required=False, type='str'), - ), - required_together=[['state', 'pn_timeout']], - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - timeout = module.params['pn_timeout'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - if command == 'admin-session-timeout-modify': - cli += ' %s ' % command - if timeout: - cli += ' timeout ' + timeout - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_admin_syslog.py b/plugins/modules/network/netvisor/pn_admin_syslog.py deleted file mode 100644 index bc971c0355..0000000000 --- a/plugins/modules/network/netvisor/pn_admin_syslog.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_admin_syslog -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/modify/delete admin-syslog -description: - - This module can be used to create the scope and other parameters of syslog event collection. - - This module can be used to modify parameters of syslog event collection. - - This module can be used to delete the scope and other parameters of syslog event collection. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(present) to create admin-syslog and - C(absent) to delete admin-syslog C(update) to modify the admin-syslog. - required: True - type: str - choices: ['present', 'absent', 'update'] - pn_scope: - description: - - Scope of the system log. - required: False - type: str - choices: ['local', 'fabric'] - pn_host: - description: - - Hostname to log system events. - required: False - type: str - pn_port: - description: - - Host port. - required: False - type: str - pn_transport: - description: - - Transport for log events - tcp/tls or udp. - required: False - type: str - choices: ['tcp-tls', 'udp'] - default: 'udp' - pn_message_format: - description: - - message-format for log events - structured or legacy. - required: False - choices: ['structured', 'legacy'] - type: str - pn_name: - description: - - name of the system log. - required: False - type: str -''' - -EXAMPLES = """ -- name: admin-syslog functionality - pn_admin_syslog: - pn_cliswitch: "sw01" - state: "absent" - pn_name: "foo" - pn_scope: "local" - -- name: admin-syslog functionality - pn_admin_syslog: - pn_cliswitch: "sw01" - state: "present" - pn_name: "foo" - pn_scope: "local" - pn_host: "166.68.224.46" - pn_message_format: "structured" - -- name: admin-syslog functionality - pn_admin_syslog: - pn_cliswitch: "sw01" - state: "update" - pn_name: "foo" - pn_host: "166.68.224.10" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the admin-syslog command. - returned: always - type: list -stderr: - description: set of error responses from the admin-syslog command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the admin-syslog-show command. - If a user with given name exists, return as True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - - name = module.params['pn_name'] - - cli += ' admin-syslog-show format name no-show-headers' - out = run_commands(module, cli)[1] - - out = out.split() - - return True if name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='admin-syslog-create', - absent='admin-syslog-delete', - update='admin-syslog-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_scope=dict(required=False, type='str', - choices=['local', 'fabric']), - pn_host=dict(required=False, type='str'), - pn_port=dict(required=False, type='str'), - pn_transport=dict(required=False, type='str', - choices=['tcp-tls', 'udp'], default='udp'), - pn_message_format=dict(required=False, type='str', - choices=['structured', 'legacy']), - pn_name=dict(required=False, type='str'), - ), - required_if=( - ['state', 'present', ['pn_name', 'pn_host', 'pn_scope']], - ['state', 'absent', ['pn_name']], - ['state', 'update', ['pn_name']] - ), - required_one_of=[['pn_port', 'pn_message_format', - 'pn_host', 'pn_transport', 'pn_scope']] - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - scope = module.params['pn_scope'] - host = module.params['pn_host'] - port = module.params['pn_port'] - transport = module.params['pn_transport'] - message_format = module.params['pn_message_format'] - name = module.params['pn_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - SYSLOG_EXISTS = check_cli(module, cli) - cli += ' %s name %s ' % (command, name) - - if command == 'admin-syslog-modify': - if SYSLOG_EXISTS is False: - module.fail_json( - failed=True, - msg='admin syslog with name %s does not exist' % name - ) - - if command == 'admin-syslog-delete': - if SYSLOG_EXISTS is False: - module.exit_json( - skipped=True, - msg='admin syslog with name %s does not exist' % name - ) - - if command == 'admin-syslog-create': - if SYSLOG_EXISTS is True: - module.exit_json( - skipped=True, - msg='admin syslog user with name %s already exists' % name - ) - - if command == 'admin-syslog-create': - if scope: - cli += ' scope ' + scope - - if command != 'admin-syslog-delete': - if host: - cli += ' host ' + host - if port: - cli += ' port ' + port - if transport: - cli += ' transport ' + transport - if message_format: - cli += ' message-format ' + message_format - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_cluster.py b/plugins/modules/network/netvisor/pn_cluster.py deleted file mode 100644 index 06d09639cf..0000000000 --- a/plugins/modules/network/netvisor/pn_cluster.py +++ /dev/null @@ -1,322 +0,0 @@ -#!/usr/bin/python -""" PN CLI cluster-create/cluster-delete """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_cluster -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to create/delete a cluster. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute cluster-create or cluster-delete command. - - A cluster allows two switches to cooperate in high-availability (HA) - deployments. The nodes that form the cluster must be members of the same - fabric. Clusters are typically used in conjunction with a virtual link - aggregation group (VLAG) that allows links physically connected to two - separate switches appear as a single trunk to a third device. The third - device can be a switch,server, or any Ethernet device. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch to run the cli on. - required: False - default: 'local' - state: - description: - - Specify action to perform. Use 'present' to create cluster and 'absent' - to delete cluster. - required: true - choices: ['present', 'absent'] - pn_name: - description: - - Specify the name of the cluster. - required: true - pn_cluster_node1: - description: - - Specify the name of the first switch in the cluster. - - Required for 'cluster-create'. - pn_cluster_node2: - description: - - Specify the name of the second switch in the cluster. - - Required for 'cluster-create'. - pn_validate: - description: - - Validate the inter-switch links and state of switches in the cluster. - type: bool -''' - -EXAMPLES = """ -- name: create spine cluster - pn_cluster: - state: 'present' - pn_name: 'spine-cluster' - pn_cluster_node1: 'spine01' - pn_cluster_node2: 'spine02' - pn_validate: True - pn_quiet: True - -- name: delete spine cluster - pn_cluster: - state: 'absent' - pn_name: 'spine-cluster' - pn_quiet: True -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the cluster command. - returned: always - type: list -stderr: - description: The set of error responses from the cluster command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# AnsibleModule boilerplate -from ansible.module_utils.basic import AnsibleModule - -NAME_EXISTS = None -NODE1_EXISTS = None -NODE2_EXISTS = None - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks for idempotency using the cluster-show command. - If a cluster with given name exists, return NAME_EXISTS as True else False. - If the given cluster-node-1 is already a part of another cluster, return - NODE1_EXISTS as True else False. - If the given cluster-node-2 is already a part of another cluster, return - NODE2_EXISTS as True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: NAME_EXISTS, NODE1_EXISTS, NODE2_EXISTS - """ - name = module.params['pn_name'] - node1 = module.params['pn_cluster_node1'] - node2 = module.params['pn_cluster_node2'] - - show = cli + ' cluster-show format name,cluster-node-1,cluster-node-2 ' - show = shlex.split(show) - out = module.run_command(show)[1] - - out = out.split() - # Global flags - global NAME_EXISTS, NODE1_EXISTS, NODE2_EXISTS - - if name in out: - NAME_EXISTS = True - else: - NAME_EXISTS = False - if node1 in out: - NODE1_EXISTS = True - else: - NODE2_EXISTS = False - if node2 in out: - NODE2_EXISTS = True - else: - NODE2_EXISTS = False - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - - cmd = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'cluster-create' - if state == 'absent': - command = 'cluster-delete' - return command - - -def main(): - """ This section is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(required=True, type='str', - choices=['present', 'absent']), - pn_name=dict(required=True, type='str'), - pn_cluster_node1=dict(type='str'), - pn_cluster_node2=dict(type='str'), - pn_validate=dict(type='bool') - ), - required_if=( - ["state", "present", - ["pn_name", "pn_cluster_node1", "pn_cluster_node2"]], - ["state", "absent", ["pn_name"]] - ) - ) - - # Accessing the parameters - state = module.params['state'] - name = module.params['pn_name'] - cluster_node1 = module.params['pn_cluster_node1'] - cluster_node2 = module.params['pn_cluster_node2'] - validate = module.params['pn_validate'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = pn_cli(module) - - if command == 'cluster-create': - - check_cli(module, cli) - - if NAME_EXISTS is True: - module.exit_json( - skipped=True, - msg='Cluster with name %s already exists' % name - ) - if NODE1_EXISTS is True: - module.exit_json( - skipped=True, - msg='Node %s already part of a cluster' % cluster_node1 - ) - if NODE2_EXISTS is True: - module.exit_json( - skipped=True, - msg='Node %s already part of a cluster' % cluster_node2 - ) - - cli += ' %s name %s ' % (command, name) - cli += 'cluster-node-1 %s cluster-node-2 %s ' % (cluster_node1, - cluster_node2) - if validate is True: - cli += ' validate ' - if validate is False: - cli += ' no-validate ' - - if command == 'cluster-delete': - - check_cli(module, cli) - - if NAME_EXISTS is False: - module.exit_json( - skipped=True, - msg='Cluster with name %s does not exist' % name - ) - cli += ' %s name %s ' % (command, name) - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_connection_stats_settings.py b/plugins/modules/network/netvisor/pn_connection_stats_settings.py deleted file mode 100644 index c6cc882ffe..0000000000 --- a/plugins/modules/network/netvisor/pn_connection_stats_settings.py +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_connection_stats_settings -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify connection-stats-settings -description: - - This module can be used to modify the settings for collecting statistical - data about connections. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(update) to modify the - connection-stats-settings. - required: True - type: str - choices: ['update'] - pn_enable: - description: - - Enable or disable collecting connections statistics. - required: False - type: bool - pn_connection_backup_enable: - description: - - Enable backup for connection statistics collection. - required: False - type: bool - pn_client_server_stats_max_memory: - description: - - maximum memory for client server statistics. - required: False - type: str - pn_connection_stats_log_disk_space: - description: - - disk-space allocated for statistics (including rotated log files). - required: False - type: str - pn_client_server_stats_log_enable: - description: - - Enable or disable statistics. - required: False - type: bool - pn_service_stat_max_memory: - description: - - maximum memory allowed for service statistics. - required: False - type: str - pn_connection_stats_log_interval: - description: - - interval to collect statistics. - required: False - type: str - pn_fabric_connection_backup_interval: - description: - - backup interval for fabric connection statistics collection. - required: False - type: str - pn_connection_backup_interval: - description: - - backup interval for connection statistics collection. - required: False - type: str - pn_connection_stats_log_enable: - description: - - enable or disable statistics. - required: False - type: bool - pn_fabric_connection_max_memory: - description: - - maximum memory allowed for fabric connection statistics. - required: False - type: str - pn_fabric_connection_backup_enable: - description: - - enable backup for fabric connection statistics collection. - required: False - type: bool - pn_client_server_stats_log_disk_space: - description: - - disk-space allocated for statistics (including rotated log files). - required: False - type: str - pn_connection_max_memory: - description: - - maximum memory allowed for connection statistics. - required: False - type: str - pn_connection_stats_max_memory: - description: - - maximum memory allowed for connection statistics. - required: False - type: str - pn_client_server_stats_log_interval: - description: - - interval to collect statistics. - required: False - type: str -''' - -EXAMPLES = """ -- name: "Modify connection stats settings" - pn_connection_stats_settings: - pn_cliswitch: "sw01" - state: "update" - pn_enable: False - pn_fabric_connection_max_memory: "1000" - -- name: "Modify connection stats settings" - pn_connection_stats_settings: - pn_cliswitch: "sw01" - state: "update" - pn_enable: True -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the connection-stats-settings command. - returned: always - type: list -stderr: - description: set of error responses from the connection-stats-settings command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='connection-stats-settings-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_enable=dict(required=False, type='bool'), - pn_connection_backup_enable=dict(required=False, type='bool'), - pn_client_server_stats_max_memory=dict(required=False, type='str'), - pn_connection_stats_log_disk_space=dict(required=False, - type='str'), - pn_client_server_stats_log_enable=dict(required=False, - type='bool'), - pn_service_stat_max_memory=dict(required=False, type='str'), - pn_connection_stats_log_interval=dict(required=False, type='str'), - pn_fabric_connection_backup_interval=dict(required=False, - type='str'), - pn_connection_backup_interval=dict(required=False, type='str'), - pn_connection_stats_log_enable=dict(required=False, type='bool'), - pn_fabric_connection_max_memory=dict(required=False, type='str'), - pn_fabric_connection_backup_enable=dict(required=False, - type='bool'), - pn_client_server_stats_log_disk_space=dict(required=False, - type='str'), - pn_connection_max_memory=dict(required=False, type='str'), - pn_connection_stats_max_memory=dict(required=False, type='str'), - pn_client_server_stats_log_interval=dict(required=False, - type='str'), - ), - required_one_of=[['pn_enable', 'pn_connection_backup_enable', - 'pn_client_server_stats_max_memory', - 'pn_connection_stats_log_disk_space', - 'pn_client_server_stats_log_enable', - 'pn_service_stat_max_memory', - 'pn_connection_stats_log_interval', - 'pn_connection_backup_interval', - 'pn_connection_stats_log_enable', - 'pn_fabric_connection_max_memory', - 'pn_fabric_connection_backup_enable', - 'pn_client_server_stats_log_disk_space', - 'pn_connection_max_memory', - 'pn_connection_stats_max_memory', - 'pn_client_server_stats_log_interval']] - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - enable = module.params['pn_enable'] - connection_backup_enable = module.params['pn_connection_backup_enable'] - client_server_stats_max_memory = module.params['pn_client_server_stats_max_memory'] - connection_stats_log_disk_space = module.params['pn_connection_stats_log_disk_space'] - client_server_stats_log_enable = module.params['pn_client_server_stats_log_enable'] - service_stat_max_memory = module.params['pn_service_stat_max_memory'] - connection_stats_log_interval = module.params['pn_connection_stats_log_interval'] - fabric_connection_backup_interval = module.params['pn_fabric_connection_backup_interval'] - connection_backup_interval = module.params['pn_connection_backup_interval'] - connection_stats_log_enable = module.params['pn_connection_stats_log_enable'] - fabric_connection_max_memory = module.params['pn_fabric_connection_max_memory'] - fabric_connection_backup_enable = module.params['pn_fabric_connection_backup_enable'] - client_server_stats_log_disk_space = module.params['pn_client_server_stats_log_disk_space'] - connection_max_memory = module.params['pn_connection_max_memory'] - connection_stats_max_memory = module.params['pn_connection_stats_max_memory'] - client_server_stats_log_interval = module.params['pn_client_server_stats_log_interval'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'connection-stats-settings-modify': - cli += ' %s ' % command - - cli += booleanArgs(enable, 'enable', 'disable') - cli += booleanArgs(connection_backup_enable, 'connection-backup-enable', 'connection-backup-disable') - cli += booleanArgs(client_server_stats_log_enable, 'client-server-stats-log-enable', 'client-server-stats-log-disable') - cli += booleanArgs(connection_stats_log_enable, 'connection-stats-log-enable', 'connection-stats-log-disable') - cli += booleanArgs(fabric_connection_backup_enable, 'fabric-connection-backup-enable', 'fabric-connection-backup-disable') - - if client_server_stats_max_memory: - cli += ' client-server-stats-max-memory ' + client_server_stats_max_memory - if connection_stats_log_disk_space: - cli += ' connection-stats-log-disk-space ' + connection_stats_log_disk_space - if service_stat_max_memory: - cli += ' service-stat-max-memory ' + service_stat_max_memory - if connection_stats_log_interval: - cli += ' connection-stats-log-interval ' + connection_stats_log_interval - if fabric_connection_backup_interval: - cli += ' fabric-connection-backup-interval ' + fabric_connection_backup_interval - if connection_backup_interval: - cli += ' connection-backup-interval ' + connection_backup_interval - if fabric_connection_max_memory: - cli += ' fabric-connection-max-memory ' + fabric_connection_max_memory - if client_server_stats_log_disk_space: - cli += ' client-server-stats-log-disk-space ' + client_server_stats_log_disk_space - if connection_max_memory: - cli += ' connection-max-memory ' + connection_max_memory - if connection_stats_max_memory: - cli += ' connection-stats-max-memory ' + connection_stats_max_memory - if client_server_stats_log_interval: - cli += ' client-server-stats-log-interval ' + client_server_stats_log_interval - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_cpu_class.py b/plugins/modules/network/netvisor/pn_cpu_class.py deleted file mode 100644 index fadbed03e4..0000000000 --- a/plugins/modules/network/netvisor/pn_cpu_class.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_cpu_class -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/modify/delete cpu-class -description: - - This module can be used to create, modify and delete CPU class information. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(present) to create cpu-class and - C(absent) to delete cpu-class C(update) to modify the cpu-class. - required: True - type: str - choices: ['present', 'absent', 'update'] - pn_scope: - description: - - scope for CPU class. - required: false - choices: ['local', 'fabric'] - pn_hog_protect: - description: - - enable host-based hog protection. - required: False - type: str - choices: ['disable', 'enable', 'enable-and-drop'] - pn_rate_limit: - description: - - rate-limit for CPU class. - required: False - type: str - pn_name: - description: - - name for the CPU class. - required: False - type: str -''' - -EXAMPLES = """ -- name: create cpu class - pn_cpu_class: - pn_cliswitch: 'sw01' - state: 'present' - pn_name: 'icmp' - pn_rate_limit: '1000' - pn_scope: 'local' - -- name: delete cpu class - pn_cpu_class: - pn_cliswitch: 'sw01' - state: 'absent' - pn_name: 'icmp' - - -- name: modify cpu class - pn_cpu_class: - pn_cliswitch: 'sw01' - state: 'update' - pn_name: 'icmp' - pn_rate_limit: '2000' -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the cpu-class command. - returned: always - type: list -stderr: - description: set of error responses from the cpu-class command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the cpu-class-show command. - If a user with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - clicopy = cli - - cli += ' system-settings-show format cpu-class-enable no-show-headers' - out = run_commands(module, cli)[1] - out = out.split() - - if 'on' not in out: - module.fail_json( - failed=True, - msg='Enable CPU class before creating or deleting' - ) - - cli = clicopy - cli += ' cpu-class-show format name no-show-headers' - out = run_commands(module, cli)[1] - if out: - out = out.split() - - return True if name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='cpu-class-create', - absent='cpu-class-delete', - update='cpu-class-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_scope=dict(required=False, type='str', - choices=['local', 'fabric']), - pn_hog_protect=dict(required=False, type='str', - choices=['disable', 'enable', - 'enable-and-drop']), - pn_rate_limit=dict(required=False, type='str'), - pn_name=dict(required=False, type='str'), - ), - required_if=( - ['state', 'present', ['pn_name', 'pn_scope', 'pn_rate_limit']], - ['state', 'absent', ['pn_name']], - ['state', 'update', ['pn_name']], - ) - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - scope = module.params['pn_scope'] - hog_protect = module.params['pn_hog_protect'] - rate_limit = module.params['pn_rate_limit'] - name = module.params['pn_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NAME_EXISTS = check_cli(module, cli) - cli += ' %s name %s ' % (command, name) - - if command == 'cpu-class-modify': - if NAME_EXISTS is False: - module.fail_json( - failed=True, - msg='cpu class with name %s does not exist' % name - ) - - if command == 'cpu-class-delete': - if NAME_EXISTS is False: - module.exit_json( - skipped=True, - msg='cpu class with name %s does not exist' % name - ) - - if command == 'cpu-class-create': - if NAME_EXISTS is True: - module.exit_json( - skipped=True, - msg='cpu class with name %s already exists' % name - ) - if scope: - cli += ' scope %s ' % scope - - if command != 'cpu-class-delete': - if hog_protect: - cli += ' hog-protect %s ' % hog_protect - if rate_limit: - cli += ' rate-limit %s ' % rate_limit - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_cpu_mgmt_class.py b/plugins/modules/network/netvisor/pn_cpu_mgmt_class.py deleted file mode 100644 index 891c0a6cf0..0000000000 --- a/plugins/modules/network/netvisor/pn_cpu_mgmt_class.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_cpu_mgmt_class -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify cpu-mgmt-class -description: - - This module can we used to update mgmt port ingress policers. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - type: str - required: false - state: - description: - - State the action to perform. Use C(update) to modify cpu-mgmt-class. - type: str - required: true - choices: ['update'] - pn_burst_size: - description: - - ingress traffic burst size (bytes) or default. - required: false - type: str - pn_name: - description: - - mgmt port ingress traffic class. - type: str - required: false - choices: ['arp', 'icmp', 'ssh', 'snmp', 'fabric', 'bcast', 'nfs', - 'web', 'web-ssl', 'net-api'] - pn_rate_limit: - description: - - ingress rate limit on mgmt port(bps) or unlimited. - type: str - required: false -''' - -EXAMPLES = """ -- name: cpu mgmt class modify ingress policers - pn_cpu_mgmt_class: - pn_cliswitch: "sw01" - state: "update" - pn_name: "icmp" - pn_rate_limit: "10000" - pn_burst_size: "14000" - -- name: cpu mgmt class modify ingress policers - pn_cpu_mgmt_class: - pn_cliswitch: "sw01" - state: "update" - pn_name: "snmp" - pn_burst_size: "8000" - pn_rate_limit: "100000" - -- name: cpu mgmt class modify ingress policers - pn_cpu_mgmt_class: - pn_cliswitch: "sw01" - state: "update" - pn_name: "web" - pn_rate_limit: "10000" - pn_burst_size: "1000" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the cpu-mgmt-class command. - returned: always - type: list -stderr: - description: set of error responses from the cpu-mgmt-class command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='cpu-mgmt-class-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', choices=state_map.keys()), - pn_burst_size=dict(required=False, type='str'), - pn_name=dict(required=False, type='str', - choices=['arp', 'icmp', 'ssh', 'snmp', - 'fabric', 'bcast', 'nfs', 'web', - 'web-ssl', 'net-api']), - pn_rate_limit=dict(required=False, type='str'), - ), - required_if=([['state', 'update', ['pn_name', 'pn_burst_size', 'pn_rate_limit']]]), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - burst_size = module.params['pn_burst_size'] - name = module.params['pn_name'] - rate_limit = module.params['pn_rate_limit'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'cpu-mgmt-class-modify': - cli += ' %s name %s ' % (command, name) - cli += ' burst-size %s rate-limit %s' % (burst_size, rate_limit) - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_dhcp_filter.py b/plugins/modules/network/netvisor/pn_dhcp_filter.py deleted file mode 100644 index e331720401..0000000000 --- a/plugins/modules/network/netvisor/pn_dhcp_filter.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_dhcp_filter -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/modify/delete dhcp-filter -description: - - This module can be used to create, delete and modify a DHCP filter config. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(present) to create dhcp-filter and - C(absent) to delete dhcp-filter C(update) to modify the dhcp-filter. - required: True - type: str - choices: ['present', 'absent', 'update'] - pn_trusted_ports: - description: - - trusted ports of dhcp config. - required: False - type: str - pn_name: - description: - - name of the DHCP filter. - required: false - type: str -''' - -EXAMPLES = """ -- name: dhcp filter create - pn_dhcp_filter: - pn_cliswitch: "sw01" - pn_name: "foo" - state: "present" - pn_trusted_ports: "1" - -- name: dhcp filter delete - pn_dhcp_filter: - pn_cliswitch: "sw01" - pn_name: "foo" - state: "absent" - pn_trusted_ports: "1" - -- name: dhcp filter modify - pn_dhcp_filter: - pn_cliswitch: "sw01" - pn_name: "foo" - state: "update" - pn_trusted_ports: "1,2" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the dhcp-filter command. - returned: always - type: list -stderr: - description: set of error responses from the dhcp-filter command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the dhcp-filter-show command. - If a user with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - user_name = module.params['pn_name'] - - cli += ' dhcp-filter-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if user_name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='dhcp-filter-create', - absent='dhcp-filter-delete', - update='dhcp-filter-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_trusted_ports=dict(required=False, type='str'), - pn_name=dict(required=False, type='str'), - ), - required_if=[ - ["state", "present", ["pn_name", "pn_trusted_ports"]], - ["state", "absent", ["pn_name"]], - ["state", "update", ["pn_name", "pn_trusted_ports"]] - ] - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - trusted_ports = module.params['pn_trusted_ports'] - name = module.params['pn_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - USER_EXISTS = check_cli(module, cli) - cli += ' %s name %s ' % (command, name) - - if command == 'dhcp-filter-modify': - if USER_EXISTS is False: - module.fail_json( - failed=True, - msg='dhcp-filter with name %s does not exist' % name - ) - if command == 'dhcp-filter-delete': - if USER_EXISTS is False: - module.exit_json( - skipped=True, - msg='dhcp-filter with name %s does not exist' % name - ) - if command == 'dhcp-filter-create': - if USER_EXISTS is True: - module.exit_json( - skipped=True, - msg='dhcp-filter with name %s already exists' % name - ) - if command != 'dhcp-filter-delete': - if trusted_ports: - cli += ' trusted-ports ' + trusted_ports - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_dscp_map.py b/plugins/modules/network/netvisor/pn_dscp_map.py deleted file mode 100644 index dc86fdee21..0000000000 --- a/plugins/modules/network/netvisor/pn_dscp_map.py +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_dscp_map -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/delete dscp-map -description: - - This module can be used to create a DSCP priority mapping table. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(present) to create dscp-map and - C(absent) to delete. - required: True - type: str - choices: ["present", "absent"] - pn_name: - description: - - Name for the DSCP map. - required: False - type: str - pn_scope: - description: - - Scope for dscp map. - required: False - choices: ["local", "fabric"] -''' - -EXAMPLES = """ -- name: dscp map create - pn_dscp_map: - pn_cliswitch: "sw01" - state: "present" - pn_name: "foo" - pn_scope: "local" - -- name: dscp map delete - pn_dscp_map: - pn_cliswitch: "sw01" - state: "absent" - pn_name: "foo" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the dscp-map command. - returned: always - type: list -stderr: - description: set of error responses from the dscp-map command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the dscp-map-show name command. - If a user with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - - cli += ' dscp-map-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='dscp-map-create', - absent='dscp-map-delete' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_name=dict(required=False, type='str'), - pn_scope=dict(required=False, type='str', - choices=['local', 'fabric']), - ), - required_if=( - ["state", "present", ["pn_name", "pn_scope"]], - ["state", "absent", ["pn_name"]], - ) - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - name = module.params['pn_name'] - scope = module.params['pn_scope'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NAME_EXISTS = check_cli(module, cli) - cli += ' %s name %s ' % (command, name) - - if command == 'dscp-map-delete': - if NAME_EXISTS is False: - module.exit_json( - skipped=True, - msg='dscp map with name %s does not exist' % name - ) - else: - if command == 'dscp-map-create': - if NAME_EXISTS is True: - module.exit_json( - skipped=True, - msg='dscp map with name %s already exists' % name - ) - - if scope: - cli += ' scope ' + scope - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_dscp_map_pri_map.py b/plugins/modules/network/netvisor/pn_dscp_map_pri_map.py deleted file mode 100644 index 30fd6f1060..0000000000 --- a/plugins/modules/network/netvisor/pn_dscp_map_pri_map.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_dscp_map_pri_map -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify dscp-map-pri-map -description: - - This module can be used to update priority mappings in tables. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(update) to modify - the dscp-map-pri-map. - required: True - type: str - choices: ['update'] - pn_pri: - description: - - CoS priority. - required: False - type: str - pn_name: - description: - - Name for the DSCP map. - required: False - type: str - pn_dsmap: - description: - - DSCP value(s). - required: False - type: str -''' - -EXAMPLES = """ -- name: dscp map pri map modify - pn_dscp_map_pri_map: - pn_cliswitch: 'sw01' - state: 'update' - pn_name: 'foo' - pn_pri: '0' - pn_dsmap: '40' - -- name: dscp map pri map modify - pn_dscp_map_pri_map: - pn_cliswitch: 'sw01' - state: 'update' - pn_name: 'foo' - pn_pri: '1' - pn_dsmap: '8,10,12,14' -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the dscp-map-pri-map command. - returned: always - type: list -stderr: - description: set of error responses from the dscp-map-pri-map command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the dscp-map-show name command. - If a user with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - - cli += ' dscp-map-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='dscp-map-pri-map-modify' - ) - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_pri=dict(required=False, type='str'), - pn_name=dict(required=False, type='str'), - pn_dsmap=dict(required=False, type='str'), - ), - required_if=( - ['state', 'update', ['pn_name', 'pn_pri']], - ) - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - pri = module.params['pn_pri'] - name = module.params['pn_name'] - dsmap = module.params['pn_dsmap'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NAME_EXISTS = check_cli(module, cli) - - if command == 'dscp-map-pri-map-modify': - if NAME_EXISTS is False: - module.fail_json( - failed=True, - msg='Create dscp map with name %s before updating' % name - ) - cli += ' %s ' % command - if pri: - cli += ' pri ' + pri - if name: - cli += ' name ' + name - if dsmap: - cli += ' dsmap ' + dsmap - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_fabric_local.py b/plugins/modules/network/netvisor/pn_fabric_local.py deleted file mode 100644 index d8a6451232..0000000000 --- a/plugins/modules/network/netvisor/pn_fabric_local.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_fabric_local -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify fabric-local -description: - - This module can be used to modify fabric local information. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: true - type: str - state: - description: - - State the action to perform. Use C(update) to modify the fabric-local. - required: false - type: str - choices: ['update'] - default: 'update' - pn_fabric_network: - description: - - fabric administration network. - required: false - choices: ['in-band', 'mgmt', 'vmgmt'] - default: 'mgmt' - pn_vlan: - description: - - VLAN assigned to fabric. - required: false - type: str - pn_control_network: - description: - - control plane network. - required: false - choices: ['in-band', 'mgmt', 'vmgmt'] - pn_fabric_advertisement_network: - description: - - network to send fabric advertisements on. - required: false - choices: ['inband-mgmt', 'inband-only', 'inband-vmgmt', 'mgmt-only'] -''' - -EXAMPLES = """ -- name: Fabric local module - pn_fabric_local: - pn_cliswitch: "sw01" - pn_vlan: "500" - -- name: Fabric local module - pn_fabric_local: - pn_cliswitch: "sw01" - pn_fabric_advertisement_network: "mgmt-only" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the fabric-local command. - returned: always - type: list -stderr: - description: set of error responses from the fabric-local command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='fabric-local-modify' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=True, type='str'), - state=dict(required=False, type='str', choices=state_map.keys(), default='update'), - pn_fabric_network=dict(required=False, type='str', - choices=['mgmt', 'in-band', 'vmgmt'], default='mgmt'), - pn_vlan=dict(required=False, type='str'), - pn_control_network=dict(required=False, type='str', - choices=['in-band', 'mgmt', 'vmgmt']), - pn_fabric_advertisement_network=dict(required=False, type='str', - choices=['inband-mgmt', 'inband-only', 'inband-vmgmt', 'mgmt-only']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=[['pn_fabric_network', 'pn_vlan', - 'pn_control_network', - 'pn_fabric_advertisement_network']], - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - fabric_network = module.params['pn_fabric_network'] - vlan = module.params['pn_vlan'] - control_network = module.params['pn_control_network'] - fabric_adv_network = module.params['pn_fabric_advertisement_network'] - - command = state_map[state] - - if vlan: - if int(vlan) < 1 or int(vlan) > 4092: - module.fail_json( - failed=True, - msg='Valid vlan range is 1 to 4092' - ) - cli = pn_cli(module, cliswitch) - cli += ' vlan-show format id no-show-headers' - out = run_commands(module, cli)[1].split() - - if vlan in out and vlan != '1': - module.fail_json( - failed=True, - msg='vlan %s is already in used. Specify unused vlan' % vlan - ) - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'fabric-local-modify': - cli += ' %s ' % command - - if fabric_network: - cli += ' fabric-network ' + fabric_network - - if vlan: - cli += ' vlan ' + vlan - - if control_network: - cli += ' control-network ' + control_network - - if fabric_adv_network: - cli += ' fabric-advertisement-network ' + fabric_adv_network - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_igmp_snooping.py b/plugins/modules/network/netvisor/pn_igmp_snooping.py deleted file mode 100644 index 0f93ed08a3..0000000000 --- a/plugins/modules/network/netvisor/pn_igmp_snooping.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_igmp_snooping -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify igmp-snooping -description: - - This module can be used to modify Internet Group Management Protocol (IGMP) snooping. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(update) to modify the igmp-snooping. - required: True - type: str - choices: ['update'] - pn_enable: - description: - - enable or disable IGMP snooping. - required: False - type: bool - pn_query_interval: - description: - - IGMP query interval in seconds. - required: False - type: str - pn_igmpv2_vlans: - description: - - VLANs on which to use IGMPv2 protocol. - required: False - type: str - pn_igmpv3_vlans: - description: - - VLANs on which to use IGMPv3 protocol. - required: False - type: str - pn_enable_vlans: - description: - - enable per VLAN IGMP snooping. - required: False - type: str - pn_vxlan: - description: - - enable or disable IGMP snooping on vxlans. - required: False - type: bool - pn_query_max_response_time: - description: - - maximum response time, in seconds, advertised in IGMP queries. - required: False - type: str - pn_scope: - description: - - IGMP snooping scope - fabric or local. - required: False - choices: ['local', 'fabric'] - pn_no_snoop_linklocal_vlans: - description: - - Remove snooping of link-local groups(224.0.0.0/24) on these vlans. - required: False - type: str - pn_snoop_linklocal_vlans: - description: - - Allow snooping of link-local groups(224.0.0.0/24) on these vlans. - required: False - type: str -''' - -EXAMPLES = """ -- name: 'Modify IGMP Snooping' - pn_igmp_snooping: - pn_cliswitch: 'sw01' - state: 'update' - pn_vxlan: True - pn_enable_vlans: '1-399,401-4092' - pn_no_snoop_linklocal_vlans: 'none' - pn_igmpv3_vlans: '1-399,401-4092' - -- name: 'Modify IGMP Snooping' - pn_igmp_snooping: - pn_cliswitch: 'sw01' - state: 'update' - pn_vxlan: False - pn_enable_vlans: '1-399' - pn_no_snoop_linklocal_vlans: 'none' - pn_igmpv3_vlans: '1-399' -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the igmp-snooping command. - returned: always - type: list -stderr: - description: set of error responses from the igmp-snooping command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='igmp-snooping-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_enable=dict(required=False, type='bool'), - pn_query_interval=dict(required=False, type='str'), - pn_igmpv2_vlans=dict(required=False, type='str'), - pn_igmpv3_vlans=dict(required=False, type='str'), - pn_enable_vlans=dict(required=False, type='str'), - pn_vxlan=dict(required=False, type='bool'), - pn_query_max_response_time=dict(required=False, type='str'), - pn_scope=dict(required=False, type='str', - choices=['local', 'fabric']), - pn_no_snoop_linklocal_vlans=dict(required=False, type='str'), - pn_snoop_linklocal_vlans=dict(required=False, type='str'), - ), - required_one_of=[['pn_enable', 'pn_query_interval', - 'pn_igmpv2_vlans', - 'pn_igmpv3_vlans', - 'pn_enable_vlans', - 'pn_vxlan', - 'pn_query_max_response_time', - 'pn_scope', - 'pn_no_snoop_linklocal_vlans', - 'pn_snoop_linklocal_vlans']] - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - enable = module.params['pn_enable'] - query_interval = module.params['pn_query_interval'] - igmpv2_vlans = module.params['pn_igmpv2_vlans'] - igmpv3_vlans = module.params['pn_igmpv3_vlans'] - enable_vlans = module.params['pn_enable_vlans'] - vxlan = module.params['pn_vxlan'] - query_max_response_time = module.params['pn_query_max_response_time'] - scope = module.params['pn_scope'] - no_snoop_linklocal_vlans = module.params['pn_no_snoop_linklocal_vlans'] - snoop_linklocal_vlans = module.params['pn_snoop_linklocal_vlans'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'igmp-snooping-modify': - cli += ' %s ' % command - - cli += booleanArgs(enable, 'enable', 'disable') - cli += booleanArgs(vxlan, 'vxlan', 'no-vxlan') - - if query_interval: - cli += ' query-interval ' + query_interval - if igmpv2_vlans: - cli += ' igmpv2-vlans ' + igmpv2_vlans - if igmpv3_vlans: - cli += ' igmpv3-vlans ' + igmpv3_vlans - if enable_vlans: - cli += ' enable-vlans ' + enable_vlans - if query_max_response_time: - cli += ' query-max-response-time ' + query_max_response_time - if scope: - cli += ' scope ' + scope - if no_snoop_linklocal_vlans: - cli += ' no-snoop-linklocal-vlans ' + no_snoop_linklocal_vlans - if snoop_linklocal_vlans: - cli += ' snoop-linklocal-vlans ' + snoop_linklocal_vlans - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_ipv6security_raguard.py b/plugins/modules/network/netvisor/pn_ipv6security_raguard.py deleted file mode 100644 index 32b68113fe..0000000000 --- a/plugins/modules/network/netvisor/pn_ipv6security_raguard.py +++ /dev/null @@ -1,238 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_ipv6security_raguard -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/modify/delete ipv6security-raguard -description: - - This module can be used to add ipv6 RA Guard Policy, Update ipv6 RA guard Policy and Remove ipv6 RA Guard Policy. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - ipv6security-raguard configuration command. - required: false - choices: ['present', 'update', 'absent'] - type: str - default: 'present' - pn_device: - description: - - RA Guard Device. host or router. - required: false - choices: ['host', 'router'] - type: str - pn_access_list: - description: - - RA Guard Access List of Source IPs. - required: false - type: str - pn_prefix_list: - description: - - RA Guard Prefix List. - required: false - type: str - pn_router_priority: - description: - - RA Guard Router Priority. - required: false - type: str - choices: ['low', 'medium', 'high'] - pn_name: - description: - - RA Guard Policy Name. - required: true - type: str -''' - -EXAMPLES = """ -- name: ipv6 security ragurad create - pn_ipv6security_raguard: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_device: "host" - -- name: ipv6 security ragurad create - pn_ipv6security_raguard: - pn_cliswitch: "sw01" - pn_name: "foo1" - pn_device: "host" - pn_access_list: "sample" - pn_prefix_list: "sample" - pn_router_priority: "low" - -- name: ipv6 security ragurad modify - pn_ipv6security_raguard: - pn_cliswitch: "sw01" - pn_name: "foo1" - pn_device: "router" - pn_router_priority: "medium" - state: "update" - -- name: ipv6 security ragurad delete - pn_ipv6security_raguard: - pn_cliswitch: "sw01" - pn_name: "foo" - state: "absent" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the ipv6security-raguard command. - returned: always - type: list -stderr: - description: set of error responses from the ipv6security-raguard command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module): - """ - This method checks for idempotency using the ipv6security-raguard-show command. - If a name exists, return True if name exists else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - - cli = 'ipv6security-raguard-show format name parsable-delim ,' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if name in out else False - - -def check_list(module, list_name, command): - """ - This method checks for idempotency using provided command. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - - cli = '%s format name no-show-headers' % command - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - if list_name not in out: - module.fail_json( - failed=True, - msg='%s name %s does not exists' % (command, list_name) - ) - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='ipv6security-raguard-create', - absent='ipv6security-raguard-delete', - update='ipv6security-raguard-modify' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', choices=state_map.keys(), default='present'), - pn_device=dict(required=False, type='str', choices=['host', 'router']), - pn_access_list=dict(required=False, type='str'), - pn_prefix_list=dict(required=False, type='str'), - pn_router_priority=dict(required=False, type='str', choices=['low', 'medium', 'high']), - pn_name=dict(required=True, type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=( - ["state", "present", ['pn_device']], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - device = module.params['pn_device'] - access_list = module.params['pn_access_list'] - prefix_list = module.params['pn_prefix_list'] - router_priority = module.params['pn_router_priority'] - name = module.params['pn_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NAME_EXISTS = check_cli(module) - - if command == 'ipv6security-raguard-modify': - if not device and not access_list and not prefix_list and not router_priority: - module.fail_json( - failed=True, - msg='required one of device, access_list, prefix_list or router_priority' - ) - - if command == 'ipv6security-raguard-create': - if NAME_EXISTS is True: - module.exit_json( - skipped=True, - msg='ipv6 security raguard with name %s already exists' % name - ) - - if command != 'ipv6security-raguard-create': - if NAME_EXISTS is False: - module.exit_json( - skipped=True, - msg='ipv6 security raguard with name %s does not exist' % name - ) - - cli += ' %s name %s ' % (command, name) - if command != 'ipv6security-raguard-delete': - if device == 'router': - cli += ' device ' + device - if access_list: - check_list(module, access_list, 'access-list-show') - cli += ' access-list ' + access_list - if prefix_list: - check_list(module, prefix_list, 'prefix-list-show') - cli += ' prefix-list ' + prefix_list - if router_priority: - cli += ' router-priority ' + router_priority - if device == 'host': - cli += ' device ' + device - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_ipv6security_raguard_port.py b/plugins/modules/network/netvisor/pn_ipv6security_raguard_port.py deleted file mode 100644 index 15f82306ef..0000000000 --- a/plugins/modules/network/netvisor/pn_ipv6security_raguard_port.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_ipv6security_raguard_port -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove ipv6security-raguard-port -description: - - This module can be used to add ports to RA Guard Policy and remove ports to RA Guard Policy. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - ipv6security-raguard-port configuration command. - required: false - type: str - choices: ['present', 'absent'] - default: 'present' - pn_name: - description: - - RA Guard Policy Name. - required: true - type: str - pn_ports: - description: - - Ports attached to RA Guard Policy. - required: true - type: str -''' - -EXAMPLES = """ -- name: ipv6 security raguard port add - pn_ipv6security_raguard_port: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_ports: "1" - -- name: ipv6 security raguard port remove - pn_ipv6security_raguard_port: - pn_cliswitch: "sw01" - pn_name: "foo" - state: "absent" - pn_ports: "1" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the ipv6security-raguard-port command. - returned: always - type: list -stderr: - description: set of error responses from the ipv6security-raguard-port command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module): - """ - This method checks for idempotency using the ipv6security-raguard-show command. - If a name exists, return True if name exists else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - - cli = 'ipv6security-raguard-show format name parsable-delim ,' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='ipv6security-raguard-port-add', - absent='ipv6security-raguard-port-remove' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', choices=state_map.keys(), default='present'), - pn_name=dict(required=True, type='str'), - pn_ports=dict(required=True, type='str') - ) - - module = AnsibleModule( - argument_spec=argument_spec - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - name = module.params['pn_name'] - ports = module.params['pn_ports'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NAME_EXISTS = check_cli(module) - - if command: - if NAME_EXISTS is False: - module.fail_json( - failed=True, - msg='ipv6 security raguard with name %s does not exist to add ports' % name - ) - - cli += ' %s name %s ports %s' % (command, name, ports) - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_ipv6security_raguard_vlan.py b/plugins/modules/network/netvisor/pn_ipv6security_raguard_vlan.py deleted file mode 100644 index 0f310d0c43..0000000000 --- a/plugins/modules/network/netvisor/pn_ipv6security_raguard_vlan.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_ipv6security_raguard_vlan -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove ipv6security-raguard-vlan -description: - - This module can be used to Add vlans to RA Guard Policy and Remove vlans to RA Guard Policy. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - ipv6security-raguard-vlan configuration command. - required: false - type: str - choices: ['present', 'absent'] - default: 'present' - pn_vlans: - description: - - Vlans attached to RA Guard Policy. - required: true - type: str - pn_name: - description: - - RA Guard Policy Name. - required: true - type: str -''' - -EXAMPLES = """ -- name: ipv6 security raguard vlan add - pn_ipv6security_raguard_vlan: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_vlans: "100-105" - -- name: ipv6 security raguard vlan add - pn_ipv6security_raguard_vlan: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_vlans: "100" - -- name: ipv6 security raguard vlan remove - pn_ipv6security_raguard_vlan: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_vlans: "100-105" - state: 'absent' -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the ipv6security-raguard-vlan command. - returned: always - type: list -stderr: - description: set of error responses from the ipv6security-raguard-vlan command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the ipv6-security-reguard command. - If a name exists, return True if name exists else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - vlans = module.params['pn_vlans'] - show = cli - - cli += ' ipv6security-raguard-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - NAME_EXISTS = True if name in out else False - - show += ' vlan-show format id no-show-headers' - out = run_commands(module, show)[1] - if out: - out = out.split() - - if vlans and '-' in vlans: - vlan_list = list() - vlans = vlans.strip().split('-') - for vlan in range(int(vlans[0]), int(vlans[1]) + 1): - vlan_list.append(str(vlan)) - - for vlan in vlan_list: - if vlan not in out: - module.fail_json( - failed=True, - msg='vlan id %s does not exist. Make sure you create vlan before adding it' % vlan - ) - else: - if vlans not in out: - module.fail_json( - failed=True, - msg='vlan id %s does not exist. Make sure you create vlan before adding it' % vlans - ) - - return NAME_EXISTS - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='ipv6security-raguard-vlan-add', - absent='ipv6security-raguard-vlan-remove' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', choices=state_map.keys(), default='present'), - pn_vlans=dict(required=True, type='str'), - pn_name=dict(required=True, type='str'), - ) - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - vlans = module.params['pn_vlans'] - name = module.params['pn_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NAME_EXISTS = check_cli(module, cli) - - cli += ' %s name %s ' % (command, name) - - if command: - if NAME_EXISTS is False: - module.exit_json( - skipped=True, - msg='ipv6security raguard with name %s does not exist' % name - ) - if vlans: - cli += ' vlans ' + vlans - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_log_audit_exception.py b/plugins/modules/network/netvisor/pn_log_audit_exception.py deleted file mode 100644 index e4cd2ff666..0000000000 --- a/plugins/modules/network/netvisor/pn_log_audit_exception.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/license/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: pn_log_audit_exception -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/delete an audit exception -description: - - This module can be used to create an audit exception and delete an audit exception. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - pn_audit_type: - description: - - Specify the type of audit exception. - required: false - type: str - choices: ['cli', 'shell', 'vtysh'] - state: - description: - - State the action to perform. Use 'present' to create audit-exception and - 'absent' to delete audit-exception. - required: false - type: str - choices: ['present', 'absent'] - default: 'present' - pn_pattern: - description: - - Specify a regular expression to match exceptions. - required: false - type: str - pn_scope: - description: - - scope - local or fabric. - required: false - type: str - choices: ['local', 'fabric'] - pn_access: - description: - - Specify the access type to match exceptions. - required: true - type: str - choices: ['any', 'read-only', 'read-write'] -''' - -EXAMPLES = """ -- name: create a log-audit-exception - pn_log_audit_exception: - pn_audit_type: "cli" - pn_pattern: "test" - state: "present" - pn_access: "any" - pn_scope: "local" - -- name: delete a log-audit-exception - pn_log_audit_exception: - pn_audit_type: "shell" - pn_pattern: "test" - state: "absent" - pn_access: "any" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the pn_log_audit_exceptions command. - returned: always - type: list -stderr: - description: set of error responses from the log_audit_exceptions command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the log-audit-exception command. - If a list with given name exists, return exists as True else False. - :param module: The Ansible module to fetch input parameters. - :return Booleans: True or False. - """ - state = module.params['state'] - audit_type = module.params['pn_audit_type'] - pattern = module.params['pn_pattern'] - access = module.params['pn_access'] - scope = module.params['pn_scope'] - cli += ' log-audit-exception-show' - cli += ' no-show-headers format ' - cli += ' type,pattern,access,scope parsable-delim DELIM' - - stdout = run_commands(module, cli)[1] - - if stdout: - linelist = stdout.strip().split('\n') - for line in linelist: - wordlist = line.split('DELIM') - count = 0 - - if wordlist[0] == audit_type: - count += 1 - if wordlist[1] == pattern: - count += 1 - if wordlist[2] == access: - count += 1 - if state == 'present' and wordlist[3] == scope: - count += 1 - elif state == 'absent' and count == 3: - return True - if state == 'present' and count == 4: - return True - - return False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='log-audit-exception-create', - absent='log-audit-exception-delete', - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - pn_pattern=dict(required=True, type='str'), - state=dict(required=False, type='str', - choices=state_map.keys(), default='present'), - pn_access=dict(required=True, type='str', choices=['any', 'read-only', 'read-write']), - pn_audit_type=dict(required=True, type='str', choices=['cli', 'shell', 'vtysh']), - pn_scope=dict(required=False, type='str', choices=['local', 'fabric']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=( - ["state", "present", ["pn_scope"]], - ), - ) - - # Accessing the arguments - - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - access = module.params['pn_access'] - audit_type = module.params['pn_audit_type'] - pattern = module.params['pn_pattern'] - scope = module.params['pn_scope'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - audit_log_exists = check_cli(module, cli) - - cli += ' %s %s pattern %s %s' % (command, audit_type, pattern, access) - - if state == 'absent': - if audit_log_exists is False: - module.exit_json( - skipped=True, - msg='This audit log exception entry does not exist' - ) - run_cli(module, cli, state_map) - - elif state == 'present': - if audit_log_exists is True: - module.exit_json( - skipped=True, - msg='This audit log exception entry already exists' - ) - cli += ' scope %s ' % scope - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_ospf.py b/plugins/modules/network/netvisor/pn_ospf.py deleted file mode 100644 index b388198174..0000000000 --- a/plugins/modules/network/netvisor/pn_ospf.py +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/python -""" PN-CLI vrouter-ospf-add/remove """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_ospf -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to add/remove ospf protocol to a vRouter. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute vrouter-ospf-add, vrouter-ospf-remove command. - - This command adds/removes Open Shortest Path First(OSPF) routing - protocol to a virtual router(vRouter) service. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - default: 'local' - state: - description: - - Assert the state of the ospf. Use 'present' to add ospf - and 'absent' to remove ospf. - required: True - default: present - choices: ['present', 'absent'] - pn_vrouter_name: - description: - - Specify the name of the vRouter. - required: True - pn_network_ip: - description: - - Specify the network IP (IPv4 or IPv6) address. - required: True - pn_ospf_area: - description: - - Stub area number for the configuration. Required for vrouter-ospf-add. -''' - -EXAMPLES = """ -- name: "Add OSPF to vrouter" - pn_ospf: - state: present - pn_vrouter_name: name-string - pn_network_ip: 192.168.11.2/24 - pn_ospf_area: 1.0.0.0 - -- name: "Remove OSPF from vrouter" - pn_ospf: - state: absent - pn_vrouter_name: name-string -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the ospf command. - returned: always - type: list -stderr: - description: The set of error responses from the ospf command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# AnsibleModule boilerplate -from ansible.module_utils.basic import AnsibleModule - -VROUTER_EXISTS = None -NETWORK_EXISTS = None - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the vrouter-ospf-show command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - If an OSPF network with the given ip exists on the given vRouter, - return NETWORK_EXISTS as True else False. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: VROUTER_EXISTS, NETWORK_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - network_ip = module.params['pn_network_ip'] - # Global flags - global VROUTER_EXISTS, NETWORK_EXISTS - - # Check for vRouter - check_vrouter = cli + ' vrouter-show format name no-show-headers ' - check_vrouter = shlex.split(check_vrouter) - out = module.run_command(check_vrouter)[1] - out = out.split() - - if vrouter_name in out: - VROUTER_EXISTS = True - else: - VROUTER_EXISTS = False - - # Check for OSPF networks - show = cli + ' vrouter-ospf-show vrouter-name %s ' % vrouter_name - show += 'format network no-show-headers' - show = shlex.split(show) - out = module.run_command(show)[1] - out = out.split() - - if network_ip in out: - NETWORK_EXISTS = True - else: - NETWORK_EXISTS = False - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - cmd = shlex.split(cli) - - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'vrouter-ospf-add' - if state == 'absent': - command = 'vrouter-ospf-remove' - return command - - -def main(): - """ This section is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(type='str', default='present', choices=['present', - 'absent']), - pn_vrouter_name=dict(required=True, type='str'), - pn_network_ip=dict(required=True, type='str'), - pn_ospf_area=dict(type='str') - ), - required_if=( - ['state', 'present', - ['pn_network_ip', 'pn_ospf_area']], - ['state', 'absent', ['pn_network_ip']] - ) - ) - - # Accessing the arguments - state = module.params['state'] - vrouter_name = module.params['pn_vrouter_name'] - network_ip = module.params['pn_network_ip'] - ospf_area = module.params['pn_ospf_area'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = pn_cli(module) - check_cli(module, cli) - - if state == 'present': - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - if NETWORK_EXISTS is True: - module.exit_json( - skipped=True, - msg=('OSPF with network ip %s already exists on %s' - % (network_ip, vrouter_name)) - ) - cli += (' %s vrouter-name %s network %s ospf-area %s' - % (command, vrouter_name, network_ip, ospf_area)) - - if state == 'absent': - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - if NETWORK_EXISTS is False: - module.exit_json( - skipped=True, - msg=('OSPF with network ip %s already exists on %s' - % (network_ip, vrouter_name)) - ) - cli += (' %s vrouter-name %s network %s' - % (command, vrouter_name, network_ip)) - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_ospfarea.py b/plugins/modules/network/netvisor/pn_ospfarea.py deleted file mode 100644 index 4a1d850a07..0000000000 --- a/plugins/modules/network/netvisor/pn_ospfarea.py +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/python -""" PN-CLI vrouter-ospf-add/remove """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_ospfarea -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to add/remove ospf area to/from a vrouter. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute vrouter-ospf-add, vrouter-ospf-remove command. - - This command adds/removes Open Shortest Path First(OSPF) area to/from - a virtual router(vRouter) service. -options: - pn_cliusername: - description: - - Login username. - required: true - pn_clipassword: - description: - - Login password. - required: true - pn_cliswitch: - description: - - Target switch(es) to run the CLI on. - required: False - state: - description: - - State the action to perform. Use 'present' to add ospf-area, 'absent' - to remove ospf-area and 'update' to modify ospf-area. - required: true - choices: ['present', 'absent', 'update'] - pn_vrouter_name: - description: - - Specify the name of the vRouter. - required: true - pn_ospf_area: - description: - - Specify the OSPF area number. - required: true - pn_stub_type: - description: - - Specify the OSPF stub type. - choices: ['none', 'stub', 'stub-no-summary', 'nssa', 'nssa-no-summary'] - pn_prefix_listin: - description: - - OSPF prefix list for filtering incoming packets. - pn_prefix_listout: - description: - - OSPF prefix list for filtering outgoing packets. - pn_quiet: - description: - - Enable/disable system information. - required: false - type: bool - default: true -''' - -EXAMPLES = """ -- name: "Add OSPF area to vrouter" - pn_ospfarea: - state: present - pn_cliusername: admin - pn_clipassword: admin - pn_ospf_area: 1.0.0.0 - pn_stub_type: stub - -- name: "Remove OSPF from vrouter" - pn_ospf: - state: absent - pn_cliusername: admin - pn_clipassword: admin - pn_vrouter_name: name-string - pn_ospf_area: 1.0.0.0 -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the ospf command. - returned: always - type: list -stderr: - description: The set of error responses from the ospf command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# AnsibleModule boilerplate -from ansible.module_utils.basic import AnsibleModule - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'vrouter-ospf-area-add' - if state == 'absent': - command = 'vrouter-ospf-area-remove' - if state == 'update': - command = 'vrouter-ospf-area-modify' - return command - - -def main(): - """ This section is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=True, type='str'), - pn_clipassword=dict(required=True, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=['present', 'absent', 'update']), - pn_vrouter_name=dict(required=True, type='str'), - pn_ospf_area=dict(required=True, type='str'), - pn_stub_type=dict(type='str', choices=['none', 'stub', 'nssa', - 'stub-no-summary', - 'nssa-no-summary']), - pn_prefix_listin=dict(type='str'), - pn_prefix_listout=dict(type='str'), - pn_quiet=dict(type='bool', default='True') - ) - ) - - # Accessing the arguments - cliusername = module.params['pn_cliusername'] - clipassword = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - vrouter_name = module.params['pn_vrouter_name'] - ospf_area = module.params['pn_ospf_area'] - stub_type = module.params['pn_stub_type'] - prefix_listin = module.params['pn_prefix_listin'] - prefix_listout = module.params['pn_prefix_listout'] - quiet = module.params['pn_quiet'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = '/usr/bin/cli' - - if quiet is True: - cli += ' --quiet ' - - cli += ' --user %s:%s ' % (cliusername, clipassword) - - if cliswitch: - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - - cli += ' %s vrouter-name %s area %s ' % (command, vrouter_name, ospf_area) - - if stub_type: - cli += ' stub-type ' + stub_type - - if prefix_listin: - cli += ' prefix-list-in ' + prefix_listin - - if prefix_listout: - cli += ' prefix-list-out ' + prefix_listout - - # Run the CLI command - ospfcommand = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(ospfcommand) - - # Response in JSON format - if result != 0: - module.exit_json( - command=cli, - stderr=err.rstrip("\r\n"), - changed=False - ) - - else: - module.exit_json( - command=cli, - stdout=out.rstrip("\r\n"), - changed=True - ) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_port_config.py b/plugins/modules/network/netvisor/pn_port_config.py deleted file mode 100644 index ecab7cbcc6..0000000000 --- a/plugins/modules/network/netvisor/pn_port_config.py +++ /dev/null @@ -1,382 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_port_config -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify port-config -description: - - This module can be used to modify a port configuration. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(update) to modify the port-config. - required: True - type: str - choices: ['update'] - pn_intf: - description: - - physical interface. - required: False - type: str - pn_crc_check_enable: - description: - - CRC check on ingress and rewrite on egress. - required: False - type: bool - pn_dscp_map: - description: - - DSCP map name to enable on port. - required: False - type: str - pn_autoneg: - description: - - physical port autonegotiation. - required: False - type: bool - pn_speed: - description: - - physical port speed. - required: False - choices: ['disable', '10m', '100m', '1g', - '2.5g', '10g', '25g', '40g', '50g', '100g'] - pn_port: - description: - - physical port. - required: False - type: str - pn_vxlan_termination: - description: - - physical port vxlan termination setting. - required: False - type: bool - pn_pause: - description: - - physical port pause. - required: False - type: bool - pn_loopback: - description: - - physical port loopback. - required: False - type: bool - pn_loop_vlans: - description: - - looping vlans. - required: False - type: str - pn_routing: - description: - - routing. - required: False - type: bool - pn_edge_switch: - description: - - physical port edge switch. - required: False - type: bool - pn_enable: - description: - - physical port enable. - required: False - type: bool - pn_description: - description: - - physical port description. - required: False - type: str - pn_host_enable: - description: - - Host facing port control setting. - required: False - type: bool - pn_allowed_tpid: - description: - - Allowed TPID in addition to 0x8100 on Vlan header. - required: False - type: str - choices: ['vlan', 'q-in-q', 'q-in-q-old'] - pn_mirror_only: - description: - - physical port mirror only. - required: False - type: bool - pn_reflect: - description: - - physical port reflection. - required: False - type: bool - pn_jumbo: - description: - - jumbo frames on physical port. - required: False - type: bool - pn_egress_rate_limit: - description: - - max egress port data rate limit. - required: False - type: str - pn_eth_mode: - description: - - physical Ethernet mode. - required: False - choices: ['1000base-x', 'sgmii', 'disabled', 'GMII'] - pn_fabric_guard: - description: - - Fabric guard configuration. - required: False - type: bool - pn_local_switching: - description: - - no-local-switching port cannot bridge traffic to - another no-local-switching port. - required: False - type: bool - pn_lacp_priority: - description: - - LACP priority from 1 to 65535. - required: False - type: str - pn_send_port: - description: - - send port. - required: False - type: str - pn_port_mac_address: - description: - - physical port MAC Address. - required: False - type: str - pn_defer_bringup: - description: - - defer port bringup. - required: False - type: bool -''' - -EXAMPLES = """ -- name: port config modify - pn_port_config: - pn_cliswitch: "sw01" - state: "update" - pn_port: "all" - pn_dscp_map: "foo" - -- name: port config modify - pn_port_config: - pn_cliswitch: "sw01" - state: "update" - pn_port: "all" - pn_host_enable: true -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the port-config command. - returned: always - type: list -stderr: - description: set of error responses from the port-config command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the dscp-map-show name command. - If a user with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_dscp_map'] - - cli += ' dscp-map-show name %s format name no-show-headers' % name - out = run_commands(module, cli)[1] - - out = out.split() - - return True if name in out[-1] else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='port-config-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=['update']), - pn_intf=dict(required=False, type='str'), - pn_crc_check_enable=dict(required=False, type='bool'), - pn_dscp_map=dict(required=False, type='str'), - pn_autoneg=dict(required=False, type='bool'), - pn_speed=dict(required=False, type='str', - choices=['disable', '10m', '100m', - '1g', '2.5g', '10g', '25g', - '40g', '50g', '100g']), - pn_port=dict(required=False, type='str'), - pn_vxlan_termination=dict(required=False, type='bool'), - pn_pause=dict(required=False, type='bool'), - pn_loopback=dict(required=False, type='bool'), - pn_loop_vlans=dict(required=False, type='str'), - pn_routing=dict(required=False, type='bool'), - pn_edge_switch=dict(required=False, type='bool'), - pn_enable=dict(required=False, type='bool'), - pn_description=dict(required=False, type='str'), - pn_host_enable=dict(required=False, type='bool'), - pn_allowed_tpid=dict(required=False, type='str', - choices=['vlan', 'q-in-q', 'q-in-q-old']), - pn_mirror_only=dict(required=False, type='bool'), - pn_reflect=dict(required=False, type='bool'), - pn_jumbo=dict(required=False, type='bool'), - pn_egress_rate_limit=dict(required=False, type='str'), - pn_eth_mode=dict(required=False, type='str', - choices=['1000base-x', 'sgmii', - 'disabled', 'GMII']), - pn_fabric_guard=dict(required=False, type='bool'), - pn_local_switching=dict(required=False, type='bool'), - pn_lacp_priority=dict(required=False, type='str'), - pn_send_port=dict(required=False, type='str'), - pn_port_mac_address=dict(required=False, type='str'), - pn_defer_bringup=dict(required=False, type='bool'), - ), - required_if=( - ['state', 'update', ['pn_port']], - ), - required_one_of=[['pn_intf', 'pn_crc_check_enable', 'pn_dscp_map', - 'pn_speed', 'pn_autoneg', - 'pn_vxlan_termination', 'pn_pause', - 'pn_fec', 'pn_loopback', 'pn_loop_vlans', - 'pn_routing', 'pn_edge_switch', - 'pn_enable', 'pn_description', - 'pn_host_enable', 'pn_allowed_tpid', - 'pn_mirror_only', 'pn_reflect', - 'pn_jumbo', 'pn_egress_rate_limit', - 'pn_eth_mode', 'pn_fabric_guard', - 'pn_local_switching', 'pn_lacp_priority', - 'pn_send_port', 'pn_port_mac_address', - 'pn_defer_bringup']], - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - intf = module.params['pn_intf'] - crc_check_enable = module.params['pn_crc_check_enable'] - dscp_map = module.params['pn_dscp_map'] - autoneg = module.params['pn_autoneg'] - speed = module.params['pn_speed'] - port = module.params['pn_port'] - vxlan_termination = module.params['pn_vxlan_termination'] - pause = module.params['pn_pause'] - loopback = module.params['pn_loopback'] - loop_vlans = module.params['pn_loop_vlans'] - routing = module.params['pn_routing'] - edge_switch = module.params['pn_edge_switch'] - enable = module.params['pn_enable'] - description = module.params['pn_description'] - host_enable = module.params['pn_host_enable'] - allowed_tpid = module.params['pn_allowed_tpid'] - mirror_only = module.params['pn_mirror_only'] - reflect = module.params['pn_reflect'] - jumbo = module.params['pn_jumbo'] - egress_rate_limit = module.params['pn_egress_rate_limit'] - eth_mode = module.params['pn_eth_mode'] - fabric_guard = module.params['pn_fabric_guard'] - local_switching = module.params['pn_local_switching'] - lacp_priority = module.params['pn_lacp_priority'] - send_port = module.params['pn_send_port'] - port_mac_address = module.params['pn_port_mac_address'] - defer_bringup = module.params['pn_defer_bringup'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if dscp_map: - NAME_EXISTS = check_cli(module, cli) - - if command == 'port-config-modify': - cli += ' %s ' % command - if dscp_map: - if NAME_EXISTS is False: - module.fail_json( - failed=True, - msg='Create dscp map with name %s before updating' % dscp_map - ) - - cli += ' dscp-map ' + dscp_map - if intf: - cli += ' intf ' + intf - if speed: - cli += ' speed ' + speed - if port: - cli += ' port ' + port - if allowed_tpid: - cli += ' allowed-tpid ' + allowed_tpid - if egress_rate_limit: - cli += ' egress-rate-limit ' + egress_rate_limit - if eth_mode: - cli += ' eth-mode ' + eth_mode - if lacp_priority: - cli += ' lacp-priority ' + lacp_priority - if send_port: - cli += ' send-port ' + send_port - if port_mac_address: - cli += ' port-mac-address ' + port_mac_address - - cli += booleanArgs(crc_check_enable, 'crc-check-enable', 'crc-check-disable') - cli += booleanArgs(autoneg, 'autoneg', 'no-autoneg') - cli += booleanArgs(vxlan_termination, 'vxlan-termination', 'no-vxlan-termination') - cli += booleanArgs(pause, 'pause', 'no-pause') - cli += booleanArgs(loopback, 'loopback', 'no-loopback') - cli += booleanArgs(routing, 'routing', 'no-routing') - cli += booleanArgs(edge_switch, 'edge-switch', 'no-edge-switch') - cli += booleanArgs(enable, 'enable', 'disable') - cli += booleanArgs(host_enable, 'host-enable', 'host-disable') - cli += booleanArgs(mirror_only, 'mirror-only', 'no-mirror-receive-only') - cli += booleanArgs(reflect, 'reflect', 'no-reflect') - cli += booleanArgs(jumbo, 'jumbo', 'no-jumbo') - cli += booleanArgs(fabric_guard, 'fabric-guard', 'no-fabric-guard') - cli += booleanArgs(local_switching, 'local-switching', 'no-local-switching') - cli += booleanArgs(defer_bringup, 'defer-bringup', 'no-defer-bringup') - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_port_cos_bw.py b/plugins/modules/network/netvisor/pn_port_cos_bw.py deleted file mode 100644 index f24d223fac..0000000000 --- a/plugins/modules/network/netvisor/pn_port_cos_bw.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_port_cos_bw -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify port-cos-bw -description: - - This module can be used to update bw settings for CoS queues. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: False - type: str - state: - description: - - State the action to perform. Use C(update) to modify the port-cos-bw. - required: True - type: str - choices: ['update'] - pn_max_bw_limit: - description: - - Maximum b/w in percentage. - required: False - type: str - pn_cos: - description: - - CoS priority. - required: False - type: str - pn_port: - description: - - physical port number. - required: False - type: str - pn_weight: - description: - - Scheduling weight (1 to 127) after b/w guarantee met. - required: False - type: str - choices: ['priority', 'no-priority'] - pn_min_bw_guarantee: - description: - - Minimum b/w in percentage. - required: False - type: str -''' - -EXAMPLES = """ -- name: port cos bw modify - pn_port_cos_bw: - pn_cliswitch: "sw01" - state: "update" - pn_port: "1" - pn_cos: "0" - pn_min_bw_guarantee: "60" - -- name: port cos bw modify - pn_port_cos_bw: - pn_cliswitch: "sw01" - state: "update" - pn_port: "all" - pn_cos: "0" - pn_weight: "priority" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the port-cos-bw command. - returned: always - type: list -stderr: - description: set of error responses from the port-cos-bw command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='port-cos-bw-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_max_bw_limit=dict(required=False, type='str'), - pn_cos=dict(required=False, type='str'), - pn_port=dict(required=False, type='str'), - pn_weight=dict(required=False, type='str', - choices=['priority', 'no-priority']), - pn_min_bw_guarantee=dict(required=False, type='str'), - ), - required_if=( - ['state', 'update', ['pn_cos', 'pn_port']], - ), - required_one_of=[['pn_max_bw_limit', 'pn_min_bw_guarantee', 'pn_weight']], - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - max_bw_limit = module.params['pn_max_bw_limit'] - cos = module.params['pn_cos'] - port = module.params['pn_port'] - weight = module.params['pn_weight'] - min_bw_guarantee = module.params['pn_min_bw_guarantee'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'port-cos-bw-modify': - cli += ' %s ' % command - if max_bw_limit: - cli += ' max-bw-limit ' + max_bw_limit - if cos: - cli += ' cos ' + cos - if port: - cli += ' port ' + port - if weight: - cli += ' weight ' + weight - if min_bw_guarantee: - cli += ' min-bw-guarantee ' + min_bw_guarantee - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_port_cos_rate_setting.py b/plugins/modules/network/netvisor/pn_port_cos_rate_setting.py deleted file mode 100644 index 0a111dfe51..0000000000 --- a/plugins/modules/network/netvisor/pn_port_cos_rate_setting.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_port_cos_rate_setting -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify port-cos-rate-setting -description: - - This modules can be used to update the port cos rate limit. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(update) to modify - the port-cos-rate-setting. - required: true - type: str - choices: ['update'] - pn_cos0_rate: - description: - - cos0 rate limit (pps) unlimited or 0 to 10000000. - required: false - type: str - pn_cos1_rate: - description: - - cos1 rate limit (pps) unlimited or 0 to 10000000. - required: false - type: str - pn_cos2_rate: - description: - - cos2 rate limit (pps) unlimited or 0 to 10000000. - required: false - type: str - pn_cos3_rate: - description: - - cos3 rate limit (pps) unlimited or 0 to 10000000. - required: false - type: str - pn_cos4_rate: - description: - - cos4 rate limit (pps) unlimited or 0 to 10000000. - required: false - type: str - pn_cos5_rate: - description: - - cos5 rate limit (pps) unlimited or 0 to 10000000. - required: false - type: str - pn_cos6_rate: - description: - - cos6 rate limit (pps) unlimited or 0 to 10000000. - required: false - type: str - pn_cos7_rate: - description: - - cos7 rate limit (pps) unlimited or 0 to 10000000. - required: false - type: str - pn_port: - description: - - port. - required: false - type: str - choices: ['control-port', 'data-port', 'span-ports'] -''' - -EXAMPLES = """ -- name: port cos rate modify - pn_port_cos_rate_setting: - pn_cliswitch: "sw01" - state: "update" - pn_port: "control-port" - pn_cos1_rate: "1000" - pn_cos5_rate: "1000" - pn_cos2_rate: "1000" - pn_cos0_rate: "1000" - -- name: port cos rate modify - pn_port_cos_rate_setting: - pn_cliswitch: "sw01" - state: "update" - pn_port: "data-port" - pn_cos1_rate: "2000" - pn_cos5_rate: "2000" - pn_cos2_rate: "2000" - pn_cos0_rate: "2000" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the port-cos-rate-setting command. - returned: always - type: list -stderr: - description: set of error responses from the port-cos-rate-setting command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='port-cos-rate-setting-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_cos1_rate=dict(required=False, type='str'), - pn_cos5_rate=dict(required=False, type='str'), - pn_cos2_rate=dict(required=False, type='str'), - pn_cos0_rate=dict(required=False, type='str'), - pn_cos6_rate=dict(required=False, type='str'), - pn_cos3_rate=dict(required=False, type='str'), - pn_cos4_rate=dict(required=False, type='str'), - pn_cos7_rate=dict(required=False, type='str'), - pn_port=dict(required=False, type='str', - choices=['control-port', 'data-port', 'span-ports']), - ), - required_if=( - ['state', 'update', ['pn_port']], - ), - required_one_of=[['pn_cos0_rate', - 'pn_cos1_rate', - 'pn_cos2_rate', - 'pn_cos3_rate', - 'pn_cos4_rate', - 'pn_cos5_rate', - 'pn_cos6_rate', - 'pn_cos7_rate']], - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - cos1_rate = module.params['pn_cos1_rate'] - cos5_rate = module.params['pn_cos5_rate'] - cos2_rate = module.params['pn_cos2_rate'] - cos0_rate = module.params['pn_cos0_rate'] - cos6_rate = module.params['pn_cos6_rate'] - cos3_rate = module.params['pn_cos3_rate'] - cos4_rate = module.params['pn_cos4_rate'] - cos7_rate = module.params['pn_cos7_rate'] - port = module.params['pn_port'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'port-cos-rate-setting-modify': - cli += ' %s ' % command - if cos1_rate: - cli += ' cos1-rate ' + cos1_rate - if cos5_rate: - cli += ' cos5-rate ' + cos5_rate - if cos2_rate: - cli += ' cos2-rate ' + cos2_rate - if cos0_rate: - cli += ' cos0-rate ' + cos0_rate - if cos6_rate: - cli += ' cos6-rate ' + cos6_rate - if cos3_rate: - cli += ' cos3-rate ' + cos3_rate - if cos4_rate: - cli += ' cos4-rate ' + cos4_rate - if cos7_rate: - cli += ' cos7-rate ' + cos7_rate - if port: - cli += ' port ' + port - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_prefix_list.py b/plugins/modules/network/netvisor/pn_prefix_list.py deleted file mode 100644 index 9623a43cfe..0000000000 --- a/plugins/modules/network/netvisor/pn_prefix_list.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_prefix_list -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/delete prefix-list -description: - - This module can be used to create or delete prefix list. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(present) to create prefix-list and - C(absent) to delete prefix-list. - required: false - type: str - choices: ['present', 'absent'] - default: 'present' - pn_name: - description: - - Prefix List Name. - required: true - type: str - pn_scope: - description: - - scope of prefix-list. - required: false - type: str - choices: ['local', 'fabric'] -''' - -EXAMPLES = """ -- name: Create prefix list - pn_prefix_list: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_scope: "local" - state: "present" - -- name: Delete prefix list - pn_prefix_list: - pn_cliswitch: "sw01" - pn_name: "foo" - state: "absent" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the prefix-list command. - returned: always - type: list -stderr: - description: set of error responses from the prefix-list command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the prefix-list-show command. - If a name exists, return True if name exists else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - - cli += ' prefix-list-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='prefix-list-create', - absent='prefix-list-delete' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', - choices=state_map.keys(), default='present'), - pn_name=dict(required=True, type='str'), - pn_scope=dict(required=False, type='str', - choices=['local', 'fabric']), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=( - ["state", "present", ["pn_name", "pn_scope"]], - ["state", "absent", ["pn_name"]], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - name = module.params['pn_name'] - scope = module.params['pn_scope'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NAME_EXISTS = check_cli(module, cli) - - cli += ' %s name %s ' % (command, name) - - if command == 'prefix-list-delete': - if NAME_EXISTS is False: - module.exit_json( - skipped=True, - msg='prefix-list with name %s does not exist' % name - ) - else: - if command == 'prefix-list-create': - if NAME_EXISTS is True: - module.exit_json( - skipped=True, - msg='prefix list with name %s already exists' % name - ) - cli += ' scope %s ' % scope - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_prefix_list_network.py b/plugins/modules/network/netvisor/pn_prefix_list_network.py deleted file mode 100644 index b61815b21e..0000000000 --- a/plugins/modules/network/netvisor/pn_prefix_list_network.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_prefix_list_network -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove prefix-list-network -description: - - This module is used to add network associated with prefix list - and remove networks associated with prefix list. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(present) to create - prefix-list-network and C(absent) to delete prefix-list-network. - required: true - type: str - choices: ['present', 'absent'] - pn_netmask: - description: - - netmask of the network associated the prefix list. - required: false - type: str - pn_name: - description: - - Prefix List Name. - required: false - type: str - pn_network: - description: - - network associated with the prefix list. - required: false - type: str -''' - -EXAMPLES = """ -- name: prefix list network add - pn_prefix_list_network: - pn_cliswitch: "sw01" - pn_name: "foo" - pn_network: "172.16.3.1" - pn_netmask: "24" - state: "present" - -- name: prefix list network remove - pn_prefix_list_network: - pn_cliswitch: "sw01" - state: "absent" - pn_name: "foo" - pn_network: "172.16.3.1" - pn_netmask: "24" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the prefix-list-network command. - returned: always - type: list -stderr: - description: set of error responses from the prefix-list-network command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using prefix-list-network-show command. - If network exists, return as True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - network = module.params['pn_network'] - show = cli - - cli += ' prefix-list-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if name not in out.split()[-1]: - module.fail_json( - failed=True, - msg='Prefix list with name %s does not exists' % name - ) - - cli = show - cli += ' prefix-list-network-show name %s format network no-show-headers' % name - rc, out, err = run_commands(module, cli) - - if out: - out = out.split()[-1] - return True if network in out.split('/')[0] else False - - return False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='prefix-list-network-add', - absent='prefix-list-network-remove' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_netmask=dict(required=False, type='str'), - pn_name=dict(required=False, type='str'), - pn_network=dict(required=False, type='str'), - ), - required_if=( - ["state", "present", ["pn_name", "pn_network", "pn_netmask"]], - ["state", "absent", ["pn_name", "pn_network", "pn_netmask"]], - ), - required_together=( - ["pn_network", "pn_netmask"], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - netmask = module.params['pn_netmask'] - name = module.params['pn_name'] - network = module.params['pn_network'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NETWORK_EXISTS = check_cli(module, cli) - cli += ' %s ' % command - - if command == 'prefix-list-network-remove': - if NETWORK_EXISTS is False: - module.exit_json( - skipped=True, - msg='Prefix list with network %s does not exist' % network - ) - - if command == 'prefix-list-network-add': - if NETWORK_EXISTS is True: - module.exit_json( - skipped=True, - msg='Prefix list with network %s already exists' % network - ) - - if name: - cli += ' name ' + name - if network: - cli += ' network ' + network - if netmask: - cli += ' netmask ' + netmask - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_role.py b/plugins/modules/network/netvisor/pn_role.py deleted file mode 100644 index e4687ee42d..0000000000 --- a/plugins/modules/network/netvisor/pn_role.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_role -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/delete/modify role -description: - - This module can be used to create, delete and modify user roles. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(present) to create role and - C(absent) to delete role and C(update) to modify role. - required: true - type: str - choices: ['present', 'absent', 'update'] - pn_scope: - description: - - local or fabric. - required: false - type: str - choices: ['local', 'fabric'] - pn_access: - description: - - type of access. - required: false - type: str - choices: ['read-only', 'read-write'] - pn_shell: - description: - - allow shell command. - required: false - type: bool - pn_sudo: - description: - - allow sudo from shell. - required: false - type: bool - pn_running_config: - description: - - display running configuration of switch. - required: false - type: bool - pn_name: - description: - - role name. - required: true - type: str - pn_delete_from_users: - description: - - delete from users. - required: false - type: bool -''' - -EXAMPLES = """ -- name: Role create - pn_role: - pn_cliswitch: 'sw01' - state: 'present' - pn_name: 'foo' - pn_scope: 'local' - pn_access: 'read-only' - -- name: Role delete - pn_role: - pn_cliswitch: 'sw01' - state: 'absent' - pn_name: 'foo' - -- name: Role modify - pn_role: - pn_cliswitch: 'sw01' - state: 'update' - pn_name: 'foo' - pn_access: 'read-write' - pn_sudo: true - pn_shell: true -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the role command. - returned: always - type: list -stderr: - description: set of error responses from the role command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the role-show command. - If a role with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - role_name = module.params['pn_name'] - - cli += ' role-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if role_name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='role-create', - absent='role-delete', - update='role-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_scope=dict(required=False, type='str', - choices=['local', 'fabric']), - pn_access=dict(required=False, type='str', - choices=['read-only', 'read-write']), - pn_shell=dict(required=False, type='bool'), - pn_sudo=dict(required=False, type='bool'), - pn_running_config=dict(required=False, type='bool'), - pn_name=dict(required=False, type='str'), - pn_delete_from_users=dict(required=False, type='bool'), - ), - required_if=( - ["state", "present", ["pn_name", "pn_scope"]], - ["state", "absent", ["pn_name"]], - ["state", "update", ["pn_name"]], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - scope = module.params['pn_scope'] - access = module.params['pn_access'] - shell = module.params['pn_shell'] - sudo = module.params['pn_sudo'] - running_config = module.params['pn_running_config'] - name = module.params['pn_name'] - delete_from_users = module.params['pn_delete_from_users'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - ROLE_EXISTS = check_cli(module, cli) - cli += ' %s name %s ' % (command, name) - - if shell is (False or '') and sudo is True: - module.fail_json( - failed=True, - msg='sudo access requires shell access' - ) - - if command == 'role-modify': - if ROLE_EXISTS is False: - module.fail_json( - failed=True, - msg='Role with name %s does not exist' % name - ) - - if command == 'role-delete': - if ROLE_EXISTS is False: - module.exit_json( - skipped=True, - msg='Role with name %s does not exist' % name - ) - - if command == 'role-create': - if ROLE_EXISTS is True: - module.exit_json( - skipped=True, - msg='Role with name %s already exists' % name - ) - - if scope: - cli += ' scope ' + scope - - if command != 'role-delete': - if access: - cli += ' access ' + access - - cli += booleanArgs(shell, 'shell', 'no-shell') - cli += booleanArgs(sudo, 'sudo', 'no-sudo') - cli += booleanArgs(running_config, 'running-config', 'no-running-config') - - if command == 'role-modify': - if delete_from_users: - cli += ' delete-from-users ' + delete_from_users - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_show.py b/plugins/modules/network/netvisor/pn_show.py deleted file mode 100644 index bcc3deae5a..0000000000 --- a/plugins/modules/network/netvisor/pn_show.py +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/python -""" PN CLI show commands """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_show -author: "Pluribus Networks (@amitsi)" -short_description: Run show commands on nvOS device. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute show command in the nodes and returns the results - read from the device. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch(es) to run the cli on. - required: False - pn_command: - description: - - The C(pn_command) takes a CLI show command as value. - required: true - pn_parameters: - description: - - Display output using a specific parameter. Use 'all' to display - possible output. List of comma separated parameters. - default: 'all' - pn_options: - description: - - Specify formatting options. -''' - -EXAMPLES = """ -- name: run the vlan-show command - pn_show: - pn_command: 'vlan-show' - pn_parameters: id,scope,ports - pn_options: 'layout vertical' - -- name: run the vlag-show command - pn_show: - pn_command: 'vlag-show' - pn_parameters: 'id,name,cluster,mode' - pn_options: 'no-show-headers' - -- name: run the cluster-show command - pn_show: - pn_command: 'cluster-show' -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the show command. - returned: always - type: list -stderr: - description: The set of error responses from the show command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused any change on the target. - returned: always(False) - type: bool -""" - -import shlex - -# AnsibleModule boilerplate -from ansible.module_utils.basic import AnsibleModule - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch: - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - command = module.params['pn_command'] - cmd = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - msg='%s: ' % command, - stderr=err.strip(), - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - msg='%s: ' % command, - stdout=out.strip(), - changed=False - ) - - else: - module.exit_json( - command=cli, - msg='%s: Nothing to display!!!' % command, - changed=False - ) - - -def main(): - """ This section is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=True, type='str'), - pn_clipassword=dict(required=True, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str'), - pn_command=dict(required=True, type='str'), - pn_parameters=dict(default='all', type='str'), - pn_options=dict(type='str') - ) - ) - - # Accessing the arguments - command = module.params['pn_command'] - parameters = module.params['pn_parameters'] - options = module.params['pn_options'] - - # Building the CLI command string - cli = pn_cli(module) - - cli += ' %s format %s ' % (command, parameters) - - if options: - cli += options - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_snmp_community.py b/plugins/modules/network/netvisor/pn_snmp_community.py deleted file mode 100644 index b6719be569..0000000000 --- a/plugins/modules/network/netvisor/pn_snmp_community.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_snmp_community -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/modify/delete snmp-community -description: - - This module can be used to create SNMP communities for SNMPv1 or - delete SNMP communities for SNMPv1 or modify SNMP communities for SNMPv1. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - state: - description: - - State the action to perform. Use C(present) to create snmp-community and - C(absent) to delete snmp-community C(update) to update snmp-community. - required: true - type: str - choices: ['present', 'absent', 'update'] - pn_community_type: - description: - - community type. - type: str - choices: ['read-only', 'read-write'] - pn_community_string: - description: - - community name. - type: str -''' - -EXAMPLES = """ -- name: Create snmp community - pn_snmp_community: - pn_cliswitch: "sw01" - state: "present" - pn_community_string: "foo" - pn_community_type: "read-write" - -- name: Delete snmp community - pn_snmp_community: - pn_cliswitch: "sw01" - state: "absent" - pn_community_string: "foo" - -- name: Modify snmp community - pn_snmp_community: - pn_cliswitch: "sw01" - state: "update" - pn_community_string: "foo" - pn_community_type: "read-only" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the snmp-community command. - returned: always - type: list -stderr: - description: set of error responses from the snmp-community command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the snmp-community-show command. - If a user with given name exists, return as True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - comm_str = module.params['pn_community_string'] - - cli += ' snmp-community-show format community-string no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if comm_str in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='snmp-community-create', - absent='snmp-community-delete', - update='snmp-community-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_community_type=dict(required=False, type='str', - choices=['read-only', 'read-write']), - pn_community_string=dict(required=False, type='str'), - ), - required_if=( - ["state", "present", ["pn_community_type", "pn_community_string"]], - ["state", "absent", ["pn_community_string"]], - ["state", "update", ["pn_community_type", "pn_community_string"]], - ) - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - community_type = module.params['pn_community_type'] - comm_str = module.params['pn_community_string'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - COMMUNITY_EXISTS = check_cli(module, cli) - - if command == 'snmp-community-modify': - if COMMUNITY_EXISTS is False: - module.fail_json( - failed=True, - msg='snmp community name %s does not exist' % comm_str - ) - - if command == 'snmp-community-delete': - if COMMUNITY_EXISTS is False: - module.exit_json( - skipped=True, - msg='snmp community name %s does not exist' % comm_str - ) - - if command == 'snmp-community-create': - if COMMUNITY_EXISTS is True: - module.exit_json( - skipped=True, - msg='snmp community with name %s already exists' % comm_str - ) - - cli += ' %s community-string %s ' % (command, comm_str) - - if command != 'snmp-community-delete' and community_type: - cli += ' community-type ' + community_type - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_snmp_trap_sink.py b/plugins/modules/network/netvisor/pn_snmp_trap_sink.py deleted file mode 100644 index 35d8ac0144..0000000000 --- a/plugins/modules/network/netvisor/pn_snmp_trap_sink.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_snmp_trap_sink -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/delete snmp-trap-sink -description: - - This module can be used to create a SNMP trap sink and delete a SNMP trap sink. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(present) to create snmp-trap-sink and - C(absent) to delete snmp-trap-sink. - required: true - type: str - choices: ['present', 'absent'] - pn_dest_host: - description: - - destination host. - type: str - pn_community: - description: - - community type. - type: str - pn_dest_port: - description: - - destination port. - type: str - default: '162' - pn_type: - description: - - trap type. - type: str - choices: ['TRAP_TYPE_V1_TRAP', 'TRAP_TYPE_V2C_TRAP', 'TRAP_TYPE_V2_INFORM'] - default: 'TRAP_TYPE_V2C_TRAP' -''' - -EXAMPLES = """ -- name: snmp trap sink functionality - pn_snmp_trap_sink: - pn_cliswitch: "sw01" - state: "present" - pn_community: "foo" - pn_type: "TRAP_TYPE_V2_INFORM" - pn_dest_host: "192.168.67.8" - -- name: snmp trap sink functionality - pn_snmp_trap_sink: - pn_cliswitch: "sw01" - state: "absent" - pn_community: "foo" - pn_type: "TRAP_TYPE_V2_INFORM" - pn_dest_host: "192.168.67.8" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the snmp-trap-sink command. - returned: always - type: list -stderr: - description: set of error responses from the snmp-trap-sink command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the snmp-trap-sink-show command. - If a trap with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - community = module.params['pn_community'] - dest_host = module.params['pn_dest_host'] - - show = cli - cli += ' snmp-community-show format community-string no-show-headers' - rc, out, err = run_commands(module, cli) - - if out: - out = out.split() - - if community in out: - cli = show - cli += ' snmp-trap-sink-show community %s format type,dest-host no-show-headers' % community - rc, out, err = run_commands(module, cli) - - if out: - out = out.split() - - return True if dest_host in out else False - else: - return None - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='snmp-trap-sink-create', - absent='snmp-trap-sink-delete' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_dest_host=dict(required=False, type='str'), - pn_community=dict(required=False, type='str'), - pn_dest_port=dict(required=False, type='str', default='162'), - pn_type=dict(required=False, type='str', - choices=['TRAP_TYPE_V1_TRAP', - 'TRAP_TYPE_V2C_TRAP', - 'TRAP_TYPE_V2_INFORM'], - default='TRAP_TYPE_V2C_TRAP'), - ), - required_if=( - ["state", "present", ["pn_community", "pn_dest_host"]], - ["state", "absent", ["pn_community", "pn_dest_host"]], - ) - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - dest_host = module.params['pn_dest_host'] - community = module.params['pn_community'] - dest_port = module.params['pn_dest_port'] - pn_type = module.params['pn_type'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - VALUE_EXISTS = check_cli(module, cli) - cli += ' %s ' % command - - if command == 'snmp-trap-sink-create': - if VALUE_EXISTS is True: - module.exit_json( - skipped=True, - msg='snmp trap sink already exists' - ) - if VALUE_EXISTS is None: - module.fail_json( - failed=True, - msg='snmp community does not exists to create trap sink' - ) - if pn_type: - cli += ' type ' + pn_type - if dest_host: - cli += ' dest-host ' + dest_host - if community: - cli += ' community ' + community - if dest_port: - cli += ' dest-port ' + dest_port - - if command == 'snmp-trap-sink-delete': - if VALUE_EXISTS is None: - module.fail_json( - failed=True, - msg='snmp community does not exists to delete trap sink' - ) - if VALUE_EXISTS is False: - module.exit_json( - skipped=True, - msg='snmp-trap-sink with community %s does not exist with dest-host %s ' % (community, dest_host) - ) - if community: - cli += ' community ' + community - if dest_host: - cli += ' dest-host ' + dest_host - if dest_port: - cli += ' dest-port ' + dest_port - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_snmp_vacm.py b/plugins/modules/network/netvisor/pn_snmp_vacm.py deleted file mode 100644 index b3047bc77f..0000000000 --- a/plugins/modules/network/netvisor/pn_snmp_vacm.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_snmp_vacm -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/modify/delete snmp-vacm -description: - - This module can be used to create View Access Control Models (VACM), - modify VACM and delete VACM. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - type: str - required: false - state: - description: - - State the action to perform. Use C(present) to create snmp-vacm and - C(absent) to delete snmp-vacm and C(update) to modify snmp-vacm. - type: str - required: true - choices: ['present', 'absent', 'update'] - pn_oid_restrict: - description: - - restrict OID. - type: str - pn_priv: - description: - - privileges. - type: bool - pn_auth: - description: - - authentication required. - type: bool - pn_user_type: - description: - - SNMP user type. - type: str - choices: ['rouser', 'rwuser'] - pn_user_name: - description: - - SNMP administrator name. - type: str -''' - -EXAMPLES = """ -- name: create snmp vacm - pn_snmp_vacm: - pn_cliswitch: "sw01" - state: "present" - pn_user_name: "foo" - pn_user_type: "rouser" - -- name: update snmp vacm - pn_snmp_vacm: - pn_cliswitch: "sw01" - state: "update" - pn_user_name: "foo" - pn_user_type: "rwuser" - -- name: delete snmp vacm - pn_snmp_vacm: - pn_cliswitch: "sw01" - state: "absent" - pn_user_name: "foo" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the snmp-vacm command. - returned: always - type: list -stderr: - description: set of error responses from the snmp-vacm command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the snmp-vacm-show command. - If a user with given name exists, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - user_name = module.params['pn_user_name'] - show = cli - - cli += ' snmp-user-show format user-name no-show-headers' - rc, out, err = run_commands(module, cli) - - if out and user_name in out.split(): - pass - else: - return None - - cli = show - cli += ' snmp-vacm-show format user-name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if user_name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='snmp-vacm-create', - absent='snmp-vacm-delete', - update='snmp-vacm-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_oid_restrict=dict(required=False, type='str'), - pn_priv=dict(required=False, type='bool'), - pn_auth=dict(required=False, type='bool'), - pn_user_type=dict(required=False, type='str', - choices=['rouser', 'rwuser']), - pn_user_name=dict(required=False, type='str'), - ), - required_if=( - ["state", "present", ["pn_user_name"]], - ["state", "absent", ["pn_user_name"]], - ["state", "update", ["pn_user_name"]] - ) - - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - oid_restrict = module.params['pn_oid_restrict'] - priv = module.params['pn_priv'] - auth = module.params['pn_auth'] - user_type = module.params['pn_user_type'] - user_name = module.params['pn_user_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - USER_EXISTS = check_cli(module, cli) - cli += ' %s user-name %s ' % (command, user_name) - - if command == 'snmp-vacm-modify': - if USER_EXISTS is None: - module.fail_json( - failed=True, - msg='snmp user with name %s does not exists' % user_name - ) - if USER_EXISTS is False: - module.fail_json( - failed=True, - msg='snmp vacm with name %s does not exists' % user_name - ) - - if command == 'snmp-vacm-delete': - if USER_EXISTS is None: - module.fail_json( - failed=True, - msg='snmp user with name %s does not exists' % user_name - ) - - if USER_EXISTS is False: - module.exit_json( - skipped=True, - msg='snmp vacm with name %s does not exist' % user_name - ) - - if command == 'snmp-vacm-create': - if USER_EXISTS is None: - module.fail_json( - failed=True, - msg='snmp user with name %s does not exists' % user_name - ) - if USER_EXISTS is True: - module.exit_json( - skipped=True, - msg='snmp vacm with name %s already exists' % user_name - ) - - if command != 'snmp-vacm-delete': - if oid_restrict: - cli += ' oid-restrict ' + oid_restrict - if user_type: - cli += ' user-type ' + user_type - - cli += booleanArgs(auth, 'auth', 'no-auth') - cli += booleanArgs(priv, 'priv', 'no-priv') - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_stp.py b/plugins/modules/network/netvisor/pn_stp.py deleted file mode 100644 index 29421e62c5..0000000000 --- a/plugins/modules/network/netvisor/pn_stp.py +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_stp -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify stp -description: - - This module can be used to modify Spanning Tree Protocol parameters. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - type: str - required: false - state: - description: - - State the action to perform. Use C(update) to stp. - type: str - required: true - choices: ['update'] - pn_hello_time: - description: - - STP hello time between 1 and 10 secs. - type: str - default: '2' - pn_enable: - description: - - enable or disable STP - type: bool - pn_root_guard_wait_time: - description: - - root guard wait time between 0 and 300 secs. 0 to disable wait. - type: str - default: '20' - pn_bpdus_bridge_ports: - description: - - BPDU packets to bridge specific port. - type: bool - pn_mst_max_hops: - description: - - maximum hop count for mstp bpdu. - type: str - default: '20' - pn_bridge_id: - description: - - STP bridge id. - type: str - pn_max_age: - description: - - maximum age time between 6 and 40 secs. - type: str - default: '20' - pn_stp_mode: - description: - - STP mode. - type: str - choices: ['rstp', 'mstp'] - pn_mst_config_name: - description: - - Name for MST Configuration Instance. - type: str - pn_forwarding_delay: - description: - - STP forwarding delay between 4 and 30 secs. - type: str - default: '15' - pn_bridge_priority: - description: - - STP bridge priority. - type: str - default: '32768' -''' - -EXAMPLES = """ -- name: Modify stp - pn_stp: - pn_cliswitch: "sw01" - state: "update" - pn_hello_time: "3" - pn_stp_mode: "rstp" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the stp command. - returned: always - type: list -stderr: - description: set of error responses from the stp command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='stp-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_hello_time=dict(required=False, type='str', default='2'), - pn_enable=dict(required=False, type='bool'), - pn_root_guard_wait_time=dict(required=False, type='str', default='20'), - pn_bpdus_bridge_ports=dict(required=False, type='bool'), - pn_mst_max_hops=dict(required=False, type='str', default='20'), - pn_bridge_id=dict(required=False, type='str'), - pn_max_age=dict(required=False, type='str', default='20'), - pn_stp_mode=dict(required=False, type='str', - choices=['rstp', 'mstp']), - pn_mst_config_name=dict(required=False, type='str'), - pn_forwarding_delay=dict(required=False, type='str', default='15'), - pn_bridge_priority=dict(required=False, type='str', default='32768'), - ), - required_one_of=[['pn_enable', 'pn_hello_time', - 'pn_root_guard_wait_time', - 'pn_bpdus_bridge_ports', - 'pn_mst_max_hops', - 'pn_bridge_id', - 'pn_max_age', - 'pn_stp_mode', - 'pn_mst_config_name', - 'pn_forwarding_delay', - 'pn_bridge_priority']] - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - hello_time = module.params['pn_hello_time'] - enable = module.params['pn_enable'] - root_guard_wait_time = module.params['pn_root_guard_wait_time'] - bpdus_bridge_ports = module.params['pn_bpdus_bridge_ports'] - mst_max_hops = module.params['pn_mst_max_hops'] - bridge_id = module.params['pn_bridge_id'] - max_age = module.params['pn_max_age'] - stp_mode = module.params['pn_stp_mode'] - mst_config_name = module.params['pn_mst_config_name'] - forwarding_delay = module.params['pn_forwarding_delay'] - bridge_priority = module.params['pn_bridge_priority'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'stp-modify': - cli += ' %s ' % command - if hello_time: - cli += ' hello-time ' + hello_time - if root_guard_wait_time: - cli += ' root-guard-wait-time ' + root_guard_wait_time - if mst_max_hops: - cli += ' mst-max-hops ' + mst_max_hops - if bridge_id: - cli += ' bridge-id ' + bridge_id - if max_age: - cli += ' max-age ' + max_age - if stp_mode: - cli += ' stp-mode ' + stp_mode - if mst_config_name: - cli += ' mst-config-name ' + mst_config_name - if forwarding_delay: - cli += ' forwarding-delay ' + forwarding_delay - if bridge_priority: - cli += ' bridge-priority ' + bridge_priority - - cli += booleanArgs(enable, 'enable', 'disable') - cli += booleanArgs(bpdus_bridge_ports, 'bpdus-bridge-ports', 'bpdus-all-ports') - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_stp_port.py b/plugins/modules/network/netvisor/pn_stp_port.py deleted file mode 100644 index 013ba07ec3..0000000000 --- a/plugins/modules/network/netvisor/pn_stp_port.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_stp_port -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify stp-port. -description: - - This module can be used modify Spanning Tree Protocol (STP) parameters on ports. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - type: str - required: false - state: - description: - - State the action to perform. Use C(update) to update stp-port. - type: str - required: true - choices: ['update'] - pn_priority: - description: - - STP port priority from 0 to 240. - type: str - default: '128' - pn_cost: - description: - - STP port cost from 1 to 200000000. - type: str - default: '2000' - pn_root_guard: - description: - - STP port Root guard. - type: bool - pn_filter: - description: - - STP port filters BPDUs. - type: bool - pn_edge: - description: - - STP port is an edge port. - type: bool - pn_bpdu_guard: - description: - - STP port BPDU guard. - type: bool - pn_port: - description: - - STP port. - type: str - pn_block: - description: - - Specify if a STP port blocks BPDUs. - type: bool -''' - -EXAMPLES = """ -- name: Modify stp port - pn_stp_port: - pn_cliswitch: "sw01" - state: "update" - pn_port: "1" - pn_filter: True - pn_priority: '144' - -- name: Modify stp port - pn_stp_port: - pn_cliswitch: "sw01" - state: "update" - pn_port: "1" - pn_cost: "200" - -- name: Modify stp port - pn_stp_port: - pn_cliswitch: "sw01" - state: "update" - pn_port: "1" - pn_edge: True - pn_cost: "200" - -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the stp-port command. - returned: always - type: list -stderr: - description: set of error responses from the stp-port command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='stp-port-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_priority=dict(required=False, type='str', default='128'), - pn_cost=dict(required=False, type='str', default='2000'), - pn_root_guard=dict(required=False, type='bool'), - pn_filter=dict(required=False, type='bool'), - pn_edge=dict(required=False, type='bool'), - pn_bpdu_guard=dict(required=False, type='bool'), - pn_port=dict(required=False, type='str'), - pn_block=dict(required=False, type='bool'), - ), - required_if=( - ["state", "update", ["pn_port"]], - ), - required_one_of=( - ['pn_cost', 'pn_root_guard', 'pn_filter', - 'pn_edge', 'pn_bpdu_guard', 'pn_block'], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - priority = module.params['pn_priority'] - cost = module.params['pn_cost'] - root_guard = module.params['pn_root_guard'] - pn_filter = module.params['pn_filter'] - edge = module.params['pn_edge'] - bpdu_guard = module.params['pn_bpdu_guard'] - port = module.params['pn_port'] - block = module.params['pn_block'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'stp-port-modify': - cli += ' %s ' % command - if priority and (int(priority) % 16 == 0 and int(priority) < 240): - cli += ' priority ' + priority - else: - module.fail_json( - failed=True, - msg='Priority must be increment of 16 and should be less that 240' - ) - if cost and (int(cost) < 200000000): - cli += ' cost ' + cost - else: - module.fail_json( - failed=True, - msg='cost must be between 1 and 200000000' - ) - if port: - cli += ' port ' + port - - cli += booleanArgs(root_guard, 'root-guard', 'no-root-guard') - cli += booleanArgs(pn_filter, 'filter', 'no-filter') - cli += booleanArgs(edge, 'edge', 'no-edge') - cli += booleanArgs(bpdu_guard, 'bpdu-guard', 'no-bpdu-guard') - cli += booleanArgs(block, 'block', 'no-block') - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_switch_setup.py b/plugins/modules/network/netvisor/pn_switch_setup.py deleted file mode 100644 index b79df53971..0000000000 --- a/plugins/modules/network/netvisor/pn_switch_setup.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_switch_setup -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify switch-setup -description: - - This module can be used to modify switch setup. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(update) to modify the switch-setup. - required: true - type: str - choices: ['update'] - pn_force: - description: - - Force analytics-store change even if it involves removing data. - required: false - type: bool - pn_dns_ip: - description: - - DNS IP address. - required: false - type: str - pn_mgmt_netmask: - description: - - Netmask. - required: false - type: str - pn_gateway_ip6: - description: - - Gateway IPv6 address. - required: false - type: str - pn_in_band_ip6_assign: - description: - - Data IPv6 address assignment. - required: false - type: str - choices: ['none', 'autoconf'] - pn_domain_name: - description: - - Domain name. - required: false - type: str - pn_timezone: - description: - - Timezone to be configured. - required: false - type: str - pn_in_band_netmask: - description: - - Data in-band netmask. - required: false - type: str - pn_in_band_ip6: - description: - - Data in-band IPv6 address. - required: false - type: str - pn_in_band_netmask_ip6: - description: - - Data in-band IPv6 netmask. - required: false - type: str - pn_motd: - description: - - Message of the Day. - required: false - type: str - pn_loopback_ip6: - description: - - loopback IPv6 address. - required: false - type: str - pn_mgmt_ip6_assignment: - description: - - IPv6 address assignment. - required: false - choices: ['none', 'autoconf'] - pn_ntp_secondary_server: - description: - - Secondary NTP server. - required: false - type: str - pn_in_band_ip: - description: - - data in-band IP address. - required: false - type: str - pn_eula_accepted: - description: - - Accept EULA. - required: false - type: str - choices: ['true', 'false'] - pn_mgmt_ip: - description: - - Management IP address. - required: false - type: str - pn_ntp_server: - description: - - NTP server. - required: false - type: str - pn_mgmt_ip_assignment: - description: - - IP address assignment. - required: false - type: str - choices: ['none', 'dhcp'] - pn_date: - description: - - Date. - required: false - type: str - pn_password: - description: - - plain text password. - required: false - type: str - pn_banner: - description: - - Banner to display on server-switch. - required: false - type: str - pn_loopback_ip: - description: - - loopback IPv4 address. - required: false - type: str - pn_dns_secondary_ip: - description: - - secondary DNS IP address. - required: false - type: str - pn_switch_name: - description: - - switch name. - required: false - type: str - pn_eula_timestamp: - description: - - EULA timestamp. - required: false - type: str - pn_mgmt_netmask_ip6: - description: - - IPv6 netmask. - required: false - type: str - pn_enable_host_ports: - description: - - Enable host ports by default. - required: false - type: bool - pn_mgmt_ip6: - description: - - IPv6 address. - required: false - type: str - pn_analytics_store: - description: - - type of disk storage for analytics. - required: false - type: str - choices: ['default', 'optimized'] - pn_gateway_ip: - description: - - gateway IPv4 address. - required: false - type: str -''' - -EXAMPLES = """ -- name: Modify switch - pn_switch_setup: - pn_cliswitch: "sw01" - state: "update" - pn_timezone: "America/New_York" - pn_in_band_ip: "20.20.1.1" - pn_in_band_netmask: "24" - -- name: Modify switch - pn_switch_setup: - pn_cliswitch: "sw01" - state: "update" - pn_in_band_ip6: "2001:0db8:85a3::8a2e:0370:7334" - pn_in_band_netmask_ip6: "127" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the switch-setup command. - returned: always - type: list -stderr: - description: set of error responses from the switch-setup command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, booleanArgs, run_cli - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='switch-setup-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=['update']), - pn_force=dict(required=False, type='bool'), - pn_dns_ip=dict(required=False, type='str'), - pn_mgmt_netmask=dict(required=False, type='str'), - pn_gateway_ip6=dict(required=False, type='str'), - pn_in_band_ip6_assign=dict(required=False, type='str', - choices=['none', 'autoconf']), - pn_domain_name=dict(required=False, type='str'), - pn_timezone=dict(required=False, type='str'), - pn_in_band_netmask=dict(required=False, type='str'), - pn_in_band_ip6=dict(required=False, type='str'), - pn_in_band_netmask_ip6=dict(required=False, type='str'), - pn_motd=dict(required=False, type='str'), - pn_loopback_ip6=dict(required=False, type='str'), - pn_mgmt_ip6_assignment=dict(required=False, type='str', - choices=['none', 'autoconf']), - pn_ntp_secondary_server=dict(required=False, type='str'), - pn_in_band_ip=dict(required=False, type='str'), - pn_eula_accepted=dict(required=False, type='str', - choices=['true', 'false']), - pn_mgmt_ip=dict(required=False, type='str'), - pn_ntp_server=dict(required=False, type='str'), - pn_mgmt_ip_assignment=dict(required=False, type='str', - choices=['none', 'dhcp']), - pn_date=dict(required=False, type='str'), - pn_password=dict(required=False, type='str', no_log=True), - pn_banner=dict(required=False, type='str'), - pn_loopback_ip=dict(required=False, type='str'), - pn_dns_secondary_ip=dict(required=False, type='str'), - pn_switch_name=dict(required=False, type='str'), - pn_eula_timestamp=dict(required=False, type='str'), - pn_mgmt_netmask_ip6=dict(required=False, type='str'), - pn_enable_host_ports=dict(required=False, type='bool'), - pn_mgmt_ip6=dict(required=False, type='str'), - pn_analytics_store=dict(required=False, type='str', - choices=['default', 'optimized']), - pn_gateway_ip=dict(required=False, type='str'), - ), - required_one_of=[['pn_force', 'pn_dns_ip', 'pn_mgmt_netmask', - 'pn_gateway_ip6', 'pn_in_band_ip6_assign', - 'pn_domain_name', 'pn_timezone', - 'pn_in_band_netmask', 'pn_in_band_ip6', - 'pn_in_band_netmask_ip6', 'pn_motd', - 'pn_loopback_ip6', 'pn_mgmt_ip6_assignment', - 'pn_ntp_secondary_server', 'pn_in_band_ip', - 'pn_eula_accepted', 'pn_mgmt_ip', - 'pn_ntp_server', 'pn_mgmt_ip_assignment', - 'pn_date', 'pn_password', - 'pn_banner', 'pn_loopback_ip', - 'pn_dns_secondary_ip', 'pn_switch_name', - 'pn_eula_timestamp', 'pn_mgmt_netmask_ip6', - 'pn_enable_host_ports', 'pn_mgmt_ip6', - 'pn_analytics_store', 'pn_gateway_ip']], - required_together=[['pn_in_band_ip6', 'pn_in_band_netmask_ip6'], - ['pn_in_band_ip', 'pn_in_band_netmask'], - ['pn_mgmt_ip', 'pn_mgmt_netmask'], - ['pn_mgmt_ip6', 'pn_mgmt_netmask_ip6']], - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - force = module.params['pn_force'] - dns_ip = module.params['pn_dns_ip'] - mgmt_netmask = module.params['pn_mgmt_netmask'] - gateway_ip6 = module.params['pn_gateway_ip6'] - in_band_ip6_assign = module.params['pn_in_band_ip6_assign'] - domain_name = module.params['pn_domain_name'] - timezone = module.params['pn_timezone'] - in_band_netmask = module.params['pn_in_band_netmask'] - in_band_ip6 = module.params['pn_in_band_ip6'] - in_band_netmask_ip6 = module.params['pn_in_band_netmask_ip6'] - motd = module.params['pn_motd'] - loopback_ip6 = module.params['pn_loopback_ip6'] - mgmt_ip6_assignment = module.params['pn_mgmt_ip6_assignment'] - ntp_secondary_server = module.params['pn_ntp_secondary_server'] - in_band_ip = module.params['pn_in_band_ip'] - eula_accepted = module.params['pn_eula_accepted'] - mgmt_ip = module.params['pn_mgmt_ip'] - ntp_server = module.params['pn_ntp_server'] - mgmt_ip_assignment = module.params['pn_mgmt_ip_assignment'] - date = module.params['pn_date'] - password = module.params['pn_password'] - banner = module.params['pn_banner'] - loopback_ip = module.params['pn_loopback_ip'] - dns_secondary_ip = module.params['pn_dns_secondary_ip'] - switch_name = module.params['pn_switch_name'] - eula_timestamp = module.params['pn_eula_timestamp'] - mgmt_netmask_ip6 = module.params['pn_mgmt_netmask_ip6'] - enable_host_ports = module.params['pn_enable_host_ports'] - mgmt_ip6 = module.params['pn_mgmt_ip6'] - analytics_store = module.params['pn_analytics_store'] - gateway_ip = module.params['pn_gateway_ip'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'switch-setup-modify': - cli += ' %s ' % command - if dns_ip: - cli += ' dns-ip ' + dns_ip - if mgmt_netmask: - cli += ' mgmt-netmask ' + mgmt_netmask - if gateway_ip6: - cli += ' gateway-ip6 ' + gateway_ip6 - if in_band_ip6_assign: - cli += ' in-band-ip6-assign ' + in_band_ip6_assign - if domain_name: - cli += ' domain-name ' + domain_name - if timezone: - cli += ' timezone ' + timezone - if in_band_netmask: - cli += ' in-band-netmask ' + in_band_netmask - if in_band_ip6: - cli += ' in-band-ip6 ' + in_band_ip6 - if in_band_netmask_ip6: - cli += ' in-band-netmask-ip6 ' + in_band_netmask_ip6 - if motd: - cli += ' motd ' + motd - if loopback_ip6: - cli += ' loopback-ip6 ' + loopback_ip6 - if mgmt_ip6_assignment: - cli += ' mgmt-ip6-assignment ' + mgmt_ip6_assignment - if ntp_secondary_server: - cli += ' ntp-secondary-server ' + ntp_secondary_server - if in_band_ip: - cli += ' in-band-ip ' + in_band_ip - if eula_accepted: - cli += ' eula-accepted ' + eula_accepted - if mgmt_ip: - cli += ' mgmt-ip ' + mgmt_ip - if ntp_server: - cli += ' ntp-server ' + ntp_server - if mgmt_ip_assignment: - cli += ' mgmt-ip-assignment ' + mgmt_ip_assignment - if date: - cli += ' date ' + date - if password: - cli += ' password ' + password - if banner: - cli += ' banner ' + banner - if loopback_ip: - cli += ' loopback-ip ' + loopback_ip - if dns_secondary_ip: - cli += ' dns-secondary-ip ' + dns_secondary_ip - if switch_name: - cli += ' switch-name ' + switch_name - if eula_timestamp: - cli += ' eula_timestamp ' + eula_timestamp - if mgmt_netmask_ip6: - cli += ' mgmt-netmask-ip6 ' + mgmt_netmask_ip6 - if mgmt_ip6: - cli += ' mgmt-ip6 ' + mgmt_ip6 - if analytics_store: - cli += ' analytics-store ' + analytics_store - if gateway_ip: - cli += ' gateway-ip ' + gateway_ip - - cli += booleanArgs(force, 'force', 'no-force') - cli += booleanArgs(enable_host_ports, 'enable-host-ports', 'disable-host-ports') - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_trunk.py b/plugins/modules/network/netvisor/pn_trunk.py deleted file mode 100644 index 69236c6fbc..0000000000 --- a/plugins/modules/network/netvisor/pn_trunk.py +++ /dev/null @@ -1,464 +0,0 @@ -#!/usr/bin/python -""" PN CLI trunk-create/trunk-delete/trunk-modify """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_trunk -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to create/delete/modify a trunk. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute trunk-create or trunk-delete command. - - Trunks can be used to aggregate network links at Layer 2 on the local - switch. Use this command to create a new trunk. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch(es) to run the cli on. - required: False - default: 'local' - state: - description: - - State the action to perform. Use 'present' to create trunk, - 'absent' to delete trunk and 'update' to modify trunk. - required: True - choices: ['present', 'absent', 'update'] - pn_name: - description: - - Specify the name for the trunk configuration. - required: true - pn_ports: - description: - - Specify the port number(s) for the link(s) to aggregate into the trunk. - - Required for trunk-create. - pn_speed: - description: - - Specify the port speed or disable the port. - choices: ['disable', '10m', '100m', '1g', '2.5g', '10g', '40g'] - pn_egress_rate_limit: - description: - - Specify an egress port data rate limit for the configuration. - pn_jumbo: - description: - - Specify if the port can receive jumbo frames. - type: bool - pn_lacp_mode: - description: - - Specify the LACP mode for the configuration. - choices: ['off', 'passive', 'active'] - pn_lacp_priority: - description: - - Specify the LACP priority. This is a number between 1 and 65535 with a - default value of 32768. - pn_lacp_timeout: - description: - - Specify the LACP time out as slow (30 seconds) or fast (4seconds). - The default value is slow. - choices: ['slow', 'fast'] - pn_lacp_fallback: - description: - - Specify the LACP fallback mode as bundles or individual. - choices: ['bundle', 'individual'] - pn_lacp_fallback_timeout: - description: - - Specify the LACP fallback timeout in seconds. The range is between 30 - and 60 seconds with a default value of 50 seconds. - pn_edge_switch: - description: - - Specify if the switch is an edge switch. - type: bool - pn_pause: - description: - - Specify if pause frames are sent. - type: bool - pn_description: - description: - - Specify a description for the trunk configuration. - pn_loopback: - description: - - Specify loopback if you want to use loopback. - type: bool - pn_mirror_receive: - description: - - Specify if the configuration receives mirrored traffic. - type: bool - pn_unknown_ucast_level: - description: - - Specify an unknown unicast level in percent. The default value is 100%. - pn_unknown_mcast_level: - description: - - Specify an unknown multicast level in percent. The default value is 100%. - pn_broadcast_level: - description: - - Specify a broadcast level in percent. The default value is 100%. - pn_port_macaddr: - description: - - Specify the MAC address of the port. - pn_loopvlans: - description: - - Specify a list of looping vlans. - pn_routing: - description: - - Specify if the port participates in routing on the network. - type: bool - pn_host: - description: - - Host facing port control setting. - type: bool -''' - -EXAMPLES = """ -- name: create trunk - pn_trunk: - state: 'present' - pn_name: 'spine-to-leaf' - pn_ports: '11,12,13,14' - -- name: delete trunk - pn_trunk: - state: 'absent' - pn_name: 'spine-to-leaf' -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the trunk command. - returned: always - type: list -stderr: - description: The set of error responses from the trunk command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# Ansible boiler-plate -from ansible.module_utils.basic import AnsibleModule - -TRUNK_EXISTS = None - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks for idempotency using the trunk-show command. - If a trunk with given name exists, return TRUNK_EXISTS as True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: TRUNK_EXISTS - """ - name = module.params['pn_name'] - - show = cli + ' trunk-show format switch,name no-show-headers' - show = shlex.split(show) - out = module.run_command(show)[1] - - out = out.split() - # Global flags - global TRUNK_EXISTS - if name in out: - TRUNK_EXISTS = True - else: - TRUNK_EXISTS = False - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - - cmd = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'trunk-create' - if state == 'absent': - command = 'trunk-delete' - if state == 'update': - command = 'trunk-modify' - return command - - -def main(): - """ This portion is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(required=True, type='str', - choices=['present', 'absent', 'update']), - pn_name=dict(required=True, type='str'), - pn_ports=dict(type='str'), - pn_speed=dict(type='str', - choices=['disable', '10m', '100m', '1g', '2.5g', - '10g', '40g']), - pn_egress_rate_limit=dict(type='str'), - pn_jumbo=dict(type='bool'), - pn_lacp_mode=dict(type='str', choices=[ - 'off', 'passive', 'active']), - pn_lacp_priority=dict(type='int'), - pn_lacp_timeout=dict(type='str', choices=['slow', 'fast']), - pn_lacp_fallback=dict(type='str', choices=[ - 'bundle', 'individual']), - pn_lacp_fallback_timeout=dict(type='str'), - pn_edge_switch=dict(type='bool'), - pn_pause=dict(type='bool'), - pn_description=dict(type='str'), - pn_loopback=dict(type='bool'), - pn_mirror_receive=dict(type='bool'), - pn_unknown_ucast_level=dict(type='str'), - pn_unknown_mcast_level=dict(type='str'), - pn_broadcast_level=dict(type='str'), - pn_port_macaddr=dict(type='str'), - pn_loopvlans=dict(type='str'), - pn_routing=dict(type='bool'), - pn_host=dict(type='bool') - ), - required_if=( - ["state", "present", ["pn_name", "pn_ports"]], - ["state", "absent", ["pn_name"]], - ["state", "update", ["pn_name"]] - ) - ) - - # Accessing the arguments - state = module.params['state'] - name = module.params['pn_name'] - ports = module.params['pn_ports'] - speed = module.params['pn_speed'] - egress_rate_limit = module.params['pn_egress_rate_limit'] - jumbo = module.params['pn_jumbo'] - lacp_mode = module.params['pn_lacp_mode'] - lacp_priority = module.params['pn_lacp_priority'] - lacp_timeout = module.params['pn_lacp_timeout'] - lacp_fallback = module.params['pn_lacp_fallback'] - lacp_fallback_timeout = module.params['pn_lacp_fallback_timeout'] - edge_switch = module.params['pn_edge_switch'] - pause = module.params['pn_pause'] - description = module.params['pn_description'] - loopback = module.params['pn_loopback'] - mirror_receive = module.params['pn_mirror_receive'] - unknown_ucast_level = module.params['pn_unknown_ucast_level'] - unknown_mcast_level = module.params['pn_unknown_mcast_level'] - broadcast_level = module.params['pn_broadcast_level'] - port_macaddr = module.params['pn_port_macaddr'] - loopvlans = module.params['pn_loopvlans'] - routing = module.params['pn_routing'] - host = module.params['pn_host'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = pn_cli(module) - - if command == 'trunk-delete': - - check_cli(module, cli) - if TRUNK_EXISTS is False: - module.exit_json( - skipped=True, - msg='Trunk with name %s does not exist' % name - ) - cli += ' %s name %s ' % (command, name) - - else: - if command == 'trunk-create': - check_cli(module, cli) - if TRUNK_EXISTS is True: - module.exit_json( - skipped=True, - msg='Trunk with name %s already exists' % name - ) - cli += ' %s name %s ' % (command, name) - - # Appending options - if ports: - cli += ' ports ' + ports - - if speed: - cli += ' speed ' + speed - - if egress_rate_limit: - cli += ' egress-rate-limit ' + egress_rate_limit - - if jumbo is True: - cli += ' jumbo ' - if jumbo is False: - cli += ' no-jumbo ' - - if lacp_mode: - cli += ' lacp-mode ' + lacp_mode - - if lacp_priority: - cli += ' lacp-priority ' + lacp_priority - - if lacp_timeout: - cli += ' lacp-timeout ' + lacp_timeout - - if lacp_fallback: - cli += ' lacp-fallback ' + lacp_fallback - - if lacp_fallback_timeout: - cli += ' lacp-fallback-timeout ' + lacp_fallback_timeout - - if edge_switch is True: - cli += ' edge-switch ' - if edge_switch is False: - cli += ' no-edge-switch ' - - if pause is True: - cli += ' pause ' - if pause is False: - cli += ' no-pause ' - - if description: - cli += ' description ' + description - - if loopback is True: - cli += ' loopback ' - if loopback is False: - cli += ' no-loopback ' - - if mirror_receive is True: - cli += ' mirror-receive-only ' - if mirror_receive is False: - cli += ' no-mirror-receive-only ' - - if unknown_ucast_level: - cli += ' unknown-ucast-level ' + unknown_ucast_level - - if unknown_mcast_level: - cli += ' unknown-mcast-level ' + unknown_mcast_level - - if broadcast_level: - cli += ' broadcast-level ' + broadcast_level - - if port_macaddr: - cli += ' port-mac-address ' + port_macaddr - - if loopvlans: - cli += ' loopvlans ' + loopvlans - - if routing is True: - cli += ' routing ' - if routing is False: - cli += ' no-routing ' - - if host is True: - cli += ' host-enable ' - if host is False: - cli += ' host-disable ' - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_user.py b/plugins/modules/network/netvisor/pn_user.py deleted file mode 100644 index e3ac9db3e2..0000000000 --- a/plugins/modules/network/netvisor/pn_user.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_user -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/modify/delete user -description: - - This module can be used to create a user and apply a role, - update a user and delete a user. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - type: str - required: false - state: - description: - - State the action to perform. Use C(present) to create user and - C(absent) to delete user C(update) to update user. - type: str - required: true - choices: ['present', 'absent', 'update'] - pn_scope: - description: - - local or fabric. - type: str - choices: ['local', 'fabric'] - pn_initial_role: - description: - - initial role for user. - type: str - pn_password: - description: - - plain text password. - type: str - pn_name: - description: - - username. - type: str -''' - -EXAMPLES = """ -- name: Create user - pn_user: - pn_cliswitch: "sw01" - state: "present" - pn_scope: "fabric" - pn_password: "foo123" - pn_name: "foo" - -- name: Delete user - pn_user: - pn_cliswitch: "sw01" - state: "absent" - pn_name: "foo" - -- name: Modify user - pn_user: - pn_cliswitch: "sw01" - state: "update" - pn_password: "test1234" - pn_name: "foo" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the user command. - returned: always - type: list -stderr: - description: set of error responses from the user command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the user-show command. - If a user already exists on the given switch, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - - cli += ' user-show format name no-show-headers' - out = run_commands(module, cli)[1] - - out = out.split() - - return True if name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='user-create', - absent='user-delete', - update='user-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_scope=dict(required=False, type='str', - choices=['local', 'fabric']), - pn_initial_role=dict(required=False, type='str'), - pn_password=dict(required=False, type='str', no_log=True), - pn_name=dict(required=False, type='str'), - ), - required_if=( - ["state", "present", ["pn_name", "pn_scope"]], - ["state", "absent", ["pn_name"]], - ["state", "update", ["pn_name", "pn_password"]] - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - scope = module.params['pn_scope'] - initial_role = module.params['pn_initial_role'] - password = module.params['pn_password'] - name = module.params['pn_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - USER_EXISTS = check_cli(module, cli) - cli += ' %s name %s ' % (command, name) - - if command == 'user-modify': - if USER_EXISTS is False: - module.fail_json( - failed=True, - msg='User with name %s does not exist' % name - ) - if initial_role or scope: - module.fail_json( - failed=True, - msg='Only password can be modified' - ) - - if command == 'user-delete': - if USER_EXISTS is False: - module.exit_json( - skipped=True, - msg='user with name %s does not exist' % name - ) - - if command == 'user-create': - if USER_EXISTS is True: - module.exit_json( - skipped=True, - msg='User with name %s already exists' % name - ) - if scope: - cli += ' scope ' + scope - - if initial_role: - cli += ' initial-role ' + initial_role - - if command != 'user-delete': - if password: - cli += ' password ' + password - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vflow_table_profile.py b/plugins/modules/network/netvisor/pn_vflow_table_profile.py deleted file mode 100644 index e2c941e250..0000000000 --- a/plugins/modules/network/netvisor/pn_vflow_table_profile.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vflow_table_profile -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify vflow-table-profile -description: - - This module can be used to modify a vFlow table profile. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(update) to modify - the vflow-table-profile. - required: true - type: str - choices: ['update'] - pn_profile: - description: - - type of vFlow profile. - required: false - type: str - choices: ['application', 'ipv6', 'qos'] - pn_hw_tbl: - description: - - hardware table used by vFlow. - required: false - type: str - choices: ['switch-main', 'switch-hash', 'npu-main', 'npu-hash'] - pn_enable: - description: - - enable or disable vflow profile table. - required: false - type: bool -''' - -EXAMPLES = """ -- name: Modify vflow table profile - pn_vflow_table_profile: - pn_cliswitch: 'sw01' - state: 'update' - pn_profile: 'ipv6' - pn_hw_tbl: 'switch-main' - pn_enable: true - -- name: Modify vflow table profile - pn_vflow_table_profile: - state: 'update' - pn_profile: 'qos' - pn_hw_tbl: 'switch-main' - pn_enable: false -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vflow-table-profile command. - returned: always - type: list -stderr: - description: set of error responses from the vflow-table-profile command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='vflow-table-profile-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_profile=dict(required=False, type='str', - choices=['application', 'ipv6', 'qos']), - pn_hw_tbl=dict(required=False, type='str', - choices=['switch-main', 'switch-hash', - 'npu-main', 'npu-hash']), - pn_enable=dict(required=False, type='bool'), - ), - required_if=( - ['state', 'update', ['pn_profile', 'pn_hw_tbl']], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - profile = module.params['pn_profile'] - hw_tbl = module.params['pn_hw_tbl'] - enable = module.params['pn_enable'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'vflow-table-profile-modify': - cli += ' %s ' % command - if profile: - cli += ' profile ' + profile - if hw_tbl: - cli += ' hw-tbl ' + hw_tbl - - cli += booleanArgs(enable, 'enable', 'disable') - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vlag.py b/plugins/modules/network/netvisor/pn_vlag.py deleted file mode 100644 index 996d298354..0000000000 --- a/plugins/modules/network/netvisor/pn_vlag.py +++ /dev/null @@ -1,352 +0,0 @@ -#!/usr/bin/python -""" PN CLI vlag-create/vlag-delete/vlag-modify """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_vlag -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to create/delete/modify vlag. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute vlag-create/vlag-delete/vlag-modify command. - - A virtual link aggregation group (VLAG) allows links that are physically - connected to two different Pluribus Networks devices to appear as a single - trunk to a third device. The third device can be a switch, server, or any - Ethernet device. A VLAG can provide Layer 2 multipathing, which allows you - to create redundancy by increasing bandwidth, enabling multiple parallel - paths between nodes and loadbalancing traffic where alternative paths exist. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch(es) to run this command on. - default: 'local' - state: - description: - - State the action to perform. Use 'present' to create vlag, - 'absent' to delete vlag and 'update' to modify vlag. - required: True - choices: ['present', 'absent', 'update'] - pn_name: - description: - - The C(pn_name) takes a valid name for vlag configuration. - required: true - pn_port: - description: - - Specify the local VLAG port. - - Required for vlag-create. - pn_peer_port: - description: - - Specify the peer VLAG port. - - Required for vlag-create. - pn_mode: - description: - - Specify the mode for the VLAG. Active-standby indicates one side is - active and the other side is in standby mode. Active-active indicates - that both sides of the vlag are up by default. - choices: ['active-active', 'active-standby'] - pn_peer_switch: - description: - - Specify the fabric-name of the peer switch. - pn_failover_action: - description: - - Specify the failover action as move or ignore. - choices: ['move', 'ignore'] - pn_lacp_mode: - description: - - Specify the LACP mode. - choices: ['off', 'passive', 'active'] - pn_lacp_timeout: - description: - - Specify the LACP timeout as slow(30 seconds) or fast(4 seconds). - choices: ['slow', 'fast'] - pn_lacp_fallback: - description: - - Specify the LACP fallback mode as bundles or individual. - choices: ['bundle', 'individual'] - pn_lacp_fallback_timeout: - description: - - Specify the LACP fallback timeout in seconds. The range is between 30 - and 60 seconds with a default value of 50 seconds. -''' - -EXAMPLES = """ -- name: create a VLAG - pn_vlag: - state: 'present' - pn_name: spine-to-leaf - pn_port: 'spine01-to-leaf' - pn_peer_port: 'spine02-to-leaf' - pn_peer_switch: spine02 - pn_mode: 'active-active' - -- name: delete VLAGs - pn_vlag: - state: 'absent' - pn_name: spine-to-leaf -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the vlag command. - returned: always - type: list -stderr: - description: The set of error responses from the vlag command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# AnsibleModule boilerplate -from ansible.module_utils.basic import AnsibleModule - -VLAG_EXISTS = None - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks for idempotency using the vlag-show command. - If a vlag with given vlag exists, return VLAG_EXISTS as True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: VLAG_EXISTS - """ - name = module.params['pn_name'] - - show = cli + ' vlag-show format name no-show-headers' - show = shlex.split(show) - out = module.run_command(show)[1] - - out = out.split() - # Global flags - global VLAG_EXISTS - if name in out: - VLAG_EXISTS = True - else: - VLAG_EXISTS = False - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - - cmd = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'vlag-create' - if state == 'absent': - command = 'vlag-delete' - if state == 'update': - command = 'vlag-modify' - return command - - -def main(): - """ This section is for argument parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(required=True, type='str', - choices=['present', 'absent', 'update']), - pn_name=dict(required=True, type='str'), - pn_port=dict(type='str'), - pn_peer_port=dict(type='str'), - pn_mode=dict(type='str', choices=[ - 'active-standby', 'active-active']), - pn_peer_switch=dict(type='str'), - pn_failover_action=dict(type='str', choices=['move', 'ignore']), - pn_lacp_mode=dict(type='str', choices=[ - 'off', 'passive', 'active']), - pn_lacp_timeout=dict(type='str', choices=['slow', 'fast']), - pn_lacp_fallback=dict(type='str', choices=[ - 'bundle', 'individual']), - pn_lacp_fallback_timeout=dict(type='str') - ), - required_if=( - ["state", "present", ["pn_name", "pn_port", "pn_peer_port", - "pn_peer_switch"]], - ["state", "absent", ["pn_name"]], - ["state", "update", ["pn_name"]] - ) - ) - - # Argument accessing - state = module.params['state'] - name = module.params['pn_name'] - port = module.params['pn_port'] - peer_port = module.params['pn_peer_port'] - mode = module.params['pn_mode'] - peer_switch = module.params['pn_peer_switch'] - failover_action = module.params['pn_failover_action'] - lacp_mode = module.params['pn_lacp_mode'] - lacp_timeout = module.params['pn_lacp_timeout'] - lacp_fallback = module.params['pn_lacp_fallback'] - lacp_fallback_timeout = module.params['pn_lacp_fallback_timeout'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = pn_cli(module) - - if command == 'vlag-delete': - - check_cli(module, cli) - if VLAG_EXISTS is False: - module.exit_json( - skipped=True, - msg='VLAG with name %s does not exist' % name - ) - cli += ' %s name %s ' % (command, name) - - else: - - if command == 'vlag-create': - check_cli(module, cli) - if VLAG_EXISTS is True: - module.exit_json( - skipped=True, - msg='VLAG with name %s already exists' % name - ) - cli += ' %s name %s ' % (command, name) - - if port: - cli += ' port %s peer-port %s ' % (port, peer_port) - - if mode: - cli += ' mode ' + mode - - if peer_switch: - cli += ' peer-switch ' + peer_switch - - if failover_action: - cli += ' failover-' + failover_action + '-L2 ' - - if lacp_mode: - cli += ' lacp-mode ' + lacp_mode - - if lacp_timeout: - cli += ' lacp-timeout ' + lacp_timeout - - if lacp_fallback: - cli += ' lacp-fallback ' + lacp_fallback - - if lacp_fallback_timeout: - cli += ' lacp-fallback-timeout ' + lacp_fallback_timeout - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vlan.py b/plugins/modules/network/netvisor/pn_vlan.py deleted file mode 100644 index 2c6b4bad29..0000000000 --- a/plugins/modules/network/netvisor/pn_vlan.py +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/python -""" PN CLI vlan-create/vlan-delete """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_vlan -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to create/delete a VLAN. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute vlan-create or vlan-delete command. - - VLANs are used to isolate network traffic at Layer 2.The VLAN identifiers - 0 and 4095 are reserved and cannot be used per the IEEE 802.1Q standard. - The range of configurable VLAN identifiers is 2 through 4092. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch(es) to run the cli on. - required: False - default: 'local' - state: - description: - - State the action to perform. Use 'present' to create vlan and - 'absent' to delete vlan. - required: True - choices: ['present', 'absent'] - pn_vlanid: - description: - - Specify a VLAN identifier for the VLAN. This is a value between - 2 and 4092. - required: True - pn_scope: - description: - - Specify a scope for the VLAN. - - Required for vlan-create. - choices: ['fabric', 'local'] - pn_description: - description: - - Specify a description for the VLAN. - pn_stats: - description: - - Specify if you want to collect statistics for a VLAN. Statistic - collection is enabled by default. - type: bool - pn_ports: - description: - - Specifies the switch network data port number, list of ports, or range - of ports. Port numbers must ne in the range of 1 to 64. - pn_untagged_ports: - description: - - Specifies the ports that should have untagged packets mapped to the - VLAN. Untagged packets are packets that do not contain IEEE 802.1Q VLAN - tags. -''' - -EXAMPLES = """ -- name: create a VLAN - pn_vlan: - state: 'present' - pn_vlanid: 1854 - pn_scope: fabric - -- name: delete VLANs - pn_vlan: - state: 'absent' - pn_vlanid: 1854 -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the vlan command. - returned: always - type: list -stderr: - description: The set of error responses from the vlan command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# AnsibleModule boilerplate -from ansible.module_utils.basic import AnsibleModule - -VLAN_EXISTS = None -MAX_VLAN_ID = 4092 -MIN_VLAN_ID = 2 - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks for idempotency using the vlan-show command. - If a vlan with given vlan id exists, return VLAN_EXISTS as True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: VLAN_EXISTS - """ - vlanid = module.params['pn_vlanid'] - - show = cli + \ - ' vlan-show id %s format id,scope no-show-headers' % str(vlanid) - show = shlex.split(show) - out = module.run_command(show)[1] - - out = out.split() - # Global flags - global VLAN_EXISTS - if str(vlanid) in out: - VLAN_EXISTS = True - else: - VLAN_EXISTS = False - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - - cmd = shlex.split(cli) - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'vlan-create' - if state == 'absent': - command = 'vlan-delete' - return command - - -def main(): - """ This section is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(required=True, type='str', - choices=['present', 'absent']), - pn_vlanid=dict(required=True, type='int'), - pn_scope=dict(type='str', choices=['fabric', 'local']), - pn_description=dict(type='str'), - pn_stats=dict(type='bool'), - pn_ports=dict(type='str'), - pn_untagged_ports=dict(type='str') - ), - required_if=( - ["state", "present", ["pn_vlanid", "pn_scope"]], - ["state", "absent", ["pn_vlanid"]] - ) - ) - - # Accessing the arguments - state = module.params['state'] - vlanid = module.params['pn_vlanid'] - scope = module.params['pn_scope'] - description = module.params['pn_description'] - stats = module.params['pn_stats'] - ports = module.params['pn_ports'] - untagged_ports = module.params['pn_untagged_ports'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = pn_cli(module) - - if not MIN_VLAN_ID <= vlanid <= MAX_VLAN_ID: - module.exit_json( - msg="VLAN id must be between 2 and 4092", - changed=False - ) - - if command == 'vlan-create': - - check_cli(module, cli) - if VLAN_EXISTS is True: - module.exit_json( - skipped=True, - msg='VLAN with id %s already exists' % str(vlanid) - ) - - cli += ' %s id %s scope %s ' % (command, str(vlanid), scope) - - if description: - cli += ' description ' + description - - if stats is True: - cli += ' stats ' - if stats is False: - cli += ' no-stats ' - - if ports: - cli += ' ports ' + ports - - if untagged_ports: - cli += ' untagged-ports ' + untagged_ports - - if command == 'vlan-delete': - - check_cli(module, cli) - if VLAN_EXISTS is False: - module.exit_json( - skipped=True, - msg='VLAN with id %s does not exist' % str(vlanid) - ) - - cli += ' %s id %s ' % (command, str(vlanid)) - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter.py b/plugins/modules/network/netvisor/pn_vrouter.py deleted file mode 100644 index 10e41fd5ad..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter.py +++ /dev/null @@ -1,425 +0,0 @@ -#!/usr/bin/python -""" PN CLI vrouter-create/vrouter-delete/vrouter-modify """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_vrouter -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to create/delete/modify a vrouter. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute vrouter-create, vrouter-delete, vrouter-modify command. - - Each fabric, cluster, standalone switch, or virtual network (VNET) can - provide its tenants with a virtual router (vRouter) service that forwards - traffic between networks and implements Layer 3 protocols. - - C(vrouter-create) creates a new vRouter service. - - C(vrouter-delete) deletes a vRouter service. - - C(vrouter-modify) modifies a vRouter service. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch(es) to run the CLI on. - required: False - default: 'local' - state: - description: - - State the action to perform. Use 'present' to create vrouter, - 'absent' to delete vrouter and 'update' to modify vrouter. - required: True - choices: ['present', 'absent', 'update'] - pn_name: - description: - - Specify the name of the vRouter. - required: true - pn_vnet: - description: - - Specify the name of the VNET. - - Required for vrouter-create. - pn_service_type: - description: - - Specify if the vRouter is a dedicated or shared VNET service. - choices: ['dedicated', 'shared'] - pn_service_state: - description: - - Specify to enable or disable vRouter service. - choices: ['enable', 'disable'] - pn_router_type: - description: - - Specify if the vRouter uses software or hardware. - - Note that if you specify hardware as router type, you cannot assign IP - addresses using DHCP. You must specify a static IP address. - choices: ['hardware', 'software'] - pn_hw_vrrp_id: - description: - - Specifies the VRRP ID for a hardware vrouter. - pn_router_id: - description: - - Specify the vRouter IP address. - pn_bgp_as: - description: - - Specify the Autonomous System Number(ASN) if the vRouter runs Border - Gateway Protocol(BGP). - pn_bgp_redistribute: - description: - - Specify how BGP routes are redistributed. - choices: ['static', 'connected', 'rip', 'ospf'] - pn_bgp_max_paths: - description: - - Specify the maximum number of paths for BGP. This is a number between - 1 and 255 or 0 to unset. - pn_bgp_options: - description: - - Specify other BGP options as a whitespaces separated string within - single quotes ''. - pn_rip_redistribute: - description: - - Specify how RIP routes are redistributed. - choices: ['static', 'connected', 'ospf', 'bgp'] - pn_ospf_redistribute: - description: - - Specify how OSPF routes are redistributed. - choices: ['static', 'connected', 'bgp', 'rip'] - pn_ospf_options: - description: - - Specify other OSPF options as a whitespaces separated string within - single quotes ''. - pn_vrrp_track_port: - description: - - Specify list of ports and port ranges. -''' - -EXAMPLES = """ -- name: create vrouter - pn_vrouter: - state: 'present' - pn_name: 'ansible-vrouter' - pn_vnet: 'ansible-fab-global' - pn_router_id: 208.74.182.1 - -- name: delete vrouter - pn_vrouter: - state: 'absent' - pn_name: 'ansible-vrouter' -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the vrouter command. - returned: always - type: list -stderr: - description: The set of error responses from the vrouter command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# AnsibleModule boilerplate -from ansible.module_utils.basic import AnsibleModule - -VROUTER_EXISTS = None -VROUTER_NAME_EXISTS = None - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks for idempotency using the vlan-show command. - A switch can have only one vRouter configuration. - If a vRouter already exists on the given switch, return VROUTER_EXISTS as - True else False. - If a vRouter with the given name exists(on a different switch), return - VROUTER_NAME_EXISTS as True else False. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: VROUTER_EXISTS, VROUTER_NAME_EXISTS - """ - name = module.params['pn_name'] - # Global flags - global VROUTER_EXISTS, VROUTER_NAME_EXISTS - - # Get the name of the local switch - location = cli + ' switch-setup-show format switch-name' - location = shlex.split(location) - out = module.run_command(location)[1] - location = out.split()[1] - - # Check for any vRouters on the switch - check_vrouter = cli + ' vrouter-show location %s ' % location - check_vrouter += 'format name no-show-headers' - check_vrouter = shlex.split(check_vrouter) - out = module.run_command(check_vrouter)[1] - - if out: - VROUTER_EXISTS = True - else: - VROUTER_EXISTS = False - - # Check for any vRouters with the given name - show = cli + ' vrouter-show format name no-show-headers ' - show = shlex.split(show) - out = module.run_command(show)[1] - out = out.split() - - if name in out: - VROUTER_NAME_EXISTS = True - else: - VROUTER_NAME_EXISTS = False - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - - cmd = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'vrouter-create' - if state == 'absent': - command = 'vrouter-delete' - if state == 'update': - command = 'vrouter-modify' - return command - - -def main(): - """ This section is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(required=True, type='str', - choices=['present', 'absent', 'update']), - pn_name=dict(required=True, type='str'), - pn_vnet=dict(type='str'), - pn_service_type=dict(type='str', choices=['dedicated', 'shared']), - pn_service_state=dict(type='str', choices=['enable', 'disable']), - pn_router_type=dict(type='str', choices=['hardware', 'software']), - pn_hw_vrrp_id=dict(type='int'), - pn_router_id=dict(type='str'), - pn_bgp_as=dict(type='int'), - pn_bgp_redistribute=dict(type='str', choices=['static', 'connected', - 'rip', 'ospf']), - pn_bgp_max_paths=dict(type='int'), - pn_bgp_options=dict(type='str'), - pn_rip_redistribute=dict(type='str', choices=['static', 'connected', - 'bgp', 'ospf']), - pn_ospf_redistribute=dict(type='str', choices=['static', 'connected', - 'bgp', 'rip']), - pn_ospf_options=dict(type='str'), - pn_vrrp_track_port=dict(type='str') - ), - required_if=( - ["state", "present", ["pn_name", "pn_vnet"]], - ["state", "absent", ["pn_name"]], - ["state", "update", ["pn_name"]] - ) - ) - - # Accessing the arguments - state = module.params['state'] - name = module.params['pn_name'] - vnet = module.params['pn_vnet'] - service_type = module.params['pn_service_type'] - service_state = module.params['pn_service_state'] - router_type = module.params['pn_router_type'] - hw_vrrp_id = module.params['pn_hw_vrrp_id'] - router_id = module.params['pn_router_id'] - bgp_as = module.params['pn_bgp_as'] - bgp_redistribute = module.params['pn_bgp_redistribute'] - bgp_max_paths = module.params['pn_bgp_max_paths'] - bgp_options = module.params['pn_bgp_options'] - rip_redistribute = module.params['pn_rip_redistribute'] - ospf_redistribute = module.params['pn_ospf_redistribute'] - ospf_options = module.params['pn_ospf_options'] - vrrp_track_port = module.params['pn_vrrp_track_port'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = pn_cli(module) - - if command == 'vrouter-delete': - check_cli(module, cli) - if VROUTER_NAME_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter with name %s does not exist' % name - ) - cli += ' %s name %s ' % (command, name) - - else: - - if command == 'vrouter-create': - check_cli(module, cli) - if VROUTER_EXISTS is True: - module.exit_json( - skipped=True, - msg='Maximum number of vRouters has been reached on this ' - 'switch' - ) - if VROUTER_NAME_EXISTS is True: - module.exit_json( - skipped=True, - msg='vRouter with name %s already exists' % name - ) - cli += ' %s name %s ' % (command, name) - - if vnet: - cli += ' vnet ' + vnet - - if service_type: - cli += ' %s-vnet-service ' % service_type - - if service_state: - cli += ' ' + service_state - - if router_type: - cli += ' router-type ' + router_type - - if hw_vrrp_id: - cli += ' hw-vrrp-id ' + str(hw_vrrp_id) - - if router_id: - cli += ' router-id ' + router_id - - if bgp_as: - cli += ' bgp-as ' + str(bgp_as) - - if bgp_redistribute: - cli += ' bgp-redistribute ' + bgp_redistribute - - if bgp_max_paths: - cli += ' bgp-max-paths ' + str(bgp_max_paths) - - if bgp_options: - cli += ' %s ' % bgp_options - - if rip_redistribute: - cli += ' rip-redistribute ' + rip_redistribute - - if ospf_redistribute: - cli += ' ospf-redistribute ' + ospf_redistribute - - if ospf_options: - cli += ' %s ' % ospf_options - - if vrrp_track_port: - cli += ' vrrp-track-port ' + vrrp_track_port - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter_bgp.py b/plugins/modules/network/netvisor/pn_vrouter_bgp.py deleted file mode 100644 index 40928ad43e..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter_bgp.py +++ /dev/null @@ -1,472 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vrouter_bgp -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/modify/remove vrouter-bgp -description: - - This module can be used to add Border Gateway Protocol neighbor to a vRouter - modify Border Gateway Protocol neighbor to a vRouter and remove Border Gateway Protocol - neighbor from a vRouter. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - vrouter-bgp configuration command. - required: false - type: str - choices: ['present', 'absent', 'update'] - default: 'present' - pn_neighbor: - description: - - IP address for BGP neighbor. - required: true - type: str - pn_vrouter_name: - description: - - name of service config. - required: true - type: str - pn_send_community: - description: - - send any community attribute to neighbor. - required: false - type: bool - pn_weight: - description: - - default weight value between 0 and 65535 for the neighbor's routes. - required: false - pn_multi_protocol: - description: - - Multi-protocol features. - required: false - choices: ['ipv4-unicast', 'ipv6-unicast'] - pn_prefix_list_in: - description: - - prefixes used for filtering. - required: false - type: str - pn_route_reflector_client: - description: - - set as route reflector client. - required: false - type: bool - pn_default_originate: - description: - - announce default routes to the neighbor or not. - required: false - type: bool - pn_neighbor_holdtime: - description: - - BGP Holdtime (seconds). - required: false - type: str - pn_connect_retry_interval: - description: - - BGP Connect retry interval (seconds). - required: false - type: str - pn_advertisement_interval: - description: - - Minimum interval between sending BGP routing updates. - required: false - type: str - pn_route_map_out: - description: - - route map out for nbr. - required: false - type: str - pn_update_source: - description: - - IP address of BGP packets required for peering over loopback interface. - required: false - type: str - pn_bfd: - description: - - BFD protocol support for fault detection. - required: false - type: bool - default: False - pn_next_hop_self: - description: - - BGP next hop is self or not. - required: false - type: bool - pn_allowas_in: - description: - - Allow/reject routes with local AS in AS_PATH. - required: false - type: bool - pn_neighbor_keepalive_interval: - description: - - BGP Keepalive interval (seconds). - required: false - type: str - pn_max_prefix: - description: - - maximum number of prefixes. - required: false - type: str - pn_bfd_multihop: - description: - - always use BFD multi-hop port for fault detection. - required: false - type: bool - pn_interface: - description: - - Interface to reach the neighbor. - required: false - type: str - pn_password: - description: - - password for MD5 BGP. - required: false - type: str - pn_route_map_in: - description: - - route map in for nbr. - required: false - type: str - pn_soft_reconfig_inbound: - description: - - soft reset to reconfigure inbound traffic. - required: false - type: bool - pn_override_capability: - description: - - override capability. - required: false - type: bool - pn_max_prefix_warn_only: - description: - - warn if the maximum number of prefixes is exceeded. - required: false - type: bool - pn_ebgp_multihop: - description: - - value for external BGP from 1 to 255. - required: false - type: str - pn_remote_as: - description: - - BGP remote AS from 1 to 4294967295. - required: false - type: str - pn_prefix_list_out: - description: - - prefixes used for filtering outgoing packets. - required: false - type: str - pn_no_route_map_out: - description: - - Remove egress route-map from BGP neighbor. - required: false - type: str - pn_no_route_map_in: - description: - - Remove ingress route-map from BGP neighbor. - required: false - type: str -''' - -EXAMPLES = """ -- name: "Add BGP to vRouter" - pn_vrouter_bgp: - state: 'present' - pn_vrouter_name: 'sw01-vrouter' - pn_neighbor: '105.104.104.1' - pn_remote_as: 65000 - pn_bfd: true - -- name: "Remove BGP to vRouter" - pn_vrouter_bgp: - state: 'absent' - pn_vrouter_name: 'sw01-vrouter' - pn_neighbor: '105.104.104.1' - -- name: "Modify BGP to vRouter" - pn_vrouter_bgp: - state: 'update' - pn_vrouter_name: 'sw01-vrouter' - pn_neighbor: '105.104.104.1' - pn_remote_as: 65000 - pn_bfd: false - pn_allowas_in: true -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vrouter-bgp command. - returned: always - type: list -stderr: - description: set of error responses from the vrouter-bgp command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli, booleanArgs -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def is_valid(module, param_name, param_val, min_val, max_val): - if int(param_val) < min_val or int(param_val) > max_val: - module.fail_json( - failed=True, - msg='Valid %s range is %s to %s' % (param_name, min_val, max_val) - ) - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the vrouter-bgp-show command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - If the given neighbor exists on the given vRouter, return NEIGHBOR_EXISTS as True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Booleans: VROUTER_EXISTS, NEIGHBOR_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - neighbor = module.params['pn_neighbor'] - - # Check for vRouter - check_vrouter = cli + ' vrouter-show format name no-show-headers' - out = run_commands(module, check_vrouter)[1] - if out: - out = out.split() - - VROUTER_EXISTS = True if vrouter_name in out else False - - if neighbor: - # Check for BGP neighbor - show = cli + ' vrouter-bgp-show vrouter-name %s ' % vrouter_name - show += 'format neighbor no-show-headers' - out = run_commands(module, show)[1] - - if out and neighbor in out.split(): - NEIGHBOR_EXISTS = True - else: - NEIGHBOR_EXISTS = False - - return VROUTER_EXISTS, NEIGHBOR_EXISTS - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='vrouter-bgp-add', - absent='vrouter-bgp-remove', - update='vrouter-bgp-modify' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', choices=state_map.keys(), default='present'), - pn_neighbor=dict(required=True, type='str'), - pn_vrouter_name=dict(required=True, type='str'), - pn_send_community=dict(required=False, type='bool'), - pn_weight=dict(required=False, type='str'), - pn_multi_protocol=dict(required=False, type='str', choices=['ipv4-unicast', 'ipv6-unicast']), - pn_prefix_list_in=dict(required=False, type='str'), - pn_route_reflector_client=dict(required=False, type='bool'), - pn_default_originate=dict(required=False, type='bool'), - pn_neighbor_holdtime=dict(required=False, type='str'), - pn_connect_retry_interval=dict(required=False, type='str'), - pn_advertisement_interval=dict(required=False, type='str'), - pn_route_map_out=dict(required=False, type='str'), - pn_update_source=dict(required=False, type='str'), - pn_bfd=dict(required=False, type='bool', default=False), - pn_next_hop_self=dict(required=False, type='bool'), - pn_allowas_in=dict(required=False, type='bool'), - pn_neighbor_keepalive_interval=dict(required=False, type='str'), - pn_max_prefix=dict(required=False, type='str'), - pn_bfd_multihop=dict(required=False, type='bool'), - pn_interface=dict(required=False, type='str'), - pn_password=dict(required=False, type='str', no_log=True), - pn_route_map_in=dict(required=False, type='str'), - pn_soft_reconfig_inbound=dict(required=False, type='bool'), - pn_override_capability=dict(required=False, type='bool'), - pn_max_prefix_warn_only=dict(required=False, type='bool'), - pn_ebgp_multihop=dict(required=False, type='str'), - pn_remote_as=dict(required=False, type='str'), - pn_prefix_list_out=dict(required=False, type='str'), - pn_no_route_map_out=dict(required=False, type='str'), - pn_no_route_map_in=dict(required=False, type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=( - ["state", "present", ["pn_vrouter_name", "pn_neighbor", "pn_remote_as"]], - ["state", "absent", ["pn_vrouter_name", "pn_neighbor"]], - ["state", "update", ["pn_vrouter_name", "pn_neighbor"]] - ), - required_one_of=[['pn_send_community', 'pn_weight', 'pn_multi_protocol', - 'pn_prefix_list_in', 'pn_route_reflector_client', 'pn_default_originate', - 'pn_neighbor_holdtime', 'pn_connect_retry_interval', 'pn_advertisement_interval', - 'pn_route_map_out', 'pn_update_source', 'pn_bfd', - 'pn_next_hop_self', 'pn_allowas_in', 'pn_neighbor_keepalive_interval', - 'pn_max_prefix', 'pn_bfd_multihop', 'pn_interface', - 'pn_password', 'pn_route_map_in', 'pn_soft_reconfig_inbound', - 'pn_override_capability', 'pn_max_prefix_warn_only', 'pn_ebgp_multihop', - 'pn_remote_as', 'pn_prefix_list_out', 'pn_no_route_map_out', - 'pn_no_route_map_in']], - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - neighbor = module.params['pn_neighbor'] - vrouter_name = module.params['pn_vrouter_name'] - send_community = module.params['pn_send_community'] - weight = module.params['pn_weight'] - multi_protocol = module.params['pn_multi_protocol'] - prefix_list_in = module.params['pn_prefix_list_in'] - route_reflector_client = module.params['pn_route_reflector_client'] - default_originate = module.params['pn_default_originate'] - neighbor_holdtime = module.params['pn_neighbor_holdtime'] - connect_retry_interval = module.params['pn_connect_retry_interval'] - advertisement_interval = module.params['pn_advertisement_interval'] - route_map_out = module.params['pn_route_map_out'] - update_source = module.params['pn_update_source'] - bfd = module.params['pn_bfd'] - next_hop_self = module.params['pn_next_hop_self'] - allowas_in = module.params['pn_allowas_in'] - neighbor_keepalive_interval = module.params['pn_neighbor_keepalive_interval'] - max_prefix = module.params['pn_max_prefix'] - bfd_multihop = module.params['pn_bfd_multihop'] - interface = module.params['pn_interface'] - password = module.params['pn_password'] - route_map_in = module.params['pn_route_map_in'] - soft_reconfig_inbound = module.params['pn_soft_reconfig_inbound'] - override_capability = module.params['pn_override_capability'] - max_prefix_warn_only = module.params['pn_max_prefix_warn_only'] - ebgp_multihop = module.params['pn_ebgp_multihop'] - remote_as = module.params['pn_remote_as'] - prefix_list_out = module.params['pn_prefix_list_out'] - no_route_map_out = module.params['pn_no_route_map_out'] - no_route_map_in = module.params['pn_no_route_map_in'] - - command = state_map[state] - - if weight and weight != 'none': - if int(weight) < 1 or int(weight) > 65535: - module.fail_json( - failed=True, - msg='Valid weight range is 1 to 65535' - ) - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - VROUTER_EXISTS, NEIGHBOR_EXISTS = check_cli(module, cli) - - if state: - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - - if command == 'vrouter-bgp-remove' or command == 'vrouter-bgp-modify': - if NEIGHBOR_EXISTS is False: - module.exit_json( - skipped=True, - msg='BGP neighbor with IP %s does not exist on %s' % (neighbor, vrouter_name) - ) - - if command == 'vrouter-bgp-add': - if NEIGHBOR_EXISTS is True: - module.exit_json( - skipped=True, - msg='BGP neighbor with IP %s already exists on %s' % (neighbor, vrouter_name) - ) - - cli += ' %s vrouter-name %s neighbor %s ' % (command, vrouter_name, neighbor) - - if command == 'vrouter-bgp-add' or command == 'vrouter-bgp-modify': - if weight: - cli += ' weight ' + weight - if multi_protocol: - cli += ' multi-protocol ' + multi_protocol - if prefix_list_in: - cli += ' prefix-list-in ' + prefix_list_in - if neighbor_holdtime: - is_valid(module, 'neighbor holdtime', neighbor_holdtime, '0', '65535') - cli += ' neighbor-holdtime ' + neighbor_holdtime - if connect_retry_interval: - is_valid(module, 'connect retry interval', connect_retry_interval, '0', '65535') - cli += ' connect-retry-interval ' + connect_retry_interval - if advertisement_interval: - is_valid(module, 'advertisement interval', advertisement_interval, '0', '65535') - cli += ' advertisement-interval ' + advertisement_interval - if route_map_out: - cli += ' route-map-out ' + route_map_out - if update_source: - cli += ' update-source ' + update_source - if neighbor_keepalive_interval: - is_valid(module, 'neighbor keepalive interval', neighbor_keepalive_interval, '0', '65535') - cli += ' neighbor-keepalive-interval ' + neighbor_keepalive_interval - if max_prefix: - cli += ' max-prefix ' + max_prefix - if interface: - cli += ' interface ' + interface - if password: - cli += ' password ' + password - if route_map_in: - cli += ' route-map-in ' + route_map_in - if ebgp_multihop: - is_valid(module, 'ebgp_multihop', ebgp_multihop, '1', '255') - cli += ' ebgp-multihop ' + ebgp_multihop - if remote_as: - cli += ' remote-as ' + remote_as - if prefix_list_out: - cli += ' prefix-list-out ' + prefix_list_out - cli += booleanArgs(send_community, 'send-community', 'no-send-community') - cli += booleanArgs(route_reflector_client, 'route-reflector-client', 'no-route-reflector-client') - cli += booleanArgs(default_originate, 'default-originate', 'no-default-originate') - cli += booleanArgs(bfd, 'bfd', 'no-bfd') - cli += booleanArgs(next_hop_self, 'next-hop-self', 'no-next-hop-self') - cli += booleanArgs(allowas_in, 'allowas-in', 'no-allowas-in') - cli += booleanArgs(bfd_multihop, 'bfd-multihop', 'no-bfd-multihop') - cli += booleanArgs(soft_reconfig_inbound, 'soft-reconfig-inbound', 'no-soft-reconfig-inbound') - cli += booleanArgs(override_capability, 'override-capability', 'no-override-capability') - cli += booleanArgs(max_prefix_warn_only, 'max-prefix-warn-only', 'no-max-prefix-warn-only') - - if command == 'vrouter-bgp-modify': - if no_route_map_out: - cli += ' no-route-map-out ' + no_route_map_out - if no_route_map_in: - cli += ' no-route-map-in ' + no_route_map_in - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter_bgp_network.py b/plugins/modules/network/netvisor/pn_vrouter_bgp_network.py deleted file mode 100644 index 035c8ff271..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter_bgp_network.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vrouter_bgp_network -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove vrouter-bgp-network -description: - - This module can be used to add Border Gateway Protocol network to a vRouter - and remove Border Gateway Protocol network from a vRouter. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(present) to add bgp network and - C(absent) to remove bgp network. - required: true - type: str - choices: ['present', 'absent'] - pn_netmask: - description: - - BGP network mask. - required: false - type: str - pn_network: - description: - - IP address for BGP network. - required: false - type: str - pn_vrouter_name: - description: - - name of service config. - required: false - type: str -''' - -EXAMPLES = """ -- name: Add network to bgp - pn_vrouter_bgp_network: - pn_cliswitch: "sw01" - state: "present" - pn_vrouter_name: "foo-vrouter" - pn_network: '10.10.10.10' - pn_netmask: '31' - -- name: Remove network from bgp - pn_vrouter_bgp_network: - pn_cliswitch: "sw01" - state: "absent" - pn_vrouter_name: "foo-vrouter" - pn_network: '10.10.10.10' -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vrouter-bgp-network command. - returned: always - type: list -stderr: - description: set of error responses from the vrouter-bgp-network command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for pim ssm config using the vrouter-show command. - If a user already exists on the given switch, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_vrouter_name'] - network = module.params['pn_network'] - - show = cli - cli += ' vrouter-show name %s format name no-show-headers' % name - rc, out, err = run_commands(module, cli) - VROUTER_EXISTS = '' if out else None - - cli = show - cli += ' vrouter-bgp-network-show vrouter-name %s network %s format network no-show-headers' % (name, network) - out = run_commands(module, cli)[1] - out = out.split() - NETWORK_EXISTS = True if network in out[-1] else False - - return NETWORK_EXISTS, VROUTER_EXISTS - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='vrouter-bgp-network-add', - absent='vrouter-bgp-network-remove' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_netmask=dict(required=False, type='str'), - pn_network=dict(required=False, type='str'), - pn_vrouter_name=dict(required=False, type='str'), - ), - required_if=( - ['state', 'present', ['pn_vrouter_name', 'pn_netmask', 'pn_network']], - ['state', 'absent', ['pn_vrouter_name', 'pn_network']], - ), - ) - - # Accessing the arguments - state = module.params['state'] - cliswitch = module.params['pn_cliswitch'] - netmask = module.params['pn_netmask'] - network = module.params['pn_network'] - vrouter_name = module.params['pn_vrouter_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NETWORK_EXISTS, VROUTER_EXISTS = check_cli(module, cli) - - if VROUTER_EXISTS is None: - module.fail_json( - failed=True, - msg='vRouter %s does not exists' % vrouter_name - ) - - if command == 'vrouter-bgp-network-add': - if NETWORK_EXISTS is True: - module.exit_json( - skipped=True, - msg='Network %s already added to bgp' % network - ) - - if command == 'vrouter-bgp-network-remove': - if NETWORK_EXISTS is False: - module.exit_json( - skipped=True, - msg='Network %s does not exists' % network - ) - - cli += ' %s vrouter-name %s ' % (command, vrouter_name) - - if netmask: - cli += ' netmask ' + netmask - if network: - cli += ' network ' + network - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter_interface_ip.py b/plugins/modules/network/netvisor/pn_vrouter_interface_ip.py deleted file mode 100644 index 2fe4929549..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter_interface_ip.py +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vrouter_interface_ip -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove vrouter-interface-ip -description: - - This module can be used to add an IP address on interface from a vRouter - or remove an IP address on interface from a vRouter. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(present) to addvrouter-interface-ip - and C(absent) to remove vrouter-interface-ip. - required: true - type: str - choices: ['present', 'absent'] - pn_bd: - description: - - interface Bridge Domain. - required: false - type: str - pn_netmask: - description: - - netmask. - required: false - type: str - pn_vnet: - description: - - interface VLAN VNET. - required: false - type: str - pn_ip: - description: - - IP address. - required: false - type: str - pn_nic: - description: - - virtual NIC assigned to interface. - required: false - type: str - pn_vrouter_name: - description: - - name of service config. - required: false - type: str -''' - -EXAMPLES = """ -- name: Add vrouter interface to nic - pn_vrouter_interface_ip: - state: "present" - pn_cliswitch: "sw01" - pn_vrouter_name: "foo-vrouter" - pn_ip: "2620:0:1651:1::30" - pn_netmask: "127" - pn_nic: "eth0.4092" - -- name: Remove vrouter interface to nic - pn_vrouter_interface_ip: - state: "absent" - pn_cliswitch: "sw01" - pn_vrouter_name: "foo-vrouter" - pn_ip: "2620:0:1651:1::30" - pn_nic: "eth0.4092" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vrouter-interface-ip command. - returned: always - type: list -stderr: - description: set of error responses from the vrouter-interface-ip command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the vrouter-interface-show - command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - - If an interface with the given ip exists on the given vRouter, - return INTERFACE_EXISTS as True else False. This is required for - vrouter-interface-add. - - If nic_str exists on the given vRouter, return NIC_EXISTS as True else - False. This is required for vrouter-interface-remove. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Booleans: VROUTER_EXISTS, INTERFACE_EXISTS, NIC_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - interface_ip = module.params['pn_ip'] - nic_str = module.params['pn_nic'] - - # Check for vRouter - check_vrouter = cli + ' vrouter-show format name no-show-headers' - out = run_commands(module, check_vrouter)[1] - if out: - out = out.split() - - VROUTER_EXISTS = True if vrouter_name in out else False - - if interface_ip: - # Check for interface and VRRP and fetch nic for VRRP - show = cli + ' vrouter-interface-show vrouter-name %s ' % vrouter_name - show += 'ip2 %s format ip2,nic no-show-headers' % interface_ip - out = run_commands(module, show)[1] - - if out and interface_ip in out.split(' ')[-2]: - INTERFACE_EXISTS = True - else: - INTERFACE_EXISTS = False - - if nic_str: - # Check for nic - show = cli + ' vrouter-interface-show vrouter-name %s ' % vrouter_name - show += 'format nic no-show-headers' - out = run_commands(module, show)[1] - - if out: - out = out.split() - - NIC_EXISTS = True if nic_str in out else False - - return VROUTER_EXISTS, INTERFACE_EXISTS, NIC_EXISTS - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='vrouter-interface-ip-add', - absent='vrouter-interface-ip-remove' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_bd=dict(required=False, type='str'), - pn_netmask=dict(required=False, type='str'), - pn_vnet=dict(required=False, type='str'), - pn_ip=dict(required=False, type='str'), - pn_nic=dict(required=False, type='str'), - pn_vrouter_name=dict(required=False, type='str'), - ), - required_if=( - ["state", "present", ["pn_vrouter_name", "pn_nic", "pn_ip", "pn_netmask"]], - ["state", "absent", ["pn_vrouter_name", "pn_nic", "pn_ip"]] - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - bd = module.params['pn_bd'] - netmask = module.params['pn_netmask'] - vnet = module.params['pn_vnet'] - ip = module.params['pn_ip'] - nic = module.params['pn_nic'] - vrouter_name = module.params['pn_vrouter_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - VROUTER_EXISTS, INTERFACE_EXISTS, NIC_EXISTS = check_cli(module, cli) - - if VROUTER_EXISTS is False: - module.fail_json( - failed=True, - msg='vRouter %s does not exist' % vrouter_name - ) - - if NIC_EXISTS is False: - module.fail_json( - failed=True, - msg='vRouter with nic %s does not exist' % nic - ) - - cli += ' %s vrouter-name %s ' % (command, vrouter_name) - - if command == 'vrouter-interface-ip-add': - if INTERFACE_EXISTS is True: - module.exit_json( - skipped=True, - msg='vRouter with interface ip %s exist' % ip - ) - cli += ' nic %s ip %s ' % (nic, ip) - - if bd: - cli += ' bd ' + bd - if netmask: - cli += ' netmask ' + netmask - if vnet: - cli += ' vnet ' + vnet - - if command == 'vrouter-interface-ip-remove': - if INTERFACE_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter with interface ip %s does not exist' % ip - ) - if nic: - cli += ' nic %s ' % nic - if ip: - cli += ' ip %s ' % ip.split('/')[0] - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter_loopback_interface.py b/plugins/modules/network/netvisor/pn_vrouter_loopback_interface.py deleted file mode 100644 index f9c2e02448..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter_loopback_interface.py +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vrouter_loopback_interface -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove vrouter-loopback-interface -description: - - This module can be used to add loopback interface to a vRouter or - remove loopback interface from a vRouter. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(present) to add vrouter-loopback-interface - and C(absent) to remove vrouter-loopback-interface. - required: false - type: str - choices: ['present', 'absent'] - default: 'present' - pn_ip: - description: - - loopback IP address. - required: true - type: str - pn_index: - description: - - loopback index from 1 to 255. - required: false - type: str - pn_vrouter_name: - description: - - name of service config. - required: true - type: str -''' - -EXAMPLES = """ -- name: Add vrouter loopback interface - pn_vrouter_loopback_interface: - state: "present" - pn_cliswitch: "sw01" - pn_vrouter_name: "sw01-vrouter" - pn_ip: "192.168.10.1" - -- name: Remove vrouter loopback interface - pn_vrouter_loopback_interface: - state: "absent" - pn_cliswitch: "sw01" - pn_vrouter_name: "sw01-vrouter" - pn_ip: "192.168.10.1" - pn_index: "2" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vrouter-loopback-interface command. - returned: always - type: list -stderr: - description: set of error response from the vrouter-loopback-interface - command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the vrouter-interface-show - command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Booleans: VROUTER_EXISTS, INTERFACE_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - interface_ip = module.params['pn_ip'] - - # Check for vRouter - check_vrouter = 'vrouter-show format name no-show-headers' - out = run_commands(module, check_vrouter)[1] - if out: - out = out.split() - - VROUTER_EXISTS = True if vrouter_name in out else False - - if interface_ip: - # Check for interface and VRRP and fetch nic for VRRP - show = cli + ' vrouter-loopback-interface-show ' - show += 'vrouter-name %s ' % vrouter_name - show += 'format ip no-show-headers' - out = run_commands(module, show)[1] - - if out and interface_ip in out.split(): - INTERFACE_EXISTS = True - else: - INTERFACE_EXISTS = False - - return VROUTER_EXISTS, INTERFACE_EXISTS - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='vrouter-loopback-interface-add', - absent='vrouter-loopback-interface-remove' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', - choices=state_map.keys(), default='present'), - pn_ip=dict(required=True, type='str'), - pn_index=dict(required=False, type='str'), - pn_vrouter_name=dict(required=True, type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=( - ["state", "present", ["pn_vrouter_name", "pn_ip"]], - ["state", "absent", ["pn_vrouter_name", "pn_ip", "pn_index"]] - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - ip = module.params['pn_ip'] - index = module.params['pn_index'] - vrouter_name = module.params['pn_vrouter_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - VROUTER_EXISTS, INTERFACE_EXISTS = check_cli(module, cli) - cli += ' %s vrouter-name %s ' % (command, vrouter_name) - - if index and (int(index) < 1 or int(index) > 255): - module.fail_json( - failed=True, - msg='index should be in range 1 to 255' - ) - - if index and state == 'present': - show = 'vrouter-loopback-interface-show format index parsable-delim ,' - out = run_commands(module, show)[1] - if out: - out = out.split() - for res in out: - res = res.strip().split(',') - if index in res: - module.fail_json( - failed=True, - msg='index with value %s exist' % index - ) - - if command == 'vrouter-loopback-interface-add': - if VROUTER_EXISTS is False: - module.fail_json( - failed=True, - msg='vRouter %s does not exist' % vrouter_name - ) - if INTERFACE_EXISTS is True: - module.exit_json( - skipped=True, - msg='vRouter with loopback ip %s exist' % ip - ) - if ip: - cli += ' ip ' + ip - if index: - cli += ' index ' + index - - if command == 'vrouter-loopback-interface-remove': - if VROUTER_EXISTS is False: - module.fail_json( - failed=True, - msg='vRouter %s does not exist' % vrouter_name - ) - if INTERFACE_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter with loopback ip %s doesnt exist' % ip - ) - - if index: - cli += ' index ' + index - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter_ospf.py b/plugins/modules/network/netvisor/pn_vrouter_ospf.py deleted file mode 100644 index 5da19f97f4..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter_ospf.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vrouter_ospf -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove vrouter-ospf -description: - - This module can be used to add OSPF protocol to vRouter - and remove OSPF protocol from a vRouter -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - vrouter-ospf configuration command. - required: false - type: str - choices: ['present', 'absent'] - default: 'present' - pn_netmask: - description: - - OSPF network IP address netmask. - required: false - type: str - pn_ospf_area: - description: - - stub area number for the configuration. - required: false - type: str - pn_network: - description: - - OSPF network IP address. - required: true - type: str - pn_vrouter_name: - description: - - name of service config. - required: true - type: str -''' - -EXAMPLES = """ -- name: Add OSPF to vRouter - pn_vrouter_ospf: - state: 'present' - pn_vrouter_name: 'sw01-vrouter' - pn_network: '105.104.104.1' - pn_netmask: '24' - pn_ospf_area: '0' -- name: "Remove OSPF to vRouter" - pn_vrouter_ospf: - state: 'absent' - pn_vrouter_name: 'sw01-vrouter' - pn_network: '105.104.104.1' -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vrouter-ospf command. - returned: always - type: list -stderr: - description: set of error responses from the vrouter-ospf command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the show command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - If an OSPF network with the given ip exists on the given vRouter, - return NETWORK_EXISTS as True else False. - - :param module: The Ansible module to fetch input parameters - :return Booleans: VROUTER_EXISTS, NETWORK_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - network = module.params['pn_network'] - show_cli = pn_cli(module) - - # Check for vRouter - check_vrouter = cli + ' vrouter-show format name no-show-headers ' - out = run_commands(module, check_vrouter)[1] - if out: - out = out.split() - - VROUTER_EXISTS = True if vrouter_name in out else False - - # Check for OSPF networks - check_network = cli + ' vrouter-ospf-show vrouter-name %s ' % vrouter_name - check_network += 'format network no-show-headers' - out = run_commands(module, check_network)[1] - - if out and network in out: - NETWORK_EXISTS = True - else: - NETWORK_EXISTS = False - - return VROUTER_EXISTS, NETWORK_EXISTS - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='vrouter-ospf-add', - absent='vrouter-ospf-remove' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', choices=state_map.keys(), default='present'), - pn_netmask=dict(required=False, type='str'), - pn_ospf_area=dict(required=False, type='str'), - pn_network=dict(required=True, type='str'), - pn_vrouter_name=dict(required=True, type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=( - ["state", "present", ['pn_vrouter_name', 'pn_network', 'pn_netmask', 'pn_ospf_area']], - ["state", "absent", ['pn_vrouter_name', 'pn_network']], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - netmask = module.params['pn_netmask'] - ospf_area = module.params['pn_ospf_area'] - network = module.params['pn_network'] - vrouter_name = module.params['pn_vrouter_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - VROUTER_EXISTS, NETWORK_EXISTS = check_cli(module, cli) - - if state: - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - - if command == 'vrouter-ospf-remove': - if NETWORK_EXISTS is False: - module.exit_json( - skipped=True, - msg='OSPF with network %s dose not exists' % network - ) - cli += ' %s vrouter-name %s network %s' % (command, vrouter_name, network) - - if command == 'vrouter-ospf-add': - if NETWORK_EXISTS is True: - module.exit_json( - skipped=True, - msg='OSPF with network %s already exists' % network - ) - if netmask: - cli += ' netmask ' + netmask - if ospf_area: - cli += ' ospf-area ' + ospf_area - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter_ospf6.py b/plugins/modules/network/netvisor/pn_vrouter_ospf6.py deleted file mode 100644 index 3819ed8059..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter_ospf6.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vrouter_ospf6 -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove vrouter-ospf6 -description: - - This module can be used to add interface ip to OSPF6 protocol - or remove interface ip from OSPF6 protocol on vRouter. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(present) to add vrouter-ospf6 and - C(absent) to remove interface from vrouter-ospf6. - required: true - type: str - choices: ['present', 'absent'] - pn_ospf6_area: - description: - - area id for this interface in IPv4 address format. - required: false - type: str - pn_nic: - description: - - OSPF6 control for this interface. - required: false - type: str - pn_vrouter_name: - description: - - name of service config. - required: false - type: str -''' - -EXAMPLES = """ -- name: Add vrouter interface nic to ospf6 - pn_vrouter_ospf6: - pn_cliswitch: "sw01" - state: "present" - pn_vrouter_name: "foo-vrouter" - pn_nic: "eth0.4092" - pn_ospf6_area: "0.0.0.0" - -- name: Remove vrouter interface nic to ospf6 - pn_vrouter_ospf6: - pn_cliswitch: "sw01" - state: "absent" - pn_vrouter_name: "foo-vrouter" - pn_nic: "eth0.4092" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vrouter-ospf6 command. - returned: always - type: list -stderr: - description: set of error responses from the vrouter-ospf6 command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the vrouter-interface-show - command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - - If nic_str exists on the given vRouter, return NIC_EXISTS as True else - False. This is required for vrouter-ospf6-remove. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Booleans: VROUTER_EXISTS, NIC_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - nic_str = module.params['pn_nic'] - - # Check for vRouter - check_vrouter = cli + ' vrouter-show format name no-show-headers ' - out = run_commands(module, check_vrouter)[1] - if out: - out = out.split() - - VROUTER_EXISTS = True if vrouter_name in out else False - - if nic_str: - # Check for nic - show = cli + ' vrouter-ospf6-show vrouter-name %s format nic no-show-headers' % vrouter_name - out = run_commands(module, show)[1] - - if out: - out.split() - - NIC_EXISTS = True if nic_str in out else False - - return VROUTER_EXISTS, NIC_EXISTS - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='vrouter-ospf6-add', - absent='vrouter-ospf6-remove' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_ospf6_area=dict(required=False, type='str'), - pn_nic=dict(required=False, type='str'), - pn_vrouter_name=dict(required=False, type='str'), - ), - required_if=( - ["state", "present", ["pn_vrouter_name", "pn_nic", - "pn_ospf6_area"]], - ["state", "absent", ["pn_vrouter_name", "pn_nic"]] - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - ospf6_area = module.params['pn_ospf6_area'] - nic = module.params['pn_nic'] - vrouter_name = module.params['pn_vrouter_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - VROUTER_EXISTS, NIC_EXISTS = check_cli(module, cli) - - if VROUTER_EXISTS is False: - module.fail_json( - failed=True, - msg='vRouter %s does not exist' % vrouter_name - ) - - cli += ' %s vrouter-name %s ' % (command, vrouter_name) - - if command == 'vrouter-ospf6-add': - if NIC_EXISTS is True: - module.exit_json( - skipped=True, - msg='OSPF6 with nic %s already exist' % nic - ) - if nic: - cli += ' nic %s' % nic - if ospf6_area: - cli += ' ospf6-area %s ' % ospf6_area - - if command == 'vrouter-ospf6-remove': - if NIC_EXISTS is False: - module.exit_json( - skipped=True, - msg='OSPF6 with nic %s does not exist' % nic - ) - if nic: - cli += ' nic %s' % nic - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter_packet_relay.py b/plugins/modules/network/netvisor/pn_vrouter_packet_relay.py deleted file mode 100644 index e59ca8bb9d..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter_packet_relay.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vrouter_packet_relay -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to add/remove vrouter-packet-relay -description: - - This module can be used to add packet relay configuration for DHCP on vrouter - and remove packet relay configuration for DHCP on vrouter. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - vrouter-packet-relay configuration command. - required: false - choices: ['present', 'absent'] - type: str - default: 'present' - pn_forward_ip: - description: - - forwarding IP address. - required: true - type: str - pn_nic: - description: - - NIC. - required: true - type: str - pn_forward_proto: - description: - - protocol type to forward packets. - required: false - type: str - choices: ['dhcp'] - default: 'dhcp' - pn_vrouter_name: - description: - - name of service config. - required: true - type: str -''' - -EXAMPLES = """ -- name: vRouter packet relay add - pn_vrouter_packet_relay: - pn_cliswitch: "sw01" - pn_forward_ip: "192.168.10.1" - pn_nic: "eth0.4092" - pn_vrouter_name: "sw01-vrouter" - -- name: vRouter packet relay remove - pn_vrouter_packet_relay: - pn_cliswitch: "sw01" - state: "absent" - pn_forward_ip: "192.168.10.1" - pn_nic: "eth0.4092" - pn_vrouter_name: "sw01-vrouter" -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vrouter-packet-relay command. - returned: always - type: list -stderr: - description: set of error responses from the vrouter-packet-relay command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the vrouter-interface-show - command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - - If nic_str exists on the given vRouter, return NIC_EXISTS as True else - False. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Booleans: VROUTER_EXISTS, NIC_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - nic_str = module.params['pn_nic'] - - # Check for vRouter - check_vrouter = 'vrouter-show format name no-show-headers' - out = run_commands(module, check_vrouter)[1] - - if out: - out = out.split() - - VROUTER_EXISTS = True if vrouter_name in out else False - - if nic_str: - # Check for nic - show = 'vrouter-interface-show vrouter-name %s format nic no-show-headers' % vrouter_name - out = run_commands(module, show)[1] - if out: - out = out.split() - - NIC_EXISTS = True if nic_str in out else False - - return VROUTER_EXISTS, NIC_EXISTS - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='vrouter-packet-relay-add', - absent='vrouter-packet-relay-remove' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', choices=state_map.keys(), default='present'), - pn_forward_ip=dict(required=True, type='str'), - pn_nic=dict(required=True, type='str'), - pn_forward_proto=dict(required=False, type='str', choices=['dhcp'], default='dhcp'), - pn_vrouter_name=dict(required=True, type='str'), - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=( - ["state", "present", ["pn_vrouter_name", "pn_forward_ip", "pn_nic", "pn_forward_proto"]], - ["state", "absent", ["pn_vrouter_name", "pn_forward_ip", "pn_nic", "pn_forward_proto"]], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - forward_ip = module.params['pn_forward_ip'] - nic = module.params['pn_nic'] - forward_proto = module.params['pn_forward_proto'] - vrouter_name = module.params['pn_vrouter_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - VROUTER_EXISTS, NIC_EXISTS = check_cli(module, cli) - - if VROUTER_EXISTS is False: - module.fail_json( - failed=True, - msg='vRouter %s does not exist' % vrouter_name - ) - - if NIC_EXISTS is False: - module.fail_json( - failed=True, - msg='vRouter with nic %s does not exist' % nic - ) - - if command == 'vrouter-packet-relay-add' or command == 'vrouter-packet-relay-remove': - cli += ' %s' % command - cli += ' vrouter-name %s nic %s' % (vrouter_name, nic) - cli += ' forward-proto %s forward-ip %s' % (forward_proto, forward_ip) - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouter_pim_config.py b/plugins/modules/network/netvisor/pn_vrouter_pim_config.py deleted file mode 100644 index 3078fb5e5d..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouter_pim_config.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vrouter_pim_config -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to modify vrouter-pim-config -description: - - This module can be used to modify pim parameters. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - State the action to perform. Use C(update) to modify the vrouter-pim-config. - required: true - type: str - choices: ['update'] - pn_query_interval: - description: - - igmp query interval in seconds. - required: false - type: str - pn_querier_timeout: - description: - - igmp querier timeout in seconds. - required: false - type: str - pn_hello_interval: - description: - - hello interval in seconds. - required: false - type: str - pn_vrouter_name: - description: - - name of service config. - required: false - type: str -''' - -EXAMPLES = """ -- name: pim config modify - pn_vrouter_pim_config: - pn_cliswitch: '192.168.1.1' - pn_query_interval: '10' - pn_querier_timeout: '30' - state: 'update' - pn_vrouter_name: 'ansible-spine1-vrouter' -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vrouter-pim-config command. - returned: always - type: list -stderr: - description: set of error responses from the vrouter-pim-config command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for pim ssm config using the vrouter-show command. - If a user already exists on the given switch, return True else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_vrouter_name'] - - show = cli - cli += ' vrouter-show format name no-show-headers ' - out = run_commands(module, cli)[1] - if out: - out = out.split() - if name in out: - pass - else: - return False - - cli = show - cli += ' vrouter-show name %s format proto-multi no-show-headers' % name - out = run_commands(module, cli)[1] - if out: - out = out.split() - - return True if 'none' not in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - update='vrouter-pim-config-modify' - ) - - module = AnsibleModule( - argument_spec=dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=True, type='str', - choices=state_map.keys()), - pn_query_interval=dict(required=False, type='str'), - pn_querier_timeout=dict(required=False, type='str'), - pn_hello_interval=dict(required=False, type='str'), - pn_vrouter_name=dict(required=True, type='str'), - ), - required_if=( - ['state', 'update', ['pn_vrouter_name']], - ), - required_one_of=[['pn_query_interval', - 'pn_querier_timeout', - 'pn_hello_interval']] - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - query_interval = module.params['pn_query_interval'] - querier_timeout = module.params['pn_querier_timeout'] - hello_interval = module.params['pn_hello_interval'] - vrouter_name = module.params['pn_vrouter_name'] - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - if command == 'vrouter-pim-config-modify': - PIM_SSM_CONFIG = check_cli(module, cli) - if PIM_SSM_CONFIG is False: - module.exit_json( - skipped=True, - msg='vrouter proto-multi is not configured/vrouter is not created' - ) - cli += ' %s vrouter-name %s ' % (command, vrouter_name) - if querier_timeout: - cli += ' querier-timeout ' + querier_timeout - if hello_interval: - cli += ' hello-interval ' + hello_interval - if query_interval: - cli += ' query-interval ' + query_interval - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouterbgp.py b/plugins/modules/network/netvisor/pn_vrouterbgp.py deleted file mode 100644 index 447b85e7ac..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouterbgp.py +++ /dev/null @@ -1,487 +0,0 @@ -#!/usr/bin/python -""" PN-CLI vrouter-bgp-add/vrouter-bgp-remove/vrouter-bgp-modify """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_vrouterbgp -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to add/remove/modify vrouter-bgp. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute vrouter-bgp-add, vrouter-bgp-remove, vrouter-bgp-modify command. - - Each fabric, cluster, standalone switch, or virtual network (VNET) can - provide its tenants with a vRouter service that forwards traffic between - networks and implements Layer 4 protocols. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch(es) to run the cli on. - required: False - default: 'local' - state: - description: - - State the action to perform. Use 'present' to add bgp, - 'absent' to remove bgp and 'update' to modify bgp. - required: True - choices: ['present', 'absent', 'update'] - pn_vrouter_name: - description: - - Specify a name for the vRouter service. - required: True - pn_neighbor: - description: - - Specify a neighbor IP address to use for BGP. - - Required for vrouter-bgp-add. - pn_remote_as: - description: - - Specify the remote Autonomous System(AS) number. This value is between - 1 and 4294967295. - - Required for vrouter-bgp-add. - pn_next_hop_self: - description: - - Specify if the next-hop is the same router or not. - type: bool - pn_password: - description: - - Specify a password, if desired. - pn_ebgp: - description: - - Specify a value for external BGP to accept or attempt BGP connections - to external peers, not directly connected, on the network. This is a - value between 1 and 255. - pn_prefix_listin: - description: - - Specify the prefix list to filter traffic inbound. - pn_prefix_listout: - description: - - Specify the prefix list to filter traffic outbound. - pn_route_reflector: - description: - - Specify if a route reflector client is used. - type: bool - pn_override_capability: - description: - - Specify if you want to override capability. - type: bool - pn_soft_reconfig: - description: - - Specify if you want a soft reconfiguration of inbound traffic. - type: bool - pn_max_prefix: - description: - - Specify the maximum number of prefixes. - pn_max_prefix_warn: - description: - - Specify if you want a warning message when the maximum number of - prefixes is exceeded. - type: bool - pn_bfd: - description: - - Specify if you want BFD protocol support for fault detection. - type: bool - pn_multiprotocol: - description: - - Specify a multi-protocol for BGP. - choices: ['ipv4-unicast', 'ipv6-unicast'] - pn_weight: - description: - - Specify a default weight value between 0 and 65535 for the neighbor - routes. - pn_default_originate: - description: - - Specify if you want announce default routes to the neighbor or not. - type: bool - pn_keepalive: - description: - - Specify BGP neighbor keepalive interval in seconds. - pn_holdtime: - description: - - Specify BGP neighbor holdtime in seconds. - pn_route_mapin: - description: - - Specify inbound route map for neighbor. - pn_route_mapout: - description: - - Specify outbound route map for neighbor. -''' - -EXAMPLES = """ -- name: add vrouter-bgp - pn_vrouterbgp: - state: 'present' - pn_vrouter_name: 'ansible-vrouter' - pn_neighbor: 104.104.104.1 - pn_remote_as: 1800 - -- name: remove vrouter-bgp - pn_vrouterbgp: - state: 'absent' - pn_name: 'ansible-vrouter' -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the vrouterbpg command. - returned: always - type: list -stderr: - description: The set of error responses from the vrouterbgp command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# Ansible boiler-plate -from ansible.module_utils.basic import AnsibleModule - -VROUTER_EXISTS = None -NEIGHBOR_EXISTS = None - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the vrouter-bgp-show command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - If a BGP neighbor with the given ip exists on the given vRouter, - return NEIGHBOR_EXISTS as True else False. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: VROUTER_EXISTS, NEIGHBOR_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - neighbor = module.params['pn_neighbor'] - # Global flags - global VROUTER_EXISTS, NEIGHBOR_EXISTS - - # Check for vRouter - check_vrouter = cli + ' vrouter-show format name no-show-headers ' - check_vrouter = shlex.split(check_vrouter) - out = module.run_command(check_vrouter)[1] - out = out.split() - - if vrouter_name in out: - VROUTER_EXISTS = True - else: - VROUTER_EXISTS = False - - # Check for BGP neighbors - show = cli + ' vrouter-bgp-show vrouter-name %s ' % vrouter_name - show += 'format neighbor no-show-headers' - show = shlex.split(show) - out = module.run_command(show)[1] - out = out.split() - - if neighbor in out: - NEIGHBOR_EXISTS = True - else: - NEIGHBOR_EXISTS = False - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - - cmd = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'vrouter-bgp-add' - if state == 'absent': - command = 'vrouter-bgp-remove' - if state == 'update': - command = 'vrouter-bgp-modify' - return command - - -def main(): - """ This portion is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(required=True, type='str', - choices=['present', 'absent', 'update']), - pn_vrouter_name=dict(required=True, type='str'), - pn_neighbor=dict(type='str'), - pn_remote_as=dict(type='str'), - pn_next_hop_self=dict(type='bool'), - pn_password=dict(type='str', no_log=True), - pn_ebgp=dict(type='int'), - pn_prefix_listin=dict(type='str'), - pn_prefix_listout=dict(type='str'), - pn_route_reflector=dict(type='bool'), - pn_override_capability=dict(type='bool'), - pn_soft_reconfig=dict(type='bool'), - pn_max_prefix=dict(type='int'), - pn_max_prefix_warn=dict(type='bool'), - pn_bfd=dict(type='bool'), - pn_multiprotocol=dict(type='str', - choices=['ipv4-unicast', 'ipv6-unicast']), - pn_weight=dict(type='int'), - pn_default_originate=dict(type='bool'), - pn_keepalive=dict(type='str'), - pn_holdtime=dict(type='str'), - pn_route_mapin=dict(type='str'), - pn_route_mapout=dict(type='str') - ), - required_if=( - ["state", "present", - ["pn_vrouter_name", "pn_neighbor", "pn_remote_as"]], - ["state", "absent", - ["pn_vrouter_name", "pn_neighbor"]], - ["state", "update", - ["pn_vrouter_name", "pn_neighbor"]] - ) - ) - - # Accessing the arguments - state = module.params['state'] - vrouter_name = module.params['pn_vrouter_name'] - neighbor = module.params['pn_neighbor'] - remote_as = module.params['pn_remote_as'] - next_hop_self = module.params['pn_next_hop_self'] - password = module.params['pn_password'] - ebgp = module.params['pn_ebgp'] - prefix_listin = module.params['pn_prefix_listin'] - prefix_listout = module.params['pn_prefix_listout'] - route_reflector = module.params['pn_route_reflector'] - override_capability = module.params['pn_override_capability'] - soft_reconfig = module.params['pn_soft_reconfig'] - max_prefix = module.params['pn_max_prefix'] - max_prefix_warn = module.params['pn_max_prefix_warn'] - bfd = module.params['pn_bfd'] - multiprotocol = module.params['pn_multiprotocol'] - weight = module.params['pn_weight'] - default_originate = module.params['pn_default_originate'] - keepalive = module.params['pn_keepalive'] - holdtime = module.params['pn_holdtime'] - route_mapin = module.params['pn_route_mapin'] - route_mapout = module.params['pn_route_mapout'] - - # Building the CLI command string - cli = pn_cli(module) - - command = get_command_from_state(state) - if command == 'vrouter-bgp-remove': - check_cli(module, cli) - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - if NEIGHBOR_EXISTS is False: - module.exit_json( - skipped=True, - msg=('BGP neighbor with IP %s does not exist on %s' - % (neighbor, vrouter_name)) - ) - cli += (' %s vrouter-name %s neighbor %s ' - % (command, vrouter_name, neighbor)) - - else: - - if command == 'vrouter-bgp-add': - check_cli(module, cli) - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - if NEIGHBOR_EXISTS is True: - module.exit_json( - skipped=True, - msg=('BGP neighbor with IP %s already exists on %s' - % (neighbor, vrouter_name)) - ) - - cli += (' %s vrouter-name %s neighbor %s ' - % (command, vrouter_name, neighbor)) - - if remote_as: - cli += ' remote-as ' + str(remote_as) - - if next_hop_self is True: - cli += ' next-hop-self ' - if next_hop_self is False: - cli += ' no-next-hop-self ' - - if password: - cli += ' password ' + password - - if ebgp: - cli += ' ebgp-multihop ' + str(ebgp) - - if prefix_listin: - cli += ' prefix-list-in ' + prefix_listin - - if prefix_listout: - cli += ' prefix-list-out ' + prefix_listout - - if route_reflector is True: - cli += ' route-reflector-client ' - if route_reflector is False: - cli += ' no-route-reflector-client ' - - if override_capability is True: - cli += ' override-capability ' - if override_capability is False: - cli += ' no-override-capability ' - - if soft_reconfig is True: - cli += ' soft-reconfig-inbound ' - if soft_reconfig is False: - cli += ' no-soft-reconfig-inbound ' - - if max_prefix: - cli += ' max-prefix ' + str(max_prefix) - - if max_prefix_warn is True: - cli += ' max-prefix-warn-only ' - if max_prefix_warn is False: - cli += ' no-max-prefix-warn-only ' - - if bfd is True: - cli += ' bfd ' - if bfd is False: - cli += ' no-bfd ' - - if multiprotocol: - cli += ' multi-protocol ' + multiprotocol - - if weight: - cli += ' weight ' + str(weight) - - if default_originate is True: - cli += ' default-originate ' - if default_originate is False: - cli += ' no-default-originate ' - - if keepalive: - cli += ' neighbor-keepalive-interval ' + keepalive - - if holdtime: - cli += ' neighbor-holdtime ' + holdtime - - if route_mapin: - cli += ' route-map-in ' + route_mapin - - if route_mapout: - cli += ' route-map-out ' + route_mapout - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouterif.py b/plugins/modules/network/netvisor/pn_vrouterif.py deleted file mode 100644 index 78cb7b274c..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouterif.py +++ /dev/null @@ -1,492 +0,0 @@ -#!/usr/bin/python -""" PN-CLI vrouter-interface-add/remove/modify """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_vrouterif -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to add/remove/modify vrouter-interface. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute vrouter-interface-add, vrouter-interface-remove, - vrouter-interface-modify command. - - You configure interfaces to vRouter services on a fabric, cluster, - standalone switch or virtual network(VNET). -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch to run the cli on. - required: False - default: 'local' - state: - description: - - State the action to perform. Use 'present' to add vrouter interface, - 'absent' to remove vrouter interface and 'update' to modify vrouter - interface. - required: True - choices: ['present', 'absent', 'update'] - pn_vrouter_name: - description: - - Specify the name of the vRouter interface. - required: True - pn_vlan: - description: - - Specify the VLAN identifier. This is a value between 1 and 4092. - pn_interface_ip: - description: - - Specify the IP address of the interface in x.x.x.x/n format. - pn_assignment: - description: - - Specify the DHCP method for IP address assignment. - choices: ['none', 'dhcp', 'dhcpv6', 'autov6'] - pn_vxlan: - description: - - Specify the VXLAN identifier. This is a value between 1 and 16777215. - pn_interface: - description: - - Specify if the interface is management, data or span interface. - choices: ['mgmt', 'data', 'span'] - pn_alias: - description: - - Specify an alias for the interface. - pn_exclusive: - description: - - Specify if the interface is exclusive to the configuration. Exclusive - means that other configurations cannot use the interface. Exclusive is - specified when you configure the interface as span interface and allows - higher throughput through the interface. - type: bool - required: False - pn_nic_enable: - description: - - Specify if the NIC is enabled or not - type: bool - pn_vrrp_id: - description: - - Specify the ID for the VRRP interface. The IDs on both vRouters must be - the same IS number. - pn_vrrp_priority: - description: - - Specify the priority for the VRRP interface. This is a value between - 1 (lowest) and 255 (highest). - pn_vrrp_adv_int: - description: - - Specify a VRRP advertisement interval in milliseconds. The range is - from 30 to 40950 with a default value of 1000. - pn_l3port: - description: - - Specify a Layer 3 port for the interface. - pn_secondary_macs: - description: - - Specify a secondary MAC address for the interface. - pn_nic_str: - description: - - Specify the type of NIC. Used for vrouter-interface remove/modify. -''' - -EXAMPLES = """ -- name: Add vrouter-interface - pn_vrouterif: - pn_cliusername: admin - pn_clipassword: admin - state: 'present' - pn_vrouter_name: 'ansible-vrouter' - pn_interface_ip: 101.101.101.2/24 - pn_vlan: 101 - -- name: Add VRRP.. - pn_vrouterif: - pn_cliusername: admin - pn_clipassword: admin - state: 'present' - pn_vrouter_name: 'ansible-vrouter' - pn_interface_ip: 101.101.101.2/24 - pn_vrrp_ip: 101.101.101.1/24 - pn_vrrp_priority: 100 - pn_vlan: 101 - -- name: Remove vrouter-interface - pn_vrouterif: - pn_cliusername: admin - pn_clipassword: admin - state: 'absent' - pn_vrouter_name: 'ansible-vrouter' - pn_interface_ip: 101.101.101.2/24 -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the vrouterif command. - returned: on success - type: list -stderr: - description: The set of error responses from the vrouterif command. - returned: on error - type: str -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# Ansible boiler-plate -from ansible.module_utils.basic import AnsibleModule - -VROUTER_EXISTS = None -INTERFACE_EXISTS = None -NIC_EXISTS = None -VRRP_EXISTS = None - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the vrouter-interface-show - command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - - If an interface with the given ip exists on the given vRouter, - return INTERFACE_EXISTS as True else False. This is required for - vrouter-interface-add. - - If nic_str exists on the given vRouter, return NIC_EXISTS as True else - False. This is required for vrouter-interface-remove. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: VROUTER_EXISTS, INTERFACE_EXISTS, NIC_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - interface_ip = module.params['pn_interface_ip'] - nic_str = module.params['pn_nic_str'] - - # Global flags - global VROUTER_EXISTS, INTERFACE_EXISTS, NIC_EXISTS - - # Check for vRouter - check_vrouter = cli + ' vrouter-show format name no-show-headers ' - check_vrouter = shlex.split(check_vrouter) - out = module.run_command(check_vrouter)[1] - out = out.split() - - if vrouter_name in out: - VROUTER_EXISTS = True - else: - VROUTER_EXISTS = False - - if interface_ip: - # Check for interface and VRRP and fetch nic for VRRP - show = cli + ' vrouter-interface-show vrouter-name %s ' % vrouter_name - show += 'ip %s format ip,nic no-show-headers' % interface_ip - show = shlex.split(show) - out = module.run_command(show)[1] - if out: - INTERFACE_EXISTS = True - else: - INTERFACE_EXISTS = False - - if nic_str: - # Check for nic - show = cli + ' vrouter-interface-show vrouter-name %s ' % vrouter_name - show += ' format nic no-show-headers' - show = shlex.split(show) - out = module.run_command(show)[1] - if nic_str in out: - NIC_EXISTS = True - else: - NIC_EXISTS = False - - -def get_nic(module, cli): - """ - This module checks if VRRP interface can be added. If No, return VRRP_EXISTS - as True. - If Yes, fetch the nic string from the primary interface and return nic and - VRRP_EXISTS as False. - :param module: - :param cli: - :return: nic, Global Boolean: VRRP_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - interface_ip = module.params['pn_interface_ip'] - - global VRRP_EXISTS - - # Check for interface and VRRP and fetch nic for VRRP - show = cli + ' vrouter-interface-show vrouter-name %s ' % vrouter_name - show += 'ip %s format ip,nic no-show-headers' % interface_ip - show = shlex.split(show) - out = module.run_command(show)[1] - out = out.split() - - if len(out) > 3: - VRRP_EXISTS = True - return None - else: - nic = out[2] - VRRP_EXISTS = False - return nic - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - - cmd = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'vrouter-interface-add' - if state == 'absent': - command = 'vrouter-interface-remove' - if state == 'update': - command = 'vrouter-interface-modify' - return command - - -def main(): - """ This portion is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(required=True, type='str', - choices=['present', 'absent', 'update']), - pn_vrouter_name=dict(required=True, type='str'), - pn_vlan=dict(type='int'), - pn_interface_ip=dict(required=True, type='str'), - pn_assignment=dict(type='str', - choices=['none', 'dhcp', 'dhcpv6', 'autov6']), - pn_vxlan=dict(type='int'), - pn_interface=dict(type='str', choices=['mgmt', 'data', 'span']), - pn_alias=dict(type='str'), - pn_exclusive=dict(type='bool'), - pn_nic_enable=dict(type='bool'), - pn_vrrp_id=dict(type='int'), - pn_vrrp_priority=dict(type='int'), - pn_vrrp_adv_int=dict(type='str'), - pn_l3port=dict(type='str'), - pn_secondary_macs=dict(type='str'), - pn_nic_str=dict(type='str') - ), - required_if=( - ["state", "present", - ["pn_vrouter_name", "pn_interface_ip"]], - ["state", "absent", - ["pn_vrouter_name", "pn_nic_str"]] - ), - ) - - # Accessing the arguments - state = module.params['state'] - vrouter_name = module.params['pn_vrouter_name'] - vlan = module.params['pn_vlan'] - interface_ip = module.params['pn_interface_ip'] - assignment = module.params['pn_assignment'] - vxlan = module.params['pn_vxlan'] - interface = module.params['pn_interface'] - alias = module.params['pn_alias'] - exclusive = module.params['pn_exclusive'] - nic_enable = module.params['pn_nic_enable'] - vrrp_id = module.params['pn_vrrp_id'] - vrrp_priority = module.params['pn_vrrp_priority'] - vrrp_adv_int = module.params['pn_vrrp_adv_int'] - l3port = module.params['pn_l3port'] - secondary_macs = module.params['pn_secondary_macs'] - nic_str = module.params['pn_nic_str'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = pn_cli(module) - - check_cli(module, cli) - if command == 'vrouter-interface-add': - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - - if vrrp_id: - vrrp_primary = get_nic(module, cli) - if VRRP_EXISTS is True: - module.exit_json( - skipped=True, - msg=('VRRP interface on %s already exists. Check ' - 'the IP addresses' % vrouter_name) - ) - cli += ' %s vrouter-name %s ' % (command, vrouter_name) - cli += (' ip %s vrrp-primary %s vrrp-id %s ' - % (interface_ip, vrrp_primary, str(vrrp_id))) - if vrrp_priority: - cli += ' vrrp-priority %s ' % str(vrrp_priority) - if vrrp_adv_int: - cli += ' vrrp-adv-int %s ' % vrrp_adv_int - - else: - if INTERFACE_EXISTS is True: - module.exit_json( - skipped=True, - msg=('vRouter interface on %s already exists. Check the ' - 'IP addresses' % vrouter_name) - ) - cli += ' %s vrouter-name %s ' % (command, vrouter_name) - cli += ' ip %s ' % interface_ip - - if vlan: - cli += ' vlan ' + str(vlan) - - if l3port: - cli += ' l3-port ' + l3port - - if assignment: - cli += ' assignment ' + assignment - - if vxlan: - cli += ' vxlan ' + str(vxlan) - - if interface: - cli += ' if ' + interface - - if alias: - cli += ' alias-on ' + alias - - if exclusive is True: - cli += ' exclusive ' - if exclusive is False: - cli += ' no-exclusive ' - - if nic_enable is True: - cli += ' nic-enable ' - if nic_enable is False: - cli += ' nic-disable ' - - if secondary_macs: - cli += ' secondary-macs ' + secondary_macs - - if command == 'vrouter-interface-remove': - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - if NIC_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter interface with nic %s does not exist' % nic_str - ) - cli += ' %s vrouter-name %s nic %s ' % (command, vrouter_name, nic_str) - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vrouterlbif.py b/plugins/modules/network/netvisor/pn_vrouterlbif.py deleted file mode 100644 index cf3e64cdc9..0000000000 --- a/plugins/modules/network/netvisor/pn_vrouterlbif.py +++ /dev/null @@ -1,333 +0,0 @@ -#!/usr/bin/python -""" PN CLI vrouter-loopback-interface-add/remove """ - -# -# 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 . -# - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: pn_vrouterlbif -author: "Pluribus Networks (@amitsi)" -short_description: CLI command to add/remove vrouter-loopback-interface. -deprecated: - removed_in: '2.12' - why: Doesn't support latest Pluribus Networks netvisor - alternative: Latest modules will be pushed in Ansible future versions. -description: - - Execute vrouter-loopback-interface-add, vrouter-loopback-interface-remove - commands. - - Each fabric, cluster, standalone switch, or virtual network (VNET) can - provide its tenants with a virtual router (vRouter) service that forwards - traffic between networks and implements Layer 3 protocols. -options: - pn_cliusername: - description: - - Provide login username if user is not root. - required: False - pn_clipassword: - description: - - Provide login password if user is not root. - required: False - pn_cliswitch: - description: - - Target switch(es) to run the cli on. - required: False - default: 'local' - state: - description: - - State the action to perform. Use 'present' to add vrouter loopback - interface and 'absent' to remove vrouter loopback interface. - required: True - choices: ['present', 'absent'] - pn_vrouter_name: - description: - - Specify the name of the vRouter. - required: True - pn_index: - description: - - Specify the interface index from 1 to 255. - pn_interface_ip: - description: - - Specify the IP address. - required: True -''' - -EXAMPLES = """ -- name: add vrouter-loopback-interface - pn_vrouterlbif: - state: 'present' - pn_vrouter_name: 'ansible-vrouter' - pn_interface_ip: '104.104.104.1' - -- name: remove vrouter-loopback-interface - pn_vrouterlbif: - state: 'absent' - pn_vrouter_name: 'ansible-vrouter' - pn_interface_ip: '104.104.104.1' -""" - -RETURN = """ -command: - description: The CLI command run on the target node(s). - returned: always - type: str -stdout: - description: The set of responses from the vrouterlb command. - returned: always - type: list -stderr: - description: The set of error responses from the vrouterlb command. - returned: on error - type: list -changed: - description: Indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -import shlex - -# Ansible boiler-plate -from ansible.module_utils.basic import AnsibleModule - -VROUTER_EXISTS = None -LB_INTERFACE_EXISTS = None -# Index range -MIN_INDEX = 1 -MAX_INDEX = 255 - - -def pn_cli(module): - """ - This method is to generate the cli portion to launch the Netvisor cli. - It parses the username, password, switch parameters from module. - :param module: The Ansible module to fetch username, password and switch - :return: returns the cli string for further processing - """ - username = module.params['pn_cliusername'] - password = module.params['pn_clipassword'] - cliswitch = module.params['pn_cliswitch'] - - if username and password: - cli = '/usr/bin/cli --quiet --user %s:%s ' % (username, password) - else: - cli = '/usr/bin/cli --quiet ' - - if cliswitch == 'local': - cli += ' switch-local ' - else: - cli += ' switch ' + cliswitch - return cli - - -def check_cli(module, cli): - """ - This method checks if vRouter exists on the target node. - This method also checks for idempotency using the - vrouter-loopback-interface-show command. - If the given vRouter exists, return VROUTER_EXISTS as True else False. - If a loopback interface with the given ip exists on the given vRouter, - return LB_INTERFACE_EXISTS as True else False. - - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - :return Global Booleans: VROUTER_EXISTS, LB_INTERFACE_EXISTS - """ - vrouter_name = module.params['pn_vrouter_name'] - interface_ip = module.params['pn_interface_ip'] - - # Global flags - global VROUTER_EXISTS, LB_INTERFACE_EXISTS - - # Check for vRouter - check_vrouter = cli + ' vrouter-show format name no-show-headers ' - check_vrouter = shlex.split(check_vrouter) - out = module.run_command(check_vrouter)[1] - out = out.split() - - if vrouter_name in out: - VROUTER_EXISTS = True - else: - VROUTER_EXISTS = False - - # Check for loopback interface - show = (cli + ' vrouter-loopback-interface-show vrouter-name %s format ip ' - 'no-show-headers' % vrouter_name) - show = shlex.split(show) - out = module.run_command(show)[1] - out = out.split() - - if interface_ip in out: - LB_INTERFACE_EXISTS = True - else: - LB_INTERFACE_EXISTS = False - - -def run_cli(module, cli): - """ - This method executes the cli command on the target node(s) and returns the - output. The module then exits based on the output. - :param cli: the complete cli string to be executed on the target node(s). - :param module: The Ansible module to fetch command - """ - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - command = get_command_from_state(state) - - cmd = shlex.split(cli) - - # 'out' contains the output - # 'err' contains the error messages - result, out, err = module.run_command(cmd) - - print_cli = cli.split(cliswitch)[1] - - # Response in JSON format - if result != 0: - module.exit_json( - command=print_cli, - stderr=err.strip(), - msg="%s operation failed" % command, - changed=False - ) - - if out: - module.exit_json( - command=print_cli, - stdout=out.strip(), - msg="%s operation completed" % command, - changed=True - ) - - else: - module.exit_json( - command=print_cli, - msg="%s operation completed" % command, - changed=True - ) - - -def get_command_from_state(state): - """ - This method gets appropriate command name for the state specified. It - returns the command name for the specified state. - :param state: The state for which the respective command name is required. - """ - command = None - if state == 'present': - command = 'vrouter-loopback-interface-add' - if state == 'absent': - command = 'vrouter-loopback-interface-remove' - return command - - -def main(): - """ This portion is for arguments parsing """ - module = AnsibleModule( - argument_spec=dict( - pn_cliusername=dict(required=False, type='str'), - pn_clipassword=dict(required=False, type='str', no_log=True), - pn_cliswitch=dict(required=False, type='str', default='local'), - state=dict(required=True, type='str', - choices=['present', 'absent']), - pn_vrouter_name=dict(required=True, type='str'), - pn_interface_ip=dict(type='str'), - pn_index=dict(type='int') - ), - required_if=( - ["state", "present", - ["pn_vrouter_name", "pn_interface_ip"]], - ["state", "absent", - ["pn_vrouter_name", "pn_interface_ip"]] - ) - ) - - # Accessing the arguments - state = module.params['state'] - vrouter_name = module.params['pn_vrouter_name'] - interface_ip = module.params['pn_interface_ip'] - index = module.params['pn_index'] - - command = get_command_from_state(state) - - # Building the CLI command string - cli = pn_cli(module) - - if index: - if not MIN_INDEX <= index <= MAX_INDEX: - module.exit_json( - msg="Index must be between 1 and 255", - changed=False - ) - index = str(index) - - if command == 'vrouter-loopback-interface-remove': - check_cli(module, cli) - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg='vRouter %s does not exist' % vrouter_name - ) - if LB_INTERFACE_EXISTS is False: - module.exit_json( - skipped=True, - msg=('Loopback interface with IP %s does not exist on %s' - % (interface_ip, vrouter_name)) - ) - if not index: - # To remove loopback interface, we need the index. - # If index is not specified, get the Loopback interface index - # using the given interface ip. - get_index = cli - get_index += (' vrouter-loopback-interface-show vrouter-name %s ip ' - '%s ' % (vrouter_name, interface_ip)) - get_index += 'format index no-show-headers' - - get_index = shlex.split(get_index) - out = module.run_command(get_index)[1] - index = out.split()[1] - - cli += ' %s vrouter-name %s index %s' % (command, vrouter_name, index) - - if command == 'vrouter-loopback-interface-add': - check_cli(module, cli) - if VROUTER_EXISTS is False: - module.exit_json( - skipped=True, - msg=('vRouter %s does not exist' % vrouter_name) - ) - if LB_INTERFACE_EXISTS is True: - module.exit_json( - skipped=True, - msg=('Loopback interface with IP %s already exists on %s' - % (interface_ip, vrouter_name)) - ) - cli += (' %s vrouter-name %s ip %s' - % (command, vrouter_name, interface_ip)) - if index: - cli += ' index %s ' % index - - run_cli(module, cli) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/netvisor/pn_vtep.py b/plugins/modules/network/netvisor/pn_vtep.py deleted file mode 100644 index 919f35df24..0000000000 --- a/plugins/modules/network/netvisor/pn_vtep.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/python -# Copyright: (c) 2018, Pluribus Networks -# 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: pn_vtep -author: "Pluribus Networks (@rajaspachipulusu17)" -short_description: CLI command to create/delete vtep -description: - - This module can be used to create a vtep and delete a vtep. -options: - pn_cliswitch: - description: - - Target switch to run the CLI on. - required: false - type: str - state: - description: - - vtep configuration command. - required: false - choices: ['present', 'absent'] - type: str - default: 'present' - pn_name: - description: - - vtep name. - required: false - type: str - pn_ip: - description: - - Primary IP address. - required: false - type: str - pn_vrouter_name: - description: - - name of the vrouter service. - required: false - type: str - pn_virtual_ip: - description: - - Virtual/Secondary IP address. - required: false - type: str - pn_location: - description: - - switch name. - required: false - type: str - pn_switch_in_cluster: - description: - - Tells whether switch in cluster or not. - required: false - type: bool - default: True -''' - -EXAMPLES = """ -- name: create vtep - pn_vtep: - pn_cliswitch: 'sw01' - pn_name: 'foo' - pn_vrouter_name: 'foo-vrouter' - pn_ip: '22.22.22.2' - pn_location: 'sw01' - pn_virtual_ip: "22.22.22.1" - -- name: delete vtep - pn_vtep: - pn_cliswitch: 'sw01' - state: 'absent' - pn_name: 'foo' -""" - -RETURN = """ -command: - description: the CLI command run on the target node. - returned: always - type: str -stdout: - description: set of responses from the vtep command. - returned: always - type: list -stderr: - description: set of error responses from the vtep command. - returned: on error - type: list -changed: - description: indicates whether the CLI caused changes on the target. - returned: always - type: bool -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.netvisor.pn_nvos import pn_cli, run_cli -from ansible_collections.community.general.plugins.module_utils.network.netvisor.netvisor import run_commands - - -def check_cli(module, cli): - """ - This method checks for idempotency using the vtep-show command. - If a name exists, return True if name exists else False. - :param module: The Ansible module to fetch input parameters - :param cli: The CLI string - """ - name = module.params['pn_name'] - - cli += ' vtep-show format name no-show-headers' - out = run_commands(module, cli)[1] - - if out: - out = out.split() - - return True if name in out else False - - -def main(): - """ This section is for arguments parsing """ - - state_map = dict( - present='vtep-create', - absent='vtep-delete' - ) - - argument_spec = dict( - pn_cliswitch=dict(required=False, type='str'), - state=dict(required=False, type='str', choices=state_map.keys(), default='present'), - pn_name=dict(required=False, type='str'), - pn_ip=dict(required=False, type='str'), - pn_vrouter_name=dict(required=False, type='str'), - pn_virtual_ip=dict(required=False, type='str'), - pn_location=dict(required=False, type='str'), - pn_switch_in_cluster=dict(required=False, type='bool', default='True') - ) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=( - ["state", "present", ["pn_name", "pn_ip", "pn_vrouter_name", "pn_location"]], - ["state", "absent", ["pn_name"]], - ), - ) - - # Accessing the arguments - cliswitch = module.params['pn_cliswitch'] - state = module.params['state'] - name = module.params['pn_name'] - ip = module.params['pn_ip'] - vrouter_name = module.params['pn_vrouter_name'] - virtual_ip = module.params['pn_virtual_ip'] - location = module.params['pn_location'] - switch_in_cluster = module.params['pn_switch_in_cluster'] - - if switch_in_cluster and not virtual_ip and state == 'present': - module.exit_json( - failed=True, - msg='virtual ip is required when switch is in cluster' - ) - - command = state_map[state] - - # Building the CLI command string - cli = pn_cli(module, cliswitch) - - NAME_EXISTS = check_cli(module, cli) - - cli += ' %s name %s ' % (command, name) - - if command == 'vtep-delete': - if NAME_EXISTS is False: - module.exit_json( - skipped=True, - msg='vtep with name %s does not exist' % name - ) - - if command == 'vtep-create': - if NAME_EXISTS is True: - module.exit_json( - skipped=True, - msg='vtpe with name %s already exists' % name - ) - - cli += 'vrouter-name %s ' % vrouter_name - cli += 'ip %s ' % ip - cli += 'location %s ' % location - - if virtual_ip: - cli += 'virtual-ip %s ' % virtual_ip - - run_cli(module, cli, state_map) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/nos/nos_command.py b/plugins/modules/network/nos/nos_command.py deleted file mode 100644 index 7eb9be77a1..0000000000 --- a/plugins/modules/network/nos/nos_command.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2018, Extreme Networks 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) -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: nos_command -author: "Lindsay Hill (@LindsayHill)" -short_description: Run commands on remote devices running Extreme Networks NOS -description: - - Sends arbitrary commands to a NOS 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(nos_config) to configure NOS devices. -notes: - - Tested against NOS 7.2.0 - - If a command sent to the device requires answering a prompt, it is possible - to pass a dict containing I(command), I(answer) and I(prompt). See examples. -options: - commands: - description: - - List of commands to send to the remote NOS 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. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - 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. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -tasks: - - name: run show version on remote devices - nos_command: - commands: show version - - - name: run show version and check to see if output contains NOS - nos_command: - commands: show version - wait_for: result[0] contains NOS - - - name: run multiple commands on remote nodes - nos_command: - commands: - - show version - - show interfaces - - - name: run multiple commands and evaluate the output - nos_command: - commands: - - show version - - show interface status - wait_for: - - result[0] contains NOS - - result[1] contains Te - - name: run command that requires answering a prompt - nos_command: - commands: - - command: 'clear sessions' - prompt: 'This operation will logout all the user sessions. Do you want to continue (yes/no)?:' - answer: y -""" - -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: ['...', '...'] -""" -import re -import time - -from ansible_collections.community.general.plugins.module_utils.network.nos.nos import run_commands -from ansible.module_utils.basic import AnsibleModule -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 - - -__metaclass__ = type - - -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 item in list(commands): - configure_type = re.match(r'conf(?:\w*)(?:\s+(\w+))?', item['command']) - if module.check_mode: - if configure_type and configure_type.group(1) not in ('confirm', 'replace', 'revert', 'network'): - module.fail_json( - msg='nos_command does not support running config mode ' - 'commands. Please use nos_config instead' - ) - if not item['command'].startswith('show'): - warnings.append( - 'only show commands are supported when using check mode, not ' - 'executing `%s`' % item['command'] - ) - commands.remove(item) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - 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/plugins/modules/network/nos/nos_config.py b/plugins/modules/network/nos/nos_config.py deleted file mode 100644 index c73fdfd1b3..0000000000 --- a/plugins/modules/network/nos/nos_config.py +++ /dev/null @@ -1,394 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2018, Extreme Networks 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) -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: nos_config -author: "Lindsay Hill (@LindsayHill)" -short_description: Manage Extreme Networks NOS configuration sections -description: - - Extreme NOS configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with NOS configuration sections in - a deterministic way. -notes: - - Tested against NOS 7.2.0 -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. - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - 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. - 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. - 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. - 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. - default: line - choices: ['line', 'block'] - multiline_delimiter: - description: - - This argument is used when pushing a multiline configuration - element to the NOS device. It specifies the character to use - as the delimiting character. This only applies to the - configuration action. - default: "@" - backup: - description: - - This argument will cause the module to create a full backup of - the current C(running-config) from the remote device before any - changes are made. 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' - running_config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(running_config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - aliases: ['config'] - diff_against: - description: - - When using the C(ansible-playbook --diff) command line argument - the module can generate diffs against different sources. - - When this option is configured as I(intended), the module will - return the diff of the running-config against the configuration - provided in the C(intended_config) argument. - - When this option is configured as I(running), the module will - return the before and after diff of the running-config with respect - to any changes made to the device configuration. - choices: ['running', 'intended'] - diff_ignore_lines: - description: - - Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - intended_config: - description: - - The C(intended_config) provides the master configuration that - the node should conform to and is used to check the final - running-config against. This argument will not modify any settings - on the remote device and is strictly used to check the compliance - of the current device's configuration against. When specifying this - argument, the task should also modify the C(diff_against) value and - set it to I(intended). - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- name: configure top level configuration - nos_config: - lines: logging raslog console INFO - -- name: configure interface settings - nos_config: - lines: - - description test interface - - ip address 172.31.1.1/24 - parents: - - interface TenGigabitEthernet 104/0/1 - -- name: configure multiple interfaces - nos_config: - lines: - - lacp timeout long - parents: "{{ item }}" - with_items: - - interface TenGigabitEthernet 104/0/1 - - interface TenGigabitEthernet 104/0/2 - -- name: load new acl into device - nos_config: - lines: - - seq 10 permit ip host 1.1.1.1 any log - - seq 20 permit ip host 2.2.2.2 any log - - seq 30 permit ip host 3.3.3.3 any log - - seq 40 permit ip host 4.4.4.4 any log - - seq 50 permit ip host 5.5.5.5 any log - parents: ip access-list extended test - before: no ip access-list extended test - match: exact - -- name: check the running-config against master config - nos_config: - diff_against: intended - intended_config: "{{ lookup('file', 'master.cfg') }}" - -- name: configurable backup path - nos_config: - lines: logging raslog console INFO - 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: ['switch-attributes hostname foo', 'router ospf', 'area 0'] -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['switch-attributes hostname foo', 'router ospf', 'area 0'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/nos_config.2018-02-12@18:26:34 -""" - -from ansible_collections.community.general.plugins.module_utils.network.nos.nos import run_commands, get_config, load_config -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - -__metaclass__ = type - - -def check_args(module, warnings): - if module.params['multiline_delimiter']: - if len(module.params['multiline_delimiter']) != 1: - module.fail_json(msg='multiline_delimiter value can only be a ' - 'single character') - - -def get_running_config(module, current_config=None): - contents = module.params['running_config'] - if not contents: - if current_config: - contents = current_config.config_text - else: - contents = get_config(module) - return NetworkConfig(indent=1, contents=contents) - - -def get_candidate(module): - candidate = NetworkConfig(indent=1) - - if module.params['src']: - src = module.params['src'] - candidate.load(src) - - elif module.params['lines']: - parents = module.params['parents'] or list() - candidate.add(module.params['lines'], parents=parents) - - return candidate - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - multiline_delimiter=dict(default='@'), - - running_config=dict(aliases=['config']), - intended_config=dict(), - - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - - diff_against=dict(choices=['intended', 'running']), - diff_ignore_lines=dict(type='list'), - ) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('diff_against', 'intended', ['intended_config'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - check_args(module, warnings) - result['warnings'] = warnings - - config = None - - if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'): - contents = get_config(module) - config = NetworkConfig(indent=1, contents=contents) - if module.params['backup']: - result['__backup__'] = contents - - if any((module.params['lines'], module.params['src'])): - match = module.params['match'] - replace = module.params['replace'] - path = module.params['parents'] - - candidate = get_candidate(module) - - if match != 'none': - config = get_running_config(module, config) - configobjs = candidate.difference(config, path=path, match=match, replace=replace) - else: - configobjs = candidate.items - - if configobjs: - commands = dumps(configobjs, 'commands').split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - result['updates'] = commands - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - if commands: - load_config(module, commands) - - result['changed'] = True - - running_config = None - - diff_ignore_lines = module.params['diff_ignore_lines'] - - if module._diff: - if not running_config: - output = run_commands(module, 'show running-config') - contents = output[0] - else: - contents = running_config.config_text - - # recreate the object in order to process diff_ignore_lines - running_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if module.params['diff_against'] == 'running': - if module.check_mode: - module.warn("unable to perform diff against running-config due to check mode") - contents = None - else: - contents = config.config_text - - elif module.params['diff_against'] == 'intended': - contents = module.params['intended_config'] - - if contents is not None: - base_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if running_config.sha1 != base_config.sha1: - if module.params['diff_against'] == 'intended': - before = running_config - after = base_config - elif module.params['diff_against'] in ('running'): - before = base_config - after = running_config - - result.update({ - 'changed': True, - 'diff': {'before': str(before), 'after': str(after)} - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/nos/nos_facts.py b/plugins/modules/network/nos/nos_facts.py deleted file mode 100644 index 4c597a8706..0000000000 --- a/plugins/modules/network/nos/nos_facts.py +++ /dev/null @@ -1,458 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: nos_facts -author: "Lindsay Hill (@LindsayHill)" -short_description: Collect facts from devices running Extreme NOS -description: - - Collects a base set of device facts from a remote device that - is running NOS. This module prepends all of the - base network fact keys with C(ansible_net_). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -notes: - - Tested against NOS 7.2.0 -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: '!config' -''' - -EXAMPLES = """ -# Collect all facts from the device -- nos_facts: - gather_subset: all - -# Collect only the config and default facts -- nos_facts: - gather_subset: - - config - -# Do not collect hardware facts -- nos_facts: - gather_subset: - - "!hardware" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - -# hardware -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 Primary IPv6 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -ansible_net_neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -""" -import re - -from ansible_collections.community.general.plugins.module_utils.network.nos.nos import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, self.COMMANDS) - - def run(self, cmd): - return run_commands(self.module, cmd) - - -class Default(FactsBase): - - COMMANDS = [ - 'show version', - 'show inventory chassis', - r'show running-config | include host\-name' - ] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = self.parse_version(data) - - data = self.responses[1] - if data: - self.facts['model'] = self.parse_model(data) - self.facts['serialnum'] = self.parse_serialnum(data) - - data = self.responses[2] - if data: - self.facts['hostname'] = self.parse_hostname(data) - - def parse_version(self, data): - match = re.search(r'Network Operating System Version: (\S+)', data) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'SID:(\S+)', data, re.M) - if match: - return match.group(1) - - def parse_hostname(self, data): - match = re.search(r'switch-attributes host-name (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'SN:(\S+)', data, re.M) - if match: - return match.group(1) - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show process memory summary' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - if data: - self.facts['memtotal_mb'] = int(round(int(self.parse_memtotal(data)) / 1024, 0)) - self.facts['memfree_mb'] = int(round(int(self.parse_memfree(data)) / 1024, 0)) - - def parse_memtotal(self, data): - match = re.search(r'TotalMemory: (\d+)\s', data, re.M) - if match: - return match.group(1) - - def parse_memfree(self, data): - match = re.search(r'Total Free: (\d+)\s', data, re.M) - if match: - return match.group(1) - - -class Config(FactsBase): - - COMMANDS = ['show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'show interface', - 'show ipv6 interface brief', - r'show lldp nei detail | inc ^Local\ Interface|^Remote\ Interface|^System\ Name' - ] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - data = self.responses[0] - if data: - interfaces = self.parse_interfaces(data) - self.facts['interfaces'] = self.populate_interfaces(interfaces) - self.populate_ipv4_interfaces(interfaces) - - data = self.responses[1] - if data: - self.populate_ipv6_interfaces(data) - - data = self.responses[2] - if data: - self.facts['neighbors'] = self.parse_neighbors(data) - else: - self.facts['neighbors'] = dict() - - def populate_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - intf['description'] = self.parse_description(value) - intf['macaddress'] = self.parse_macaddress(value) - intf['mtu'] = self.parse_mtu(value) - intf['bandwidth'] = self.parse_bandwidth(value) - intf['duplex'] = self.parse_duplex(value) - intf['lineprotocol'] = self.parse_lineprotocol(value) - intf['operstatus'] = self.parse_operstatus(value) - intf['type'] = self.parse_type(value) - - facts[key] = intf - return facts - - def populate_ipv4_interfaces(self, data): - for key, value in data.items(): - self.facts['interfaces'][key]['ipv4'] = list() - primary_address = addresses = [] - primary_address = re.findall(r'Primary Internet Address is (\S+)', value, re.M) - addresses = re.findall(r'Secondary Internet Address is (\S+)', value, re.M) - if not primary_address: - continue - addresses.append(primary_address[0]) - for address in addresses: - addr, subnet = address.split("/") - ipv4 = dict(address=addr.strip(), subnet=subnet.strip()) - self.add_ip_address(addr.strip(), 'ipv4') - self.facts['interfaces'][key]['ipv4'].append(ipv4) - - # Only gets primary IPv6 addresses - def populate_ipv6_interfaces(self, data): - interfaces = re.split('=+', data)[1].strip() - matches = re.findall(r'(\S+ \S+) +[\w-]+.+\s+([\w:/]+/\d+)', interfaces, re.M) - for match in matches: - interface = match[0] - self.facts['interfaces'][interface]['ipv6'] = list() - address, masklen = match[1].split('/') - ipv6 = dict(address=address, masklen=int(masklen)) - self.add_ip_address(ipv6['address'], 'ipv6') - self.facts['interfaces'][interface]['ipv6'].append(ipv6) - - def add_ip_address(self, address, family): - if family == 'ipv4': - self.facts['all_ipv4_addresses'].append(address) - else: - self.facts['all_ipv6_addresses'].append(address) - - def parse_neighbors(self, neighbors): - facts = dict() - lines = neighbors.split('Local Interface: ') - if not lines: - return facts - for line in lines: - match = re.search(r'(\w+ \S+)\s+\(Local Int.+?\)[\s\S]+Remote Interface: (\S+.+?) \(Remote Int.+?\)[\s\S]+System Name: (\S+)', line, re.M) - if match: - intf = match.group(1) - if intf not in facts: - facts[intf] = list() - fact = dict() - fact['host'] = match.group(3) - fact['port'] = match.group(2) - facts[intf].append(fact) - return facts - - def parse_interfaces(self, data): - parsed = dict() - for interface in data.split('\n\n'): - match = re.match(r'^(\S+ \S+)', interface, re.M) - if not match: - continue - else: - parsed[match.group(1)] = interface - return parsed - - def parse_description(self, data): - match = re.search(r'Description: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_macaddress(self, data): - match = re.search(r'Hardware is Ethernet, address is (\S+)', data) - if match: - return match.group(1) - - def parse_ipv4(self, data): - match = re.search(r'Primary Internet Address is ([^\s,]+)', data) - if match: - addr, masklen = match.group(1).split('/') - return dict(address=addr, masklen=int(masklen)) - - def parse_mtu(self, data): - match = re.search(r'MTU (\d+) bytes', data) - if match: - return int(match.group(1)) - - def parse_bandwidth(self, data): - match = re.search(r'LineSpeed Actual\s+:\s(.+)', data) - if match: - return match.group(1) - - def parse_duplex(self, data): - match = re.search(r'Duplex: (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_type(self, data): - match = re.search(r'Hardware is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lineprotocol(self, data): - match = re.search(r'line protocol is (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_operstatus(self, data): - match = re.match(r'^(?:.+) is (.+),', data, re.M) - if match: - return match.group(1) - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=["!config"], type='list') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - gather_subset = module.params['gather_subset'] - - runable_subsets = set() - exclude_subsets = set() - - for subset in gather_subset: - if subset == 'all': - runable_subsets.update(VALID_SUBSETS) - continue - - if subset.startswith('!'): - subset = subset[1:] - if subset == 'all': - exclude_subsets.update(VALID_SUBSETS) - continue - exclude = True - else: - exclude = False - - if subset not in VALID_SUBSETS: - module.fail_json(msg='Bad subset') - - if exclude: - exclude_subsets.add(subset) - else: - runable_subsets.add(subset) - - if not runable_subsets: - runable_subsets.update(VALID_SUBSETS) - - runable_subsets.difference_update(exclude_subsets) - runable_subsets.add('default') - - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - instances.append(FACT_SUBSETS[key](module)) - - for inst in instances: - inst.populate() - facts.update(inst.facts) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - warnings = list() - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/nso/nso_action.py b/plugins/modules/network/nso/nso_action.py deleted file mode 100644 index 30378ecbbf..0000000000 --- a/plugins/modules/network/nso/nso_action.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified' -} - -DOCUMENTATION = ''' ---- -module: nso_action -extends_documentation_fragment: -- community.general.nso - -short_description: Executes Cisco NSO actions and verifies output. -description: - - This module provides support for executing Cisco NSO actions and then - verifying that the output is as expected. -requirements: - - Cisco NSO version 3.4 or higher. -author: "Claes Nästén (@cnasten)" -options: - path: - description: Path to NSO action. - required: true - input: - description: > - NSO action parameters. - output_required: - description: > - Required output parameters. - output_invalid: - description: > - List of result parameter names that will cause the task to fail if they - are present. - validate_strict: - description: > - If set to true, the task will fail if any output parameters not in - output_required is present in the output. - type: bool -''' - -EXAMPLES = ''' -- name: Sync NSO device - nso_action: - url: http://localhost:8080/jsonrpc - username: username - password: password - path: /ncs:devices/device{ce0}/sync-from - input: {} -''' - -RETURN = ''' -output: - description: Action output - returned: success - type: dict - sample: - result: true -''' - -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import normalize_value -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoAction(object): - REQUIRED_VERSIONS = [ - (3, 4) - ] - - def __init__(self, check_mode, client, - path, input, - output_required, output_invalid, validate_strict): - self._check_mode = check_mode - self._client = client - self._path = path - self._input = input - self._output_required = output_required - self._output_invalid = output_invalid - self._validate_strict = validate_strict - - def main(self): - schema = self._client.get_schema(path=self._path) - if schema['data']['kind'] != 'action': - raise ModuleFailException('{0} is not an action'.format(self._path)) - - input_schema = [c for c in schema['data']['children'] - if c.get('is_action_input', False)] - - for key, value in self._input.items(): - child = next((c for c in input_schema if c['name'] == key), None) - if child is None: - raise ModuleFailException('no parameter {0}'.format(key)) - - # implement type validation in the future - - if self._check_mode: - return {} - else: - return self._run_and_verify() - - def _run_and_verify(self): - output = self._client.run_action(None, self._path, self._input) - for key, value in self._output_required.items(): - if key not in output: - raise ModuleFailException('{0} not in result'.format(key)) - - n_value = normalize_value(value, output[key], key) - if value != n_value: - msg = '{0} value mismatch. expected {1} got {2}'.format( - key, value, n_value) - raise ModuleFailException(msg) - - for key in self._output_invalid.keys(): - if key in output: - raise ModuleFailException('{0} not allowed in result'.format(key)) - - if self._validate_strict: - for name in output.keys(): - if name not in self._output_required: - raise ModuleFailException('{0} not allowed in result'.format(name)) - - return output - - -def main(): - argument_spec = dict( - path=dict(required=True), - input=dict(required=False, type='dict', default={}), - output_required=dict(required=False, type='dict', default={}), - output_invalid=dict(required=False, type='dict', default={}), - validate_strict=dict(required=False, type='bool', default=False) - ) - argument_spec.update(nso_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - p = module.params - - client = connect(p) - nso_action = NsoAction( - module.check_mode, client, - p['path'], - p['input'], - p['output_required'], - p['output_invalid'], - p['validate_strict']) - try: - verify_version(client, NsoAction.REQUIRED_VERSIONS) - - output = nso_action.main() - client.logout() - module.exit_json(changed=True, output=output) - except NsoException as ex: - client.logout() - module.fail_json(msg=ex.message) - except ModuleFailException as ex: - client.logout() - module.fail_json(msg=ex.message) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/nso/nso_config.py b/plugins/modules/network/nso/nso_config.py deleted file mode 100644 index 1ed99784c1..0000000000 --- a/plugins/modules/network/nso/nso_config.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified' -} - -DOCUMENTATION = ''' ---- -module: nso_config -extends_documentation_fragment: -- community.general.nso - -short_description: Manage Cisco NSO configuration and service synchronization. -description: - - This module provides support for managing configuration in Cisco NSO and - can also ensure services are in sync. -requirements: - - Cisco NSO version 3.4.12 or higher, 4.2.7 or higher, - 4.3.8 or higher, 4.4.3 or higher, 4.5 or higher. -author: "Claes Nästén (@cnasten)" -options: - data: - description: > - NSO data in format as | display json converted to YAML. List entries can - be annotated with a __state entry. Set to in-sync/deep-in-sync for - services to verify service is in sync with the network. Set to absent in - list entries to ensure they are deleted if they exist in NSO. - required: true -''' - -EXAMPLES = ''' -- name: Create L3VPN - nso_config: - url: http://localhost:8080/jsonrpc - username: username - password: password - data: - l3vpn:vpn: - l3vpn: - - name: company - route-distinguisher: 999 - endpoint: - - id: branch-office1 - ce-device: ce6 - ce-interface: GigabitEthernet0/12 - ip-network: 10.10.1.0/24 - bandwidth: 12000000 - as-number: 65101 - - id: branch-office2 - ce-device: ce1 - ce-interface: GigabitEthernet0/11 - ip-network: 10.7.7.0/24 - bandwidth: 6000000 - as-number: 65102 - - id: branch-office3 - __state: absent - __state: in-sync -''' - -RETURN = ''' -changes: - description: List of changes - returned: always - type: complex - sample: - - path: "/l3vpn:vpn/l3vpn{example}/endpoint{office}/bandwidth" - from: '6000000' - to: '12000000' - type: set - contains: - path: - description: Path to value changed - returned: always - type: str - from: - description: Previous value if any, else null - returned: When previous value is present on value change - type: str - to: - description: Current value if any, else null. - returned: When new value is present on value change - type: - description: Type of change. create|delete|set|re-deploy -diffs: - description: List of sync changes - returned: always - type: complex - sample: - - path: "/l3vpn:vpn/l3vpn{example}" - diff: |2 - devices { - device pe3 { - config { - alu:service { - vprn 65101 { - bgp { - group example-ce6 { - - peer-as 65102; - + peer-as 65101; - } - } - } - } - } - } - } - contains: - path: - description: keypath to service changed - returned: always - type: str - diff: - description: configuration difference triggered the re-deploy - returned: always - type: str -''' - -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import State, ValueBuilder -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoConfig(object): - REQUIRED_VERSIONS = [ - (4, 5), - (4, 4, 3), - (4, 3, 8), - (4, 2, 7), - (3, 4, 12) - ] - - def __init__(self, check_mode, client, data): - self._check_mode = check_mode - self._client = client - self._data = data - - self._changes = [] - self._diffs = [] - - def main(self): - # build list of values from configured data - value_builder = ValueBuilder(self._client) - for key, value in self._data.items(): - value_builder.build('', key, value) - - self._data_write(value_builder.values) - - # check sync AFTER configuration is written - sync_values = self._sync_check(value_builder.values) - self._sync_ensure(sync_values) - - return self._changes, self._diffs - - def _data_write(self, values): - th = self._client.get_trans(mode='read_write') - - for value in values: - if value.state == State.SET: - self._client.set_value(th, value.path, value.value) - elif value.state == State.PRESENT: - self._client.create(th, value.path) - elif value.state == State.ABSENT: - self._client.delete(th, value.path) - - changes = self._client.get_trans_changes(th) - for change in changes: - if change['op'] == 'value_set': - self._changes.append({ - 'path': change['path'], - 'from': change['old'] or None, - 'to': change['value'], - 'type': 'set' - }) - elif change['op'] in ('created', 'deleted'): - self._changes.append({ - 'path': change['path'], - 'type': change['op'][:-1] - }) - - if len(changes) > 0: - warnings = self._client.validate_commit(th) - if len(warnings) > 0: - raise NsoException( - 'failed to validate transaction with warnings: {0}'.format( - ', '.join((str(warning) for warning in warnings))), {}) - - if self._check_mode or len(changes) == 0: - self._client.delete_trans(th) - else: - self._client.commit(th) - - def _sync_check(self, values): - sync_values = [] - - for value in values: - if value.state in (State.CHECK_SYNC, State.IN_SYNC): - action = 'check-sync' - elif value.state in (State.DEEP_CHECK_SYNC, State.DEEP_IN_SYNC): - action = 'deep-check-sync' - else: - action = None - - if action is not None: - action_path = '{0}/{1}'.format(value.path, action) - action_params = {'outformat': 'cli'} - resp = self._client.run_action(None, action_path, action_params) - if len(resp) > 0: - sync_values.append( - ValueBuilder.Value(value.path, value.state, resp[0]['value'])) - - return sync_values - - def _sync_ensure(self, sync_values): - for value in sync_values: - if value.state in (State.CHECK_SYNC, State.DEEP_CHECK_SYNC): - raise NsoException( - '{0} out of sync, diff {1}'.format(value.path, value.value), {}) - - action_path = '{0}/{1}'.format(value.path, 're-deploy') - if not self._check_mode: - result = self._client.run_action(None, action_path) - if not result: - raise NsoException( - 'failed to re-deploy {0}'.format(value.path), {}) - - self._changes.append({'path': value.path, 'type': 're-deploy'}) - self._diffs.append({'path': value.path, 'diff': value.value}) - - -def main(): - argument_spec = dict( - data=dict(required=True, type='dict') - ) - argument_spec.update(nso_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - p = module.params - - client = connect(p) - nso_config = NsoConfig(module.check_mode, client, p['data']) - try: - verify_version(client, NsoConfig.REQUIRED_VERSIONS) - - changes, diffs = nso_config.main() - client.logout() - - changed = len(changes) > 0 - module.exit_json( - changed=changed, changes=changes, diffs=diffs) - - except NsoException as ex: - client.logout() - module.fail_json(msg=ex.message) - except ModuleFailException as ex: - client.logout() - module.fail_json(msg=ex.message) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/nso/nso_query.py b/plugins/modules/network/nso/nso_query.py deleted file mode 100644 index 9f03551dee..0000000000 --- a/plugins/modules/network/nso/nso_query.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified' -} - -DOCUMENTATION = ''' ---- -module: nso_query -extends_documentation_fragment: -- community.general.nso - -short_description: Query data from Cisco NSO. -description: - - This module provides support for querying data from Cisco NSO using XPath. -requirements: - - Cisco NSO version 3.4 or higher. -author: "Claes Nästén (@cnasten)" -options: - xpath: - description: XPath selection relative to the root. - required: true - fields: - description: > - List of fields to select from matching nodes. - required: true -''' - -EXAMPLES = ''' -- name: Select device name and description - nso_query: - url: http://localhost:8080/jsonrpc - username: username - password: password - xpath: /ncs:devices/device - fields: - - name - - description -''' - -RETURN = ''' -output: - description: Value of matching nodes - returned: success - type: list -''' - -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoQuery(object): - REQUIRED_VERSIONS = [ - (3, 4) - ] - - def __init__(self, check_mode, client, xpath, fields): - self._check_mode = check_mode - self._client = client - self._xpath = xpath - self._fields = fields - - def main(self): - if self._check_mode: - return [] - else: - return self._client.query(self._xpath, self._fields) - - -def main(): - argument_spec = dict( - xpath=dict(required=True, type='str'), - fields=dict(required=True, type='list') - ) - argument_spec.update(nso_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - p = module.params - - client = connect(p) - nso_query = NsoQuery( - module.check_mode, client, - p['xpath'], p['fields']) - try: - verify_version(client, NsoQuery.REQUIRED_VERSIONS) - - output = nso_query.main() - client.logout() - module.exit_json(changed=False, output=output) - except NsoException as ex: - client.logout() - module.fail_json(msg=ex.message) - except ModuleFailException as ex: - client.logout() - module.fail_json(msg=ex.message) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/nso/nso_show.py b/plugins/modules/network/nso/nso_show.py deleted file mode 100644 index 4ad0c8e0f1..0000000000 --- a/plugins/modules/network/nso/nso_show.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified' -} - -DOCUMENTATION = ''' ---- -module: nso_show -extends_documentation_fragment: -- community.general.nso - -short_description: Displays data from Cisco NSO. -description: - - This module provides support for displaying data from Cisco NSO. -requirements: - - Cisco NSO version 3.4.12 or higher, 4.1.9 or higher, 4.2.6 or higher, - 4.3.7 or higher, 4.4.5 or higher, 4.5 or higher. -author: "Claes Nästén (@cnasten)" -options: - path: - description: Path to NSO data. - required: true - operational: - description: > - Controls whether or not operational data is included in the result. - type: bool - default: false -''' - -EXAMPLES = ''' -- name: Show devices including operational data - nso_show: - url: http://localhost:8080/jsonrpc - username: username - password: password - path: /ncs:devices/device - operational: true -''' - -RETURN = ''' -output: - description: Configuration - returned: success - type: dict -''' - -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoShow(object): - REQUIRED_VERSIONS = [ - (4, 5), - (4, 4, 5), - (4, 3, 7), - (4, 2, 6), - (4, 1, 9), - (3, 4, 12) - ] - - def __init__(self, check_mode, client, path, operational): - self._check_mode = check_mode - self._client = client - self._path = path - self._operational = operational - - def main(self): - if self._check_mode: - return {} - else: - return self._client.show_config(self._path, self._operational) - - -def main(): - argument_spec = dict( - path=dict(required=True, type='str'), - operational=dict(required=False, type='bool', default=False) - ) - argument_spec.update(nso_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - p = module.params - - client = connect(p) - nso_show = NsoShow( - module.check_mode, client, - p['path'], p['operational']) - try: - verify_version(client, NsoShow.REQUIRED_VERSIONS) - - output = nso_show.main() - client.logout() - module.exit_json(changed=False, output=output) - except NsoException as ex: - client.logout() - module.fail_json(msg=ex.message) - except ModuleFailException as ex: - client.logout() - module.fail_json(msg=ex.message) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/nso/nso_verify.py b/plugins/modules/network/nso/nso_verify.py deleted file mode 100644 index a32479dfa1..0000000000 --- a/plugins/modules/network/nso/nso_verify.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2017 Cisco and/or its affiliates. -# -# 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 . -# - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified' -} - -DOCUMENTATION = ''' ---- -module: nso_verify -extends_documentation_fragment: -- community.general.nso - -short_description: Verifies Cisco NSO configuration. -description: - - This module provides support for verifying Cisco NSO configuration is in - compliance with specified values. -requirements: - - Cisco NSO version 3.4.12 or higher, 4.2.7 or higher, - 4.3.8 or higher, 4.4.3 or higher, 4.5 or higher. -author: "Claes Nästén (@cnasten)" -options: - data: - description: > - NSO data in format as C(| display json) converted to YAML. List entries can - be annotated with a C(__state) entry. Set to in-sync/deep-in-sync for - services to verify service is in sync with the network. Set to absent in - list entries to ensure they are deleted if they exist in NSO. - required: true -''' - -EXAMPLES = ''' -- name: Verify interface is up - nso_config: - url: http://localhost:8080/jsonrpc - username: username - password: password - data: - ncs:devices: - device: - - name: ce0 - live-status: - interfaces: - interface: - - name: GigabitEthernet0/12 - - state: Up -''' - -RETURN = ''' -violations: - description: List of value violations - returned: failed - type: complex - sample: - - path: /ncs:devices/device{ce0}/description - expected-value: CE0 example - value: null - contains: - path: - description: Path to the value in violation - returned: always - type: str - expected-value: - description: Expected value of path - returned: always - type: str - value: - description: Current value of path - returned: always - type: str -''' - -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import connect, verify_version, nso_argument_spec -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import normalize_value -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import State, ValueBuilder -from ansible_collections.community.general.plugins.module_utils.network.nso.nso import ModuleFailException, NsoException -from ansible.module_utils.basic import AnsibleModule - - -class NsoVerify(object): - REQUIRED_VERSIONS = [ - (4, 5), - (4, 4, 3), - (4, 3, 8), - (4, 2, 7), - (3, 4, 12) - ] - - def __init__(self, client, data): - self._client = client - self._data = data - - def main(self): - violations = [] - - # build list of values from configured data - value_builder = ValueBuilder(self._client, 'verify') - for key, value in self._data.items(): - value_builder.build('', key, value) - - for expected_value in value_builder.values: - if expected_value.state == State.PRESENT: - violations.append({ - 'path': expected_value.path, - 'expected-value': 'present', - 'value': 'absent' - }) - elif expected_value.state == State.ABSENT: - violations.append({ - 'path': expected_value.path, - 'expected-value': 'absent', - 'value': 'present' - }) - elif expected_value.state == State.SET: - try: - value = self._client.get_value(expected_value.path)['value'] - except NsoException as ex: - if ex.error.get('type', '') == 'data.not_found': - value = None - else: - raise - - # handle different types properly - n_value = normalize_value( - expected_value.value, value, expected_value.path) - if n_value != expected_value.value: - # if the value comparison fails, try mapping identityref - value_type = value_builder.get_type(expected_value.path) - if value_type is not None and 'identityref' in value_type: - n_value, t_value = self.get_prefix_name(value) - - if expected_value.value != n_value: - violations.append({ - 'path': expected_value.path, - 'expected-value': expected_value.value, - 'value': n_value - }) - else: - raise ModuleFailException( - 'value state {0} not supported at {1}'.format( - expected_value.state, expected_value.path)) - - return violations - - -def main(): - argument_spec = dict( - data=dict(required=True, type='dict') - ) - argument_spec.update(nso_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - p = module.params - - client = connect(p) - nso_verify = NsoVerify(client, p['data']) - try: - verify_version(client, NsoVerify.REQUIRED_VERSIONS) - - violations = nso_verify.main() - client.logout() - - num_violations = len(violations) - if num_violations > 0: - msg = '{0} value{1} differ'.format( - num_violations, num_violations > 1 and 's' or '') - module.fail_json(msg=msg, violations=violations) - else: - module.exit_json(changed=False) - - except NsoException as ex: - client.logout() - module.fail_json(msg=ex.message) - except ModuleFailException as ex: - client.logout() - module.fail_json(msg=ex.message) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/nuage/nuage_vspk.py b/plugins/modules/network/nuage/nuage_vspk.py deleted file mode 100644 index a472a6ae39..0000000000 --- a/plugins/modules/network/nuage/nuage_vspk.py +++ /dev/null @@ -1,1020 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2017, Nokia -# 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: nuage_vspk -short_description: Manage Nuage VSP environments -description: - - Manage or find Nuage VSP entities, this includes create, update, delete, assign, unassign and find, with all supported properties. -author: Philippe Dellaert (@pdellaert) -options: - auth: - description: - - Dict with the authentication information required to connect to a Nuage VSP environment. - - Requires a I(api_username) parameter (example csproot). - - Requires either a I(api_password) parameter (example csproot) or a I(api_certificate) and I(api_key) parameters, - which point to the certificate and key files for certificate based authentication. - - Requires a I(api_enterprise) parameter (example csp). - - Requires a I(api_url) parameter (example https://10.0.0.10:8443). - - Requires a I(api_version) parameter (example v4_0). - required: true - type: - description: - - The type of entity you want to work on (example Enterprise). - - This should match the objects CamelCase class name in VSPK-Python. - - This Class name can be found on U(https://nuagenetworks.github.io/vspkdoc/index.html). - required: true - id: - description: - - The ID of the entity you want to work on. - - In combination with I(command=find), it will only return the single entity. - - In combination with I(state), it will either update or delete this entity. - - Will take precedence over I(match_filter) and I(properties) whenever an entity needs to be found. - parent_id: - description: - - The ID of the parent of the entity you want to work on. - - When I(state) is specified, the entity will be gathered from this parent, if it exists, unless an I(id) is specified. - - When I(command=find) is specified, the entity will be searched for in this parent, unless an I(id) is specified. - - If specified, I(parent_type) also needs to be specified. - parent_type: - description: - - The type of parent the ID is specified for (example Enterprise). - - This should match the objects CamelCase class name in VSPK-Python. - - This Class name can be found on U(https://nuagenetworks.github.io/vspkdoc/index.html). - - If specified, I(parent_id) also needs to be specified. - state: - description: - - Specifies the desired state of the entity. - - If I(state=present), in case the entity already exists, will update the entity if it is needed. - - If I(state=present), in case the relationship with the parent is a member relationship, will assign the entity as a member of the parent. - - If I(state=absent), in case the relationship with the parent is a member relationship, will unassign the entity as a member of the parent. - - Either I(state) or I(command) needs to be defined, both can not be defined at the same time. - choices: - - present - - absent - command: - description: - - Specifies a command to be executed. - - With I(command=find), if I(parent_id) and I(parent_type) are defined, it will only search within the parent. Otherwise, if allowed, - will search in the root object. - - With I(command=find), if I(id) is specified, it will only return the single entity matching the id. - - With I(command=find), otherwise, if I(match_filter) is define, it will use that filter to search. - - With I(command=find), otherwise, if I(properties) are defined, it will do an AND search using all properties. - - With I(command=change_password), a password of a user can be changed. Warning - In case the password is the same as the existing, - it will throw an error. - - With I(command=wait_for_job), the module will wait for a job to either have a status of SUCCESS or ERROR. In case an ERROR status is found, - the module will exit with an error. - - With I(command=wait_for_job), the job will always be returned, even if the state is ERROR situation. - - Either I(state) or I(command) needs to be defined, both can not be defined at the same time. - choices: - - find - - change_password - - wait_for_job - - get_csp_enterprise - match_filter: - description: - - A filter used when looking (both in I(command) and I(state) for entities, in the format the Nuage VSP API expects. - - If I(match_filter) is defined, it will take precedence over the I(properties), but not on the I(id) - properties: - description: - - Properties are the key, value pairs of the different properties an entity has. - - If no I(id) and no I(match_filter) is specified, these are used to find or determine if the entity exists. - children: - description: - - Can be used to specify a set of child entities. - - A mandatory property of each child is the I(type). - - Supported optional properties of each child are I(id), I(properties) and I(match_filter). - - The function of each of these properties is the same as in the general task definition. - - This can be used recursively - - Only useable in case I(state=present). -notes: - - Check mode is supported, but with some caveats. It will not do any changes, and if possible try to determine if it is able do what is requested. - - In case a parent id is provided from a previous task, it might be empty and if a search is possible on root, it will do so, which can impact performance. -requirements: - - Python 2.7 - - Supports Nuage VSP 4.0Rx & 5.x.y - - Proper VSPK-Python installed for your Nuage version - - Tested with NuageX U(https://nuagex.io) -''' - -EXAMPLES = ''' -# This can be executed as a single role, with the following vars -# vars: -# auth: -# api_username: csproot -# api_password: csproot -# api_enterprise: csp -# api_url: https://10.0.0.10:8443 -# api_version: v5_0 -# enterprise_name: Ansible-Enterprise -# enterprise_new_name: Ansible-Updated-Enterprise -# -# or, for certificate based authentication -# vars: -# auth: -# api_username: csproot -# api_certificate: /path/to/user-certificate.pem -# api_key: /path/to/user-Key.pem -# api_enterprise: csp -# api_url: https://10.0.0.10:8443 -# api_version: v5_0 -# enterprise_name: Ansible-Enterprise -# enterprise_new_name: Ansible-Updated-Enterprise - -# Creating a new enterprise -- name: Create Enterprise - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: Enterprise - state: present - properties: - name: "{{ enterprise_name }}-basic" - register: nuage_enterprise - -# Checking if an Enterprise with the new name already exists -- name: Check if an Enterprise exists with the new name - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: Enterprise - command: find - properties: - name: "{{ enterprise_new_name }}-basic" - ignore_errors: yes - register: nuage_check_enterprise - -# Updating an enterprise's name -- name: Update Enterprise name - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: Enterprise - id: "{{ nuage_enterprise.id }}" - state: present - properties: - name: "{{ enterprise_new_name }}-basic" - when: nuage_check_enterprise is failed - -# Creating a User in an Enterprise -- name: Create admin user - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: User - parent_id: "{{ nuage_enterprise.id }}" - parent_type: Enterprise - state: present - match_filter: "userName == 'ansible-admin'" - properties: - email: "ansible@localhost.local" - first_name: "Ansible" - last_name: "Admin" - password: "ansible-password" - user_name: "ansible-admin" - register: nuage_user - -# Updating password for User -- name: Update admin password - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: User - id: "{{ nuage_user.id }}" - command: change_password - properties: - password: "ansible-new-password" - ignore_errors: yes - -# Finding a group in an enterprise -- name: Find Administrators group in Enterprise - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: Group - parent_id: "{{ nuage_enterprise.id }}" - parent_type: Enterprise - command: find - properties: - name: "Administrators" - register: nuage_group - -# Assign the user to the group -- name: Assign admin user to administrators - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: User - id: "{{ nuage_user.id }}" - parent_id: "{{ nuage_group.id }}" - parent_type: Group - state: present - -# Creating multiple DomainTemplates -- name: Create multiple DomainTemplates - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: DomainTemplate - parent_id: "{{ nuage_enterprise.id }}" - parent_type: Enterprise - state: present - properties: - name: "{{ item }}" - description: "Created by Ansible" - with_items: - - "Template-1" - - "Template-2" - -# Finding all DomainTemplates -- name: Fetching all DomainTemplates - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: DomainTemplate - parent_id: "{{ nuage_enterprise.id }}" - parent_type: Enterprise - command: find - register: nuage_domain_templates - -# Deleting all DomainTemplates -- name: Deleting all found DomainTemplates - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: DomainTemplate - state: absent - id: "{{ item.ID }}" - with_items: "{{ nuage_domain_templates.entities }}" - when: nuage_domain_templates.entities is defined - -# Unassign user from group -- name: Unassign admin user to administrators - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: User - id: "{{ nuage_user.id }}" - parent_id: "{{ nuage_group.id }}" - parent_type: Group - state: absent - -# Deleting an enterprise -- name: Delete Enterprise - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: Enterprise - id: "{{ nuage_enterprise.id }}" - state: absent - -# Setup an enterprise with Children -- name: Setup Enterprise and domain structure - connection: local - nuage_vspk: - auth: "{{ nuage_auth }}" - type: Enterprise - state: present - properties: - name: "Child-based-Enterprise" - children: - - type: L2DomainTemplate - properties: - name: "Unmanaged-Template" - children: - - type: EgressACLTemplate - match_filter: "name == 'Allow All'" - properties: - name: "Allow All" - active: true - default_allow_ip: true - default_allow_non_ip: true - default_install_acl_implicit_rules: true - description: "Created by Ansible" - priority_type: "TOP" - - type: IngressACLTemplate - match_filter: "name == 'Allow All'" - properties: - name: "Allow All" - active: true - default_allow_ip: true - default_allow_non_ip: true - description: "Created by Ansible" - priority_type: "TOP" -''' - -RETURN = ''' -id: - description: The id of the entity that was found, created, updated or assigned. - returned: On state=present and command=find in case one entity was found. - type: str - sample: bae07d8d-d29c-4e2b-b6ba-621b4807a333 -entities: - description: A list of entities handled. Each element is the to_dict() of the entity. - returned: On state=present and find, with only one element in case of state=present or find=one. - type: list - sample: [{ - "ID": acabc435-3946-4117-a719-b8895a335830", - "assocEntityType": "DOMAIN", - "command": "BEGIN_POLICY_CHANGES", - "creationDate": 1487515656000, - "entityScope": "ENTERPRISE", - "externalID": null, - "lastUpdatedBy": "8a6f0e20-a4db-4878-ad84-9cc61756cd5e", - "lastUpdatedDate": 1487515656000, - "owner": "8a6f0e20-a4db-4878-ad84-9cc61756cd5e", - "parameters": null, - "parentID": "a22fddb9-3da4-4945-bd2e-9d27fe3d62e0", - "parentType": "domain", - "progress": 0.0, - "result": null, - "status": "RUNNING" - }] -''' - -import time - -try: - import importlib - HAS_IMPORTLIB = True -except ImportError: - HAS_IMPORTLIB = False - -try: - from bambou.exceptions import BambouHTTPError - HAS_BAMBOU = True -except ImportError: - HAS_BAMBOU = False - -from ansible.module_utils.basic import AnsibleModule - - -SUPPORTED_COMMANDS = ['find', 'change_password', 'wait_for_job', 'get_csp_enterprise'] -VSPK = None - - -class NuageEntityManager(object): - """ - This module is meant to manage an entity in a Nuage VSP Platform - """ - - def __init__(self, module): - self.module = module - self.auth = module.params['auth'] - self.api_username = None - self.api_password = None - self.api_enterprise = None - self.api_url = None - self.api_version = None - self.api_certificate = None - self.api_key = None - self.type = module.params['type'] - - self.state = module.params['state'] - self.command = module.params['command'] - self.match_filter = module.params['match_filter'] - self.entity_id = module.params['id'] - self.parent_id = module.params['parent_id'] - self.parent_type = module.params['parent_type'] - self.properties = module.params['properties'] - self.children = module.params['children'] - - self.entity = None - self.entity_class = None - self.parent = None - self.parent_class = None - self.entity_fetcher = None - - self.result = { - 'state': self.state, - 'id': self.entity_id, - 'entities': [] - } - self.nuage_connection = None - - self._verify_api() - self._verify_input() - self._connect_vspk() - self._find_parent() - - def _connect_vspk(self): - """ - Connects to a Nuage API endpoint - """ - try: - # Connecting to Nuage - if self.api_certificate and self.api_key: - self.nuage_connection = VSPK.NUVSDSession(username=self.api_username, enterprise=self.api_enterprise, api_url=self.api_url, - certificate=(self.api_certificate, self.api_key)) - else: - self.nuage_connection = VSPK.NUVSDSession(username=self.api_username, password=self.api_password, enterprise=self.api_enterprise, - api_url=self.api_url) - self.nuage_connection.start() - except BambouHTTPError as error: - self.module.fail_json(msg='Unable to connect to the API URL with given username, password and enterprise: {0}'.format(error)) - - def _verify_api(self): - """ - Verifies the API and loads the proper VSPK version - """ - # Checking auth parameters - if ('api_password' not in list(self.auth.keys()) or not self.auth['api_password']) and ('api_certificate' not in list(self.auth.keys()) or - 'api_key' not in list(self.auth.keys()) or - not self.auth['api_certificate'] or not self.auth['api_key']): - self.module.fail_json(msg='Missing api_password or api_certificate and api_key parameter in auth') - - self.api_username = self.auth['api_username'] - if 'api_password' in list(self.auth.keys()) and self.auth['api_password']: - self.api_password = self.auth['api_password'] - if 'api_certificate' in list(self.auth.keys()) and 'api_key' in list(self.auth.keys()) and self.auth['api_certificate'] and self.auth['api_key']: - self.api_certificate = self.auth['api_certificate'] - self.api_key = self.auth['api_key'] - self.api_enterprise = self.auth['api_enterprise'] - self.api_url = self.auth['api_url'] - self.api_version = self.auth['api_version'] - - try: - global VSPK - VSPK = importlib.import_module('vspk.{0:s}'.format(self.api_version)) - except ImportError: - self.module.fail_json(msg='vspk is required for this module, or the API version specified does not exist.') - - def _verify_input(self): - """ - Verifies the parameter input for types and parent correctness and necessary parameters - """ - - # Checking if type exists - try: - self.entity_class = getattr(VSPK, 'NU{0:s}'.format(self.type)) - except AttributeError: - self.module.fail_json(msg='Unrecognised type specified') - - if self.module.check_mode: - return - - if self.parent_type: - # Checking if parent type exists - try: - self.parent_class = getattr(VSPK, 'NU{0:s}'.format(self.parent_type)) - except AttributeError: - # The parent type does not exist, fail - self.module.fail_json(msg='Unrecognised parent type specified') - - fetcher = self.parent_class().fetcher_for_rest_name(self.entity_class.rest_name) - if fetcher is None: - # The parent has no fetcher, fail - self.module.fail_json(msg='Specified parent is not a valid parent for the specified type') - elif not self.entity_id: - # If there is an id, we do not need a parent because we'll interact directly with the entity - # If an assign needs to happen, a parent will have to be provided - # Root object is the parent - self.parent_class = VSPK.NUMe - fetcher = self.parent_class().fetcher_for_rest_name(self.entity_class.rest_name) - if fetcher is None: - self.module.fail_json(msg='No parent specified and root object is not a parent for the type') - - # Verifying if a password is provided in case of the change_password command: - if self.command and self.command == 'change_password' and 'password' not in self.properties.keys(): - self.module.fail_json(msg='command is change_password but the following are missing: password property') - - def _find_parent(self): - """ - Fetches the parent if needed, otherwise configures the root object as parent. Also configures the entity fetcher - Important notes: - - If the parent is not set, the parent is automatically set to the root object - - It the root object does not hold a fetcher for the entity, you have to provide an ID - - If you want to assign/unassign, you have to provide a valid parent - """ - self.parent = self.nuage_connection.user - - if self.parent_id: - self.parent = self.parent_class(id=self.parent_id) - try: - self.parent.fetch() - except BambouHTTPError as error: - self.module.fail_json(msg='Failed to fetch the specified parent: {0}'.format(error)) - - self.entity_fetcher = self.parent.fetcher_for_rest_name(self.entity_class.rest_name) - - def _find_entities(self, entity_id=None, entity_class=None, match_filter=None, properties=None, entity_fetcher=None): - """ - Will return a set of entities matching a filter or set of properties if the match_filter is unset. If the - entity_id is set, it will return only the entity matching that ID as the single element of the list. - :param entity_id: Optional ID of the entity which should be returned - :param entity_class: Optional class of the entity which needs to be found - :param match_filter: Optional search filter - :param properties: Optional set of properties the entities should contain - :param entity_fetcher: The fetcher for the entity type - :return: List of matching entities - """ - search_filter = '' - - if entity_id: - found_entity = entity_class(id=entity_id) - try: - found_entity.fetch() - except BambouHTTPError as error: - self.module.fail_json(msg='Failed to fetch the specified entity by ID: {0}'.format(error)) - - return [found_entity] - - elif match_filter: - search_filter = match_filter - elif properties: - # Building filter - for num, property_name in enumerate(properties): - if num > 0: - search_filter += ' and ' - search_filter += '{0:s} == "{1}"'.format(property_name, properties[property_name]) - - if entity_fetcher is not None: - try: - return entity_fetcher.get(filter=search_filter) - except BambouHTTPError: - pass - return [] - - def _find_entity(self, entity_id=None, entity_class=None, match_filter=None, properties=None, entity_fetcher=None): - """ - Finds a single matching entity that matches all the provided properties, unless an ID is specified, in which - case it just fetches the one item - :param entity_id: Optional ID of the entity which should be returned - :param entity_class: Optional class of the entity which needs to be found - :param match_filter: Optional search filter - :param properties: Optional set of properties the entities should contain - :param entity_fetcher: The fetcher for the entity type - :return: The first entity matching the criteria, or None if none was found - """ - search_filter = '' - if entity_id: - found_entity = entity_class(id=entity_id) - try: - found_entity.fetch() - except BambouHTTPError as error: - self.module.fail_json(msg='Failed to fetch the specified entity by ID: {0}'.format(error)) - - return found_entity - - elif match_filter: - search_filter = match_filter - elif properties: - # Building filter - for num, property_name in enumerate(properties): - if num > 0: - search_filter += ' and ' - search_filter += '{0:s} == "{1}"'.format(property_name, properties[property_name]) - - if entity_fetcher is not None: - try: - return entity_fetcher.get_first(filter=search_filter) - except BambouHTTPError: - pass - return None - - def handle_main_entity(self): - """ - Handles the Ansible task - """ - if self.command and self.command == 'find': - self._handle_find() - elif self.command and self.command == 'change_password': - self._handle_change_password() - elif self.command and self.command == 'wait_for_job': - self._handle_wait_for_job() - elif self.command and self.command == 'get_csp_enterprise': - self._handle_get_csp_enterprise() - elif self.state == 'present': - self._handle_present() - elif self.state == 'absent': - self._handle_absent() - self.module.exit_json(**self.result) - - def _handle_absent(self): - """ - Handles the Ansible task when the state is set to absent - """ - # Absent state - self.entity = self._find_entity(entity_id=self.entity_id, entity_class=self.entity_class, match_filter=self.match_filter, properties=self.properties, - entity_fetcher=self.entity_fetcher) - if self.entity and (self.entity_fetcher is None or self.entity_fetcher.relationship in ['child', 'root']): - # Entity is present, deleting - if self.module.check_mode: - self.result['changed'] = True - else: - self._delete_entity(self.entity) - self.result['id'] = None - elif self.entity and self.entity_fetcher.relationship == 'member': - # Entity is a member, need to check if already present - if self._is_member(entity_fetcher=self.entity_fetcher, entity=self.entity): - # Entity is not a member yet - if self.module.check_mode: - self.result['changed'] = True - else: - self._unassign_member(entity_fetcher=self.entity_fetcher, entity=self.entity, entity_class=self.entity_class, parent=self.parent, - set_output=True) - - def _handle_present(self): - """ - Handles the Ansible task when the state is set to present - """ - # Present state - self.entity = self._find_entity(entity_id=self.entity_id, entity_class=self.entity_class, match_filter=self.match_filter, properties=self.properties, - entity_fetcher=self.entity_fetcher) - # Determining action to take - if self.entity_fetcher is not None and self.entity_fetcher.relationship == 'member' and not self.entity: - self.module.fail_json(msg='Trying to assign an entity that does not exist') - elif self.entity_fetcher is not None and self.entity_fetcher.relationship == 'member' and self.entity: - # Entity is a member, need to check if already present - if not self._is_member(entity_fetcher=self.entity_fetcher, entity=self.entity): - # Entity is not a member yet - if self.module.check_mode: - self.result['changed'] = True - else: - self._assign_member(entity_fetcher=self.entity_fetcher, entity=self.entity, entity_class=self.entity_class, parent=self.parent, - set_output=True) - elif self.entity_fetcher is not None and self.entity_fetcher.relationship in ['child', 'root'] and not self.entity: - # Entity is not present as a child, creating - if self.module.check_mode: - self.result['changed'] = True - else: - self.entity = self._create_entity(entity_class=self.entity_class, parent=self.parent, properties=self.properties) - self.result['id'] = self.entity.id - self.result['entities'].append(self.entity.to_dict()) - - # Checking children - if self.children: - for child in self.children: - self._handle_child(child=child, parent=self.entity) - elif self.entity: - # Need to compare properties in entity and found entity - changed = self._has_changed(entity=self.entity, properties=self.properties) - - if self.module.check_mode: - self.result['changed'] = changed - elif changed: - self.entity = self._save_entity(entity=self.entity) - self.result['id'] = self.entity.id - self.result['entities'].append(self.entity.to_dict()) - else: - self.result['id'] = self.entity.id - self.result['entities'].append(self.entity.to_dict()) - - # Checking children - if self.children: - for child in self.children: - self._handle_child(child=child, parent=self.entity) - elif not self.module.check_mode: - self.module.fail_json(msg='Invalid situation, verify parameters') - - def _handle_get_csp_enterprise(self): - """ - Handles the Ansible task when the command is to get the csp enterprise - """ - self.entity_id = self.parent.enterprise_id - self.entity = VSPK.NUEnterprise(id=self.entity_id) - try: - self.entity.fetch() - except BambouHTTPError as error: - self.module.fail_json(msg='Unable to fetch CSP enterprise: {0}'.format(error)) - self.result['id'] = self.entity_id - self.result['entities'].append(self.entity.to_dict()) - - def _handle_wait_for_job(self): - """ - Handles the Ansible task when the command is to wait for a job - """ - # Command wait_for_job - self.entity = self._find_entity(entity_id=self.entity_id, entity_class=self.entity_class, match_filter=self.match_filter, properties=self.properties, - entity_fetcher=self.entity_fetcher) - if self.module.check_mode: - self.result['changed'] = True - else: - self._wait_for_job(self.entity) - - def _handle_change_password(self): - """ - Handles the Ansible task when the command is to change a password - """ - # Command change_password - self.entity = self._find_entity(entity_id=self.entity_id, entity_class=self.entity_class, match_filter=self.match_filter, properties=self.properties, - entity_fetcher=self.entity_fetcher) - if self.module.check_mode: - self.result['changed'] = True - else: - try: - getattr(self.entity, 'password') - except AttributeError: - self.module.fail_json(msg='Entity does not have a password property') - - try: - setattr(self.entity, 'password', self.properties['password']) - except AttributeError: - self.module.fail_json(msg='Password can not be changed for entity') - - self.entity = self._save_entity(entity=self.entity) - self.result['id'] = self.entity.id - self.result['entities'].append(self.entity.to_dict()) - - def _handle_find(self): - """ - Handles the Ansible task when the command is to find an entity - """ - # Command find - entities = self._find_entities(entity_id=self.entity_id, entity_class=self.entity_class, match_filter=self.match_filter, properties=self.properties, - entity_fetcher=self.entity_fetcher) - self.result['changed'] = False - if entities: - if len(entities) == 1: - self.result['id'] = entities[0].id - for entity in entities: - self.result['entities'].append(entity.to_dict()) - elif not self.module.check_mode: - self.module.fail_json(msg='Unable to find matching entries') - - def _handle_child(self, child, parent): - """ - Handles children of a main entity. Fields are similar to the normal fields - Currently only supported state: present - """ - if 'type' not in list(child.keys()): - self.module.fail_json(msg='Child type unspecified') - elif 'id' not in list(child.keys()) and 'properties' not in list(child.keys()): - self.module.fail_json(msg='Child ID or properties unspecified') - - # Setting intern variables - child_id = None - if 'id' in list(child.keys()): - child_id = child['id'] - child_properties = None - if 'properties' in list(child.keys()): - child_properties = child['properties'] - child_filter = None - if 'match_filter' in list(child.keys()): - child_filter = child['match_filter'] - - # Checking if type exists - entity_class = None - try: - entity_class = getattr(VSPK, 'NU{0:s}'.format(child['type'])) - except AttributeError: - self.module.fail_json(msg='Unrecognised child type specified') - - entity_fetcher = parent.fetcher_for_rest_name(entity_class.rest_name) - if entity_fetcher is None and not child_id and not self.module.check_mode: - self.module.fail_json(msg='Unable to find a fetcher for child, and no ID specified.') - - # Try and find the child - entity = self._find_entity(entity_id=child_id, entity_class=entity_class, match_filter=child_filter, properties=child_properties, - entity_fetcher=entity_fetcher) - - # Determining action to take - if entity_fetcher.relationship == 'member' and not entity: - self.module.fail_json(msg='Trying to assign a child that does not exist') - elif entity_fetcher.relationship == 'member' and entity: - # Entity is a member, need to check if already present - if not self._is_member(entity_fetcher=entity_fetcher, entity=entity): - # Entity is not a member yet - if self.module.check_mode: - self.result['changed'] = True - else: - self._assign_member(entity_fetcher=entity_fetcher, entity=entity, entity_class=entity_class, parent=parent, set_output=False) - elif entity_fetcher.relationship in ['child', 'root'] and not entity: - # Entity is not present as a child, creating - if self.module.check_mode: - self.result['changed'] = True - else: - entity = self._create_entity(entity_class=entity_class, parent=parent, properties=child_properties) - elif entity_fetcher.relationship in ['child', 'root'] and entity: - changed = self._has_changed(entity=entity, properties=child_properties) - - if self.module.check_mode: - self.result['changed'] = changed - elif changed: - entity = self._save_entity(entity=entity) - - if entity: - self.result['entities'].append(entity.to_dict()) - - # Checking children - if 'children' in list(child.keys()) and not self.module.check_mode: - for subchild in child['children']: - self._handle_child(child=subchild, parent=entity) - - def _has_changed(self, entity, properties): - """ - Compares a set of properties with a given entity, returns True in case the properties are different from the - values in the entity - :param entity: The entity to check - :param properties: The properties to check - :return: boolean - """ - # Need to compare properties in entity and found entity - changed = False - if properties: - for property_name in list(properties.keys()): - if property_name == 'password': - continue - entity_value = '' - try: - entity_value = getattr(entity, property_name) - except AttributeError: - self.module.fail_json(msg='Property {0:s} is not valid for this type of entity'.format(property_name)) - - if entity_value != properties[property_name]: - # Difference in values changing property - changed = True - try: - setattr(entity, property_name, properties[property_name]) - except AttributeError: - self.module.fail_json(msg='Property {0:s} can not be changed for this type of entity'.format(property_name)) - return changed - - def _is_member(self, entity_fetcher, entity): - """ - Verifies if the entity is a member of the parent in the fetcher - :param entity_fetcher: The fetcher for the entity type - :param entity: The entity to look for as a member in the entity fetcher - :return: boolean - """ - members = entity_fetcher.get() - for member in members: - if member.id == entity.id: - return True - return False - - def _assign_member(self, entity_fetcher, entity, entity_class, parent, set_output): - """ - Adds the entity as a member to a parent - :param entity_fetcher: The fetcher of the entity type - :param entity: The entity to add as a member - :param entity_class: The class of the entity - :param parent: The parent on which to add the entity as a member - :param set_output: If set to True, sets the Ansible result variables - """ - members = entity_fetcher.get() - members.append(entity) - try: - parent.assign(members, entity_class) - except BambouHTTPError as error: - self.module.fail_json(msg='Unable to assign entity as a member: {0}'.format(error)) - self.result['changed'] = True - if set_output: - self.result['id'] = entity.id - self.result['entities'].append(entity.to_dict()) - - def _unassign_member(self, entity_fetcher, entity, entity_class, parent, set_output): - """ - Removes the entity as a member of a parent - :param entity_fetcher: The fetcher of the entity type - :param entity: The entity to remove as a member - :param entity_class: The class of the entity - :param parent: The parent on which to add the entity as a member - :param set_output: If set to True, sets the Ansible result variables - """ - members = [] - for member in entity_fetcher.get(): - if member.id != entity.id: - members.append(member) - try: - parent.assign(members, entity_class) - except BambouHTTPError as error: - self.module.fail_json(msg='Unable to remove entity as a member: {0}'.format(error)) - self.result['changed'] = True - if set_output: - self.result['id'] = entity.id - self.result['entities'].append(entity.to_dict()) - - def _create_entity(self, entity_class, parent, properties): - """ - Creates a new entity in the parent, with all properties configured as in the file - :param entity_class: The class of the entity - :param parent: The parent of the entity - :param properties: The set of properties of the entity - :return: The entity - """ - entity = entity_class(**properties) - try: - parent.create_child(entity) - except BambouHTTPError as error: - self.module.fail_json(msg='Unable to create entity: {0}'.format(error)) - self.result['changed'] = True - return entity - - def _save_entity(self, entity): - """ - Updates an existing entity - :param entity: The entity to save - :return: The updated entity - """ - try: - entity.save() - except BambouHTTPError as error: - self.module.fail_json(msg='Unable to update entity: {0}'.format(error)) - self.result['changed'] = True - return entity - - def _delete_entity(self, entity): - """ - Deletes an entity - :param entity: The entity to delete - """ - try: - entity.delete() - except BambouHTTPError as error: - self.module.fail_json(msg='Unable to delete entity: {0}'.format(error)) - self.result['changed'] = True - - def _wait_for_job(self, entity): - """ - Waits for a job to finish - :param entity: The job to wait for - """ - running = False - if entity.status == 'RUNNING': - self.result['changed'] = True - running = True - - while running: - time.sleep(1) - entity.fetch() - - if entity.status != 'RUNNING': - running = False - - self.result['entities'].append(entity.to_dict()) - if entity.status == 'ERROR': - self.module.fail_json(msg='Job ended in an error') - - -def main(): - """ - Main method - """ - module = AnsibleModule( - argument_spec=dict( - auth=dict( - required=True, - type='dict', - options=dict( - api_username=dict(required=True, type='str'), - api_enterprise=dict(required=True, type='str'), - api_url=dict(required=True, type='str'), - api_version=dict(required=True, type='str'), - api_password=dict(default=None, required=False, type='str', no_log=True), - api_certificate=dict(default=None, required=False, type='str', no_log=True), - api_key=dict(default=None, required=False, type='str', no_log=True) - ) - ), - type=dict(required=True, type='str'), - id=dict(default=None, required=False, type='str'), - parent_id=dict(default=None, required=False, type='str'), - parent_type=dict(default=None, required=False, type='str'), - state=dict(default=None, choices=['present', 'absent'], type='str'), - command=dict(default=None, choices=SUPPORTED_COMMANDS, type='str'), - match_filter=dict(default=None, required=False, type='str'), - properties=dict(default=None, required=False, type='dict'), - children=dict(default=None, required=False, type='list') - ), - mutually_exclusive=[ - ['command', 'state'] - ], - required_together=[ - ['parent_id', 'parent_type'] - ], - required_one_of=[ - ['command', 'state'] - ], - required_if=[ - ['state', 'present', ['id', 'properties', 'match_filter'], True], - ['state', 'absent', ['id', 'properties', 'match_filter'], True], - ['command', 'change_password', ['id', 'properties']], - ['command', 'wait_for_job', ['id']] - ], - supports_check_mode=True - ) - - if not HAS_BAMBOU: - module.fail_json(msg='bambou is required for this module') - - if not HAS_IMPORTLIB: - module.fail_json(msg='importlib (python 2.7) is required for this module') - - entity_manager = NuageEntityManager(module) - entity_manager.handle_main_entity() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_aaa.py b/plugins/modules/network/onyx/onyx_aaa.py deleted file mode 100644 index 46080f0351..0000000000 --- a/plugins/modules/network/onyx/onyx_aaa.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_aaa -author: "Sara Touqan (@sarato)" -short_description: Configures AAA parameters -description: - - This module provides declarative management of AAA protocol params - on Mellanox ONYX network devices. -options: - tacacs_accounting_enabled: - description: - - Configures accounting settings. - type: bool - auth_default_user: - description: - - Sets local user default mapping. - type: str - choices: ['admin', 'monitor'] - auth_order: - description: - - Sets the order on how to handle remote to local user mappings. - type: str - choices: ['local-only', 'remote-first', 'remote-only'] - auth_fallback_enabled: - description: - - Enables/Disables fallback server-err option. - type: bool -''' - -EXAMPLES = """ -- name: configures aaa - onyx_aaa: - tacacs_accounting_enabled: yes - auth_default_user: monitor - auth_order: local-only - auth_fallback_enabled: false -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - aaa accounting changes default stop-only tacacs+ - - no aaa accounting changes default stop-only tacacs+ - - aaa authorization map default-user - - aaa authorization map order - - aaa authorization map fallback server-err - - no aaa authorization map fallback server-err -""" - -import re - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxAAAModule(BaseOnyxModule): - - def init_module(self): - """ initialize module - """ - element_spec = dict( - tacacs_accounting_enabled=dict(type='bool'), - auth_default_user=dict(type='str', choices=['admin', 'monitor']), - auth_order=dict(type='str', choices=['local-only', 'remote-first', 'remote-only']), - auth_fallback_enabled=dict(type='bool') - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def _set_aaa_config(self, all_aaa_config): - aaa_config = all_aaa_config[0] - self._current_config['auth_default_user'] = aaa_config.get("Default User") - self._current_config['auth_order'] = aaa_config.get("Map Order") - auth_fallback_enabled = aaa_config.get("Fallback on server-err") - if auth_fallback_enabled == "yes": - self._current_config['auth_fallback_enabled'] = True - else: - self._current_config['auth_fallback_enabled'] = False - aaa_config_2 = all_aaa_config[2] - accounting_message = aaa_config_2.get("message") - if accounting_message == "No accounting methods configured.": - self._current_config['tacacs_accounting_enabled'] = False - else: - self._current_config['tacacs_accounting_enabled'] = True - - def _show_aaa_config(self): - cmd = "show aaa" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - aaa_config = self._show_aaa_config() - if aaa_config: - self._set_aaa_config(aaa_config) - - def generate_commands(self): - tacacs_accounting_enabled = self._required_config.get("tacacs_accounting_enabled") - if tacacs_accounting_enabled is not None: - current_accounting_enabled = self._current_config.get("tacacs_accounting_enabled") - if current_accounting_enabled != tacacs_accounting_enabled: - if tacacs_accounting_enabled is True: - self._commands.append('aaa accounting changes default stop-only tacacs+') - else: - self._commands.append('no aaa accounting changes default stop-only tacacs+') - - auth_default_user = self._required_config.get("auth_default_user") - if auth_default_user is not None: - current_user = self._current_config.get("auth_default_user") - if current_user != auth_default_user: - self._commands.append('aaa authorization map default-user {0}' .format(auth_default_user)) - - auth_order = self._required_config.get("auth_order") - if auth_order is not None: - current_order = self._current_config.get("auth_order") - if current_order != auth_order: - self._commands.append('aaa authorization map order {0}' .format(auth_order)) - - auth_fallback_enabled = self._required_config.get("auth_fallback_enabled") - if auth_fallback_enabled is not None: - current_fallback = self._current_config.get("auth_fallback_enabled") - if current_fallback != auth_fallback_enabled: - if auth_fallback_enabled is True: - self._commands.append('aaa authorization map fallback server-err') - else: - self._commands.append('no aaa authorization map fallback server-err') - - -def main(): - """ main entry point for module execution - """ - OnyxAAAModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_bfd.py b/plugins/modules/network/onyx/onyx_bfd.py deleted file mode 100644 index d82ce254b0..0000000000 --- a/plugins/modules/network/onyx/onyx_bfd.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_bfd -author: "Sara Touqan (@sarato)" -short_description: Configures BFD parameters -description: - - This module provides declarative management of BFD protocol params - on Mellanox ONYX network devices. -options: - shutdown: - description: - - Administratively shut down BFD protection. - type: bool - vrf: - description: - - Specifys the vrf name. - type: str - interval_min_rx: - description: - - Minimum desired receive rate, should be between 50 and 6000. - type: int - interval_multiplier: - description: - - Desired detection multiplier, should be between 3 and 50. - type: int - interval_transmit_rate: - description: - - Minimum desired transmit rate, should be between 50 and 60000. - type: int - iproute_network_prefix: - description: - - Configures the ip route network prefix, e.g 1.1.1.1. - type: str - iproute_mask_length: - description: - - Configures the mask length of the ip route network prefix, e.g 24. - type: int - iproute_next_hop: - description: - - Configures the ip route next hop, e.g 2.2.2.2. - type: str -''' - -EXAMPLES = """ -- name: configures bfd - onyx_bfd: - shutdown: yes - vrf: 5 - interval_min_rx: 55 - interval_multiplier: 8 - interval_transmit_rate: 88 - iproute_network_prefix: 1.1.1.0 - iproute_mask_length: 24 - iproute_next_hop: 3.2.2.2 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - ip bfd shutdown - - no ip bfd shutdown - - ip bfd shutdown vrf - - no ip bfd shutdown vrf - - ip bfd vrf interval min-rx multiplier transmit-rate force - - ip bfd interval min-rx multiplier transmit-rate force - - ip route vrf / bfd - - ip route / bfd -""" - -import re - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxBFDModule(BaseOnyxModule): - - def init_module(self): - """ initialize module - """ - element_spec = dict( - shutdown=dict(type='bool'), - vrf=dict(type='str'), - interval_min_rx=dict(type='int'), - interval_multiplier=dict(type='int'), - interval_transmit_rate=dict(type='int'), - iproute_network_prefix=dict(type='str'), - iproute_mask_length=dict(type='int'), - iproute_next_hop=dict(type='str'), - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_together=[ - ['interval_min_rx', 'interval_multiplier', 'interval_transmit_rate'], - ['iproute_network_prefix', 'iproute_mask_length', 'iproute_next_hop']]) - - def validate_bfd_interval_values(self): - interval_min_rx = self._required_config.get('interval_min_rx') - if interval_min_rx: - if ((interval_min_rx < 50) or (interval_min_rx > 6000)): - self._module.fail_json(msg='Receive interval should be between 50 and 6000.') - interval_multiplier = self._required_config.get('interval_multiplier') - if interval_multiplier: - if ((interval_multiplier < 3) or (interval_multiplier > 50)): - self._module.fail_json(msg='Multiplier should be between 3 and 50.') - interval_transmit_rate = self._required_config.get('interval_transmit_rate') - if interval_transmit_rate: - if ((interval_transmit_rate < 50) or (interval_transmit_rate > 60000)): - self._module.fail_json(msg='Transmit interval should be between 50 and 60000.') - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - self.validate_bfd_interval_values() - - def _set_bfd_config(self, bfd_config): - curr_config_arr = [] - bfd_config = bfd_config.get('Lines') - if bfd_config is None: - return - for runn_config in bfd_config: - curr_config_arr.append(runn_config.strip()) - if 'ip bfd shutdown vrf default' in curr_config_arr: - self._current_config['bfd_shutdown'] = True - else: - self._current_config['bfd_shutdown'] = False - self._current_config['curr_config_arr'] = curr_config_arr - - def _show_bfd_config(self): - cmd = "show running-config | include bfd" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - bfd_config = self._show_bfd_config() - if bfd_config: - self._set_bfd_config(bfd_config) - - def generate_shutdown_commands(self, curr_config_arr): - shutdown_enabled = self._required_config.get('shutdown') - vrf_name = self._required_config.get('vrf') - current_shutdown = self._current_config.get("bfd_shutdown") - if shutdown_enabled is not None: - if vrf_name is not None: - if curr_config_arr is not None: - if ('ip bfd shutdown vrf {0}' .format(vrf_name)) not in curr_config_arr: - if shutdown_enabled is True: - self._commands.append('ip bfd shutdown vrf {0}' .format(vrf_name)) - else: - if shutdown_enabled is False: - self._commands.append('no ip bfd shutdown vrf {0}' .format(vrf_name)) - else: - if ((current_shutdown is not None and (current_shutdown != shutdown_enabled)) or (current_shutdown is None)): - if shutdown_enabled is True: - self._commands.append('ip bfd shutdown') - else: - self._commands.append('no ip bfd shutdown') - - def generate_interval_commands(self, curr_config_arr): - interval_min_rx = self._required_config.get('interval_min_rx') - interval_multiplier = self._required_config.get('interval_multiplier') - interval_transmit_rate = self._required_config.get('interval_transmit_rate') - vrf_name = self._required_config.get('vrf') - if ((interval_min_rx is not None) and (interval_multiplier is not None) and (interval_transmit_rate is not None)): - if vrf_name is not None: - if curr_config_arr is not None: - if ((('ip bfd vrf {0} interval transmit-rate {1} force' .format(vrf_name, interval_transmit_rate)) not in curr_config_arr) or - (('ip bfd vrf {0} interval min-rx {1} force' .format(vrf_name, interval_min_rx)) not in curr_config_arr) or - (('ip bfd vrf {0} interval multiplier {1} force' .format(vrf_name, interval_multiplier)) not in curr_config_arr)): - self._commands.append('ip bfd vrf {0} interval min-rx {1} multiplier {2} transmit-rate {3} force' - .format(vrf_name, interval_min_rx, interval_multiplier, interval_transmit_rate)) - else: - self._commands.append('ip bfd vrf {0} interval min-rx {1} multiplier {2} transmit-rate {3} force' - .format(vrf_name, interval_min_rx, interval_multiplier, interval_transmit_rate)) - else: - if curr_config_arr is not None: - if ((('ip bfd vrf default interval transmit-rate {0} force' .format(interval_transmit_rate)) not in curr_config_arr) or - (('ip bfd vrf default interval min-rx {0} force' .format(interval_min_rx)) not in curr_config_arr) or - (('ip bfd vrf default interval multiplier {0} force' .format(interval_multiplier)) not in curr_config_arr)): - self._commands.append('ip bfd interval min-rx {0} multiplier {1} transmit-rate {2} force' - .format(interval_min_rx, interval_multiplier, interval_transmit_rate)) - else: - self._commands.append('ip bfd interval min-rx {0} multiplier {1} transmit-rate {2} force' - .format(interval_min_rx, interval_multiplier, interval_transmit_rate)) - - def generate_iproute_commands(self, curr_config_arr): - iproute_network_prefix = self._required_config.get('iproute_network_prefix') - iproute_mask_length = self._required_config.get('iproute_mask_length') - iproute_next_hop = self._required_config.get('iproute_next_hop') - vrf_name = self._required_config.get('vrf') - if ((iproute_network_prefix is not None) and (iproute_mask_length is not None) and - (iproute_next_hop is not None)): - if vrf_name is not None: - if curr_config_arr is not None: - if ('ip route vrf {0} {1}/{2} {3} bfd' .format(vrf_name, iproute_network_prefix, - iproute_mask_length, iproute_next_hop)) not in curr_config_arr: - self._commands.append('ip route vrf {0} {1} /{2} {3} bfd' - .format(vrf_name, iproute_network_prefix, iproute_mask_length, iproute_next_hop)) - else: - self._commands.append('ip route vrf {0} {1} /{2} {3} bfd' .format(vrf_name, iproute_network_prefix, iproute_mask_length, iproute_next_hop)) - else: - if curr_config_arr is not None: - if ('ip route vrf default {0}/{1} {2} bfd' .format(iproute_network_prefix, - iproute_mask_length, iproute_next_hop)) not in curr_config_arr: - self._commands.append('ip route {0} /{1} {2} bfd' .format(iproute_network_prefix, iproute_mask_length, iproute_next_hop)) - else: - self._commands.append('ip route {0} /{1} {2} bfd' .format(iproute_network_prefix, iproute_mask_length, iproute_next_hop)) - - def generate_commands(self): - curr_config_arr = self._current_config.get("curr_config_arr") - self.generate_shutdown_commands(curr_config_arr) - self.generate_interval_commands(curr_config_arr) - self.generate_iproute_commands(curr_config_arr) - - -def main(): - """ main entry point for module execution - """ - OnyxBFDModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_bgp.py b/plugins/modules/network/onyx/onyx_bgp.py deleted file mode 100644 index 99ee0299fa..0000000000 --- a/plugins/modules/network/onyx/onyx_bgp.py +++ /dev/null @@ -1,450 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_bgp -author: "Samer Deeb (@samerd), Anas Badaha (@anasb)" -short_description: Configures BGP on Mellanox ONYX network devices -description: - - This module provides declarative management of BGP router and neighbors - on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.4000 -options: - as_number: - description: - - Local AS number. - required: true - router_id: - description: - - Router IP address. - neighbors: - description: - - List of neighbors. Required if I(state=present). - suboptions: - remote_as: - description: - - Remote AS number. - required: true - neighbor: - description: - - Neighbor IP address. - required: true - multihop: - description: - - multihop number. - networks: - description: - - List of advertised networks. - fast_external_fallover: - description: - - will configure fast_external_fallover when it is True. - type: bool - max_paths: - description: - - Maximum bgp paths. - ecmp_bestpath: - description: - - Enables ECMP across AS paths. - type: bool - evpn: - description: - - Configure evpn peer-group. - type: bool - vrf: - description: - - vrf name. - state: - description: - - BGP state. - default: present - choices: ['present', 'absent'] - purge: - description: - - will remove all neighbors when it is True. - type: bool - default: false -''' - -EXAMPLES = """ -- name: configure bgp - onyx_bgp: - as_number: 320 - router_id: 10.3.3.3 - neighbors: - - remote_as: 321 - neighbor: 10.3.3.4 - - remote_as: 322 - neighbor: 10.3.3.5 - multihop: 250 - purge: True - state: present - networks: - - 172.16.1.0/24 - vrf: default - evpn: yes - fast_external_fallover: yes - max_paths: 32 - ecmp_bestpath: yes - -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - router bgp 320 vrf default - - exit - - router bgp 320 router-id 10.3.3.3 force - - router bgp 320 vrf default bgp fast-external-fallover - - router bgp 320 vrf default maximum-paths 32 - - router bgp 320 vrf default bestpath as-path multipath-relax force - - router bgp 320 vrf default neighbor evpn peer-group - - router bgp 320 vrf default neighbor evpn send-community extended - - router bgp 320 vrf default address-family l2vpn-evpn neighbor evpn next-hop-unchanged - - router bgp 320 vrf default address-family l2vpn-evpn neighbor evpn activate - - router bgp 320 vrf default address-family l2vpn-evpn auto-create - - router bgp 320 vrf default neighbor 10.3.3.4 remote-as 321 - - router bgp 320 vrf default neighbor 10.3.3.4 ebgp-multihop 250 - - router bgp 320 vrf default neighbor 10.3.3.5 remote-as 322 - - router bgp 320 vrf default network 172.16.1.0 /24 -""" -import re -from ansible.module_utils.six import iteritems - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_bgp_summary -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxBgpModule(BaseOnyxModule): - LOCAL_AS_REGEX = re.compile(r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+).*') - ROUTER_ID_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+).*router-id\s+(\S+)\s+.*') - NEIGHBOR_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+).*neighbor\s+(\S+)\s+remote\-as\s+(\d+).*') - NEIGHBOR_MULTIHOP_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+).*neighbor\s+(\S+)\s+ebgp\-multihop\s+(\d+).*') - NETWORK_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+).*network\s+(\S+)\s+(\S+).*') - FAST_EXTERNAL_FALLOVER_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+bgp fast\-external\-fallover.*') - MAX_PATHS_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+maximum\-paths\s+(\d+).*') - ECMP_BESTPATH_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+bestpath as\-path multipath\-relax.*') - NEIGHBOR_EVPN_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+neighbor\s+(\S+)\s+peer\-group evpn.*') - EVPN_PEER_GROUP_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+neighbor evpn peer\-group.*') - EVPN_SEND_COMMUNITY_EXTENDED_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+neighbor evpn send-community extended.*') - EVPN_NEXT_HOP_UNCHANGED_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+address\-family l2vpn\-evpn neighbor evpn next\-hop-unchanged.*') - EVPN_ACTIVATE_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+address-family l2vpn\-evpn neighbor evpn activate.*') - EVPN_AUTO_CREATE_REGEX = re.compile( - r'^\s.*router bgp\s+(\d+)\s+vrf\s+(\S+)\s+address-family l2vpn\-evpn auto-create.*') - - _purge = False - - EVPN_PEER_GROUP_ATTR = "evpn_peer_group" - EVPN_SEND_COMMUNITY_EXTENDED_ATTR = "evpn_send_community_extended" - EVPN_NEXT_HOP_UNCHANGED_ATTR = "evpn_next_hop_unchanged" - EVPN_ACTIVATE_ATTR = "evpn_activate" - EVPN_AUTO_CREATE_ATTR = "evpn_auto_create" - - EVPN_PEER_GROUP_CMD = "router bgp %s vrf %s neighbor evpn peer-group" - EVPN_SEND_COMMUNITY_EXTENDED_CMD = "router bgp %s vrf %s neighbor evpn send-community extended" - EVPN_NEXT_HOP_UNCHANGED_CMD = "router bgp %s vrf %s address-family l2vpn-evpn neighbor evpn next-hop-unchanged" - EVPN_ACTIVATE_CMD = "router bgp %s vrf %s address-family l2vpn-evpn neighbor evpn activate" - EVPN_AUTO_CREATE_CMD = "router bgp %s vrf %s address-family l2vpn-evpn auto-create" - - EVPN_ENABLE_ATTRS = [EVPN_PEER_GROUP_ATTR, EVPN_SEND_COMMUNITY_EXTENDED_ATTR, - EVPN_NEXT_HOP_UNCHANGED_ATTR, EVPN_ACTIVATE_ATTR, EVPN_AUTO_CREATE_ATTR] - - EVPN_DISABLE_ATTRS = [EVPN_PEER_GROUP_ATTR, EVPN_AUTO_CREATE_ATTR] - - EVPN_COMMANDS_REGEX_MAPPER = { - EVPN_PEER_GROUP_ATTR: (EVPN_PEER_GROUP_REGEX, EVPN_PEER_GROUP_CMD), - EVPN_SEND_COMMUNITY_EXTENDED_ATTR: (EVPN_SEND_COMMUNITY_EXTENDED_REGEX, - EVPN_SEND_COMMUNITY_EXTENDED_CMD), - EVPN_NEXT_HOP_UNCHANGED_ATTR: (EVPN_NEXT_HOP_UNCHANGED_REGEX, - EVPN_NEXT_HOP_UNCHANGED_CMD), - EVPN_ACTIVATE_ATTR: (EVPN_ACTIVATE_REGEX, EVPN_ACTIVATE_CMD), - EVPN_AUTO_CREATE_ATTR: (EVPN_AUTO_CREATE_REGEX, EVPN_AUTO_CREATE_CMD) - } - - def init_module(self): - """ initialize module - """ - neighbor_spec = dict( - remote_as=dict(type='int', required=True), - neighbor=dict(required=True), - multihop=dict(type='int') - ) - element_spec = dict( - as_number=dict(type='int', required=True), - router_id=dict(), - neighbors=dict(type='list', elements='dict', - options=neighbor_spec), - networks=dict(type='list', elements='str'), - state=dict(choices=['present', 'absent'], default='present'), - purge=dict(default=False, type='bool'), - vrf=dict(), - fast_external_fallover=dict(type='bool'), - max_paths=dict(type='int'), - ecmp_bestpath=dict(type='bool'), - evpn=dict(type='bool') - ) - argument_spec = dict() - - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self._purge = self._required_config.get('purge', False) - self.validate_param_values(self._required_config) - - def _set_bgp_config(self, bgp_config): - lines = bgp_config.split('\n') - self._current_config['router_id'] = None - self._current_config['as_number'] = None - self._current_config['fast_external_fallover'] = False - self._current_config['ecmp_bestpath'] = False - self._current_config[self.EVPN_PEER_GROUP_ATTR] = False - self._current_config[self.EVPN_SEND_COMMUNITY_EXTENDED_ATTR] = False - self._current_config[self.EVPN_NEXT_HOP_UNCHANGED_ATTR] = False - self._current_config[self.EVPN_AUTO_CREATE_ATTR] = False - self._current_config[self.EVPN_ACTIVATE_ATTR] = False - neighbors = self._current_config['neighbors'] = dict() - networks = self._current_config['networks'] = list() - for line in lines: - if line.startswith('#'): - continue - if not self._current_config['as_number']: - match = self.LOCAL_AS_REGEX.match(line) - if match: - self._current_config['as_number'] = int(match.group(1)) - self._current_config['vrf'] = match.group(2) - continue - if not self._current_config['router_id']: - match = self.ROUTER_ID_REGEX.match(line) - if match: - self._current_config['router_id'] = match.group(2) - continue - match = self.NEIGHBOR_REGEX.match(line) - if match: - neighbor = neighbors.setdefault(match.group(2), dict()) - neighbor['remote_as'] = int(match.group(3)) - continue - match = self.NEIGHBOR_MULTIHOP_REGEX.match(line) - if match: - neighbor = neighbors.setdefault(match.group(2), dict()) - neighbor["multihop"] = int(match.group(3)) - continue - match = self.NEIGHBOR_EVPN_REGEX.match(line) - if match: - neighbor = neighbors.setdefault(match.group(3), dict()) - neighbor["evpn"] = True - continue - match = self.NETWORK_REGEX.match(line) - if match: - network = match.group(2) + match.group(3) - networks.append(network) - continue - match = self.FAST_EXTERNAL_FALLOVER_REGEX.match(line) - if match: - self._current_config['fast_external_fallover'] = True - continue - match = self.ECMP_BESTPATH_REGEX.match(line) - if match: - self._current_config['ecmp_bestpath'] = True - continue - match = self.MAX_PATHS_REGEX.match(line) - if match: - self._current_config['max_paths'] = int(match.group(3)) - continue - for key, value in iteritems(self.EVPN_COMMANDS_REGEX_MAPPER): - match = value[0].match(line) - if match: - self._current_config[key] = True - break - - def _get_bgp_summary(self): - return get_bgp_summary(self._module) - - def load_current_config(self): - self._current_config = dict() - bgp_config = self._get_bgp_summary() - if bgp_config: - self._set_bgp_config(bgp_config) - - def generate_commands(self): - state = self._required_config['state'] - if state == 'present': - self._generate_bgp_cmds() - else: - self._generate_no_bgp_cmds() - - def _generate_bgp_cmds(self): - vrf = self._required_config.get('vrf') - if vrf is None: - vrf = "default" - - as_number = self._required_config['as_number'] - curr_as_num = self._current_config.get('as_number') - curr_vrf = self._current_config.get("vrf") - bgp_removed = False - if curr_as_num != as_number or vrf != curr_vrf: - if curr_as_num: - self._commands.append('no router bgp %d vrf %s' % (curr_as_num, curr_vrf)) - bgp_removed = True - self._commands.append('router bgp %d vrf %s' % (as_number, vrf)) - self._commands.append('exit') - - req_router_id = self._required_config.get('router_id') - if req_router_id is not None: - curr_route_id = self._current_config.get('router_id') - if bgp_removed or req_router_id != curr_route_id: - self._commands.append('router bgp %d vrf %s router-id %s force' % (as_number, vrf, req_router_id)) - - fast_external_fallover = self._required_config.get('fast_external_fallover') - if fast_external_fallover is not None: - current_fast_external_fallover = self._current_config.get('fast_external_fallover') - if fast_external_fallover and (bgp_removed or fast_external_fallover != current_fast_external_fallover): - self._commands.append('router bgp %d vrf %s bgp fast-external-fallover' % (as_number, vrf)) - elif not fast_external_fallover and (bgp_removed or fast_external_fallover != current_fast_external_fallover): - self._commands.append('router bgp %d vrf %s no bgp fast-external-fallover' % (as_number, vrf)) - - max_paths = self._required_config.get('max_paths') - if max_paths is not None: - current_max_paths = self._current_config.get('max_paths') - if bgp_removed or max_paths != current_max_paths: - self._commands.append('router bgp %d vrf %s maximum-paths %s' % (as_number, vrf, max_paths)) - - ecmp_bestpath = self._required_config.get('ecmp_bestpath') - if ecmp_bestpath is not None: - current_ecmp_bestpath = self._current_config.get('ecmp_bestpath') - if ecmp_bestpath and (bgp_removed or ecmp_bestpath != current_ecmp_bestpath): - self._commands.append('router bgp %d vrf %s bestpath as-path multipath-relax force' % (as_number, vrf)) - elif not ecmp_bestpath and (bgp_removed or ecmp_bestpath != current_ecmp_bestpath): - self._commands.append('router bgp %d vrf %s no bestpath as-path multipath-relax force' % (as_number, vrf)) - - evpn = self._required_config.get('evpn') - if evpn is not None: - self._generate_evpn_cmds(evpn, as_number, vrf) - - self._generate_neighbors_cmds(as_number, vrf, bgp_removed) - self._generate_networks_cmds(as_number, vrf, bgp_removed) - - def _generate_neighbors_cmds(self, as_number, vrf, bgp_removed): - req_neighbors = self._required_config['neighbors'] - curr_neighbors = self._current_config.get('neighbors', {}) - evpn = self._required_config.get('evpn') - if self._purge: - for neighbor in curr_neighbors: - remote_as = curr_neighbors[neighbor].get("remote_as") - self._commands.append('router bgp %s vrf %s no neighbor %s remote-as %s' % ( - as_number, vrf, neighbor, remote_as)) - - if req_neighbors is not None: - for neighbor_data in req_neighbors: - neighbor = neighbor_data.get("neighbor") - curr_neighbor = curr_neighbors.get(neighbor) - remote_as = neighbor_data.get("remote_as") - multihop = neighbor_data.get("multihop") - if bgp_removed or curr_neighbor is None: - if remote_as is not None: - self._commands.append( - 'router bgp %s vrf %s neighbor %s remote-as %s' % (as_number, vrf, neighbor, remote_as)) - if multihop is not None: - self._commands.append( - 'router bgp %s vrf %s neighbor %s ebgp-multihop %s' % (as_number, vrf, neighbor, multihop)) - if evpn: - self._commands.append( - 'router bgp %s vrf %s neighbor %s peer-group evpn' % (as_number, vrf, neighbor)) - elif curr_neighbor is not None: - curr_remote_as = curr_neighbor.get("remote_as") - curr_multihop = curr_neighbor.get("multihop") - curr_neighbor_evpn = curr_neighbor.get("evpn") - if remote_as != curr_remote_as: - self._commands.append( - 'router bgp %s vrf %s neighbor %s remote-as %s' % (as_number, vrf, neighbor, remote_as)) - if multihop is not None and multihop != curr_multihop: - self._commands.append( - 'router bgp %s vrf %s neighbor %s ebgp-multihop %s' % (as_number, vrf, neighbor, multihop)) - if evpn and curr_neighbor_evpn is not True: - self._commands.append( - 'router bgp %s vrf %s neighbor %s peer-group evpn' % (as_number, vrf, neighbor)) - - def _generate_networks_cmds(self, as_number, vrf, bgp_removed): - req_networks = self._required_config['networks'] or [] - curr_networks = self._current_config.get('networks', []) - if not bgp_removed: - for network in curr_networks: - if network not in req_networks: - net_attrs = network.split('/') - if len(net_attrs) != 2: - self._module.fail_json( - msg='Invalid network %s' % network) - - net_address, netmask = net_attrs - cmd = 'router bgp %s no network %s /%s' % ( - as_number, net_address, netmask) - self._commands.append(cmd) - - for network in req_networks: - if bgp_removed or network not in curr_networks: - net_attrs = network.split('/') - if len(net_attrs) != 2: - self._module.fail_json( - msg='Invalid network %s' % network) - net_address, netmask = net_attrs - cmd = 'router bgp %s vrf %s network %s /%s' % ( - as_number, vrf, net_address, netmask) - self._commands.append(cmd) - - def _generate_no_bgp_cmds(self): - as_number = self._required_config['as_number'] - curr_as_num = self._current_config.get('as_number') - if curr_as_num and curr_as_num == as_number: - self._commands.append('no router bgp %d' % as_number) - - def _generate_evpn_cmds(self, evpn, as_number, vrf): - if evpn: - for attr in self.EVPN_ENABLE_ATTRS: - curr_attr = self._current_config.get(attr) - if curr_attr is not True: - self._commands.append(self.EVPN_COMMANDS_REGEX_MAPPER.get(attr)[1] % (as_number, vrf)) - elif not evpn: - for attr in self.EVPN_DISABLE_ATTRS: - curr_attr = self._current_config.get(attr) - if curr_attr is not False: - self._commands.append("no " + self.EVPN_COMMANDS_REGEX_MAPPER.get(attr)[1] % (as_number, vrf)) - - -def main(): - """ main entry point for module execution - """ - OnyxBgpModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_buffer_pool.py b/plugins/modules/network/onyx/onyx_buffer_pool.py deleted file mode 100644 index 52f4d3f788..0000000000 --- a/plugins/modules/network/onyx/onyx_buffer_pool.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_buffer_pool -author: "Anas Badaha (@anasb)" -short_description: Configures Buffer Pool -description: - - This module provides declarative management of Onyx Buffer Pool configuration - on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.8130 -options: - name: - description: - - pool name. - required: true - pool_type: - description: - - pool type. - choices: ['lossless', 'lossy'] - default: lossy - memory_percent: - description: - - memory percent. - switch_priority: - description: - - switch priority, range 1-7. -''' - -EXAMPLES = """ -- name: configure buffer pool - onyx_buffer_pool: - name: roce - pool_type: lossless - memory_percent: 50.00 - switch_priority: 3 - -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - traffic pool roce type lossless - - traffic pool roce memory percent 50.00 - - traffic pool roce map switch-priority 3 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxBufferPoolModule(BaseOnyxModule): - - def init_module(self): - """ initialize module - """ - element_spec = dict( - name=dict(type='str', required=True), - pool_type=dict(choices=['lossless', 'lossy'], default='lossy'), - memory_percent=dict(type='float'), - switch_priority=dict(type='int') - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def validate_switch_priority(self, value): - if value and not 0 <= int(value) <= 7: - self._module.fail_json(msg='switch_priority value must be between 0 and 7') - - def _set_traffic_pool_config(self, traffic_pool_config): - if traffic_pool_config is None: - return - traffic_pool_config = traffic_pool_config.get(self._required_config.get('name')) - self._current_config['pool_type'] = traffic_pool_config[0].get("Type") - self._current_config['switch_priority'] = int(traffic_pool_config[0].get("Switch Priorities")) - self._current_config['memory_percent'] = float(traffic_pool_config[0].get("Memory [%]")) - - def _show_traffic_pool(self): - cmd = "show traffic pool {0}".format(self._required_config.get("name")) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - traffic_pool_config = self._show_traffic_pool() - self._set_traffic_pool_config(traffic_pool_config) - - def generate_commands(self): - name = self._required_config.get("name") - pool_type = self._required_config.get("pool_type") - - if self._current_config is None: - self._add_add_traffic_pool_cmds(name, pool_type) - else: - current_pool_type = self._current_config.get("pool_type") - if pool_type != current_pool_type: - self._add_add_traffic_pool_cmds(name, pool_type) - - memory_percent = self._required_config.get("memory_percent") - if memory_percent is not None: - curr_memory_percent = self._current_config.get("memory_percent") - if curr_memory_percent is None or memory_percent != curr_memory_percent: - self._commands.append('traffic pool {0} memory percent {1}'.format(name, memory_percent)) - - switch_priority = self._required_config.get("switch_priority") - if switch_priority is not None: - curr_switch_priority = self._current_config.get("switch_priority") - if curr_switch_priority is None or switch_priority != curr_switch_priority: - self._commands.append('traffic pool {0} map switch-priority {1}'.format(name, switch_priority)) - - def _add_add_traffic_pool_cmds(self, name, pool_type): - self._commands.append('traffic pool {0} type {1}'.format(name, pool_type)) - - -def main(): - """ main entry point for module execution - """ - OnyxBufferPoolModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_command.py b/plugins/modules/network/onyx/onyx_command.py deleted file mode 100644 index 11aa50c21c..0000000000 --- a/plugins/modules/network/onyx/onyx_command.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_command -extends_documentation_fragment: -- community.general.onyx - -author: "Samer Deeb (@samerd)" -short_description: Run commands on remote devices running Mellanox ONYX -description: - - Sends arbitrary commands to an Mellanox ONYX network 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(onyx_config) to configure Mellanox ONYX devices. -notes: - - Tested on ONYX 3.6.4000 -options: - commands: - description: - - List of commands to send to the remote Mellanox ONYX network device. - 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. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - 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. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -tasks: - - name: run show version on remote devices - onyx_command: - commands: show version - - - name: run show version and check to see if output contains MLNXOS - onyx_command: - commands: show version - wait_for: result[0] contains MLNXOS - - - name: run multiple commands on remote nodes - onyx_command: - commands: - - show version - - show interfaces - - - name: run multiple commands and evaluate the output - onyx_command: - commands: - - show version - - show interfaces - wait_for: - - result[0] contains MLNXOS - - result[1] contains mgmt1 -""" - -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: ['...', '...'] -""" - -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList -from ansible.module_utils.six import string_types - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import run_commands - - -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 item in list(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'] - ) - commands.remove(item) - elif item['command'].startswith('conf'): - module.fail_json( - msg='onyx_command does not support running config mode ' - 'commands. Please use onyx_config instead' - ) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - 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/plugins/modules/network/onyx/onyx_config.py b/plugins/modules/network/onyx/onyx_config.py deleted file mode 100644 index 1ccb6d5beb..0000000000 --- a/plugins/modules/network/onyx/onyx_config.py +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_config -extends_documentation_fragment: -- community.general.onyx - -author: "Alex Tabachnik (@atabachnik), Samer Deeb (@samerd)" -short_description: Manage Mellanox ONYX configuration sections -description: - - Mellanox ONYX configurations uses a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with ONYX configuration sections in - a deterministic way. -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. - aliases: ['commands'] - parents: - description: - - The ordered set of parents that uniquely identify the section - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - 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. - 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. - 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. - 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 - default: line - choices: ['line', 'block'] - 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. - default: no - type: bool - config: - description: - - The C(config) argument allows the playbook designer to supply - the base configuration to be used to validate configuration - changes necessary. If this argument is provided, the module - will not download the running-config from the remote node. - 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. - default: no - type: bool - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ ---- -- onyx_config: - lines: - - snmp-server community - - snmp-server host 10.2.2.2 traps version 2c -""" - -RETURN = """ -updates: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['...', '...'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/onyx_config.2016-07-16@22:28:34 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_config -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import load_config -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import run_commands - - -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() - candidate.add(module.params['lines'], parents=parents) - return candidate - - -def run(module, result): - match = module.params['match'] - replace = module.params['replace'] - path = module.params['parents'] - - candidate = get_candidate(module) - if match != 'none': - contents = module.params['config'] - if not contents: - contents = get_config(module) - config = NetworkConfig(indent=1, contents=contents) - configobjs = candidate.difference(config, path=path, match=match, - replace=replace) - - else: - configobjs = candidate.items - - total_commands = [] - if configobjs: - commands = dumps(configobjs, 'commands').split('\n') - - if module.params['lines']: - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - total_commands.extend(commands) - result['updates'] = total_commands - - if module.params['save']: - total_commands.append('configuration write') - if total_commands: - result['changed'] = True - if not module.check_mode: - load_config(module, total_commands) - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - - config=dict(), - - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - save=dict(type='bool', default=False), - ) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - if module.params['backup']: - result['__backup__'] = get_config(module) - - run(module, result) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_facts.py b/plugins/modules/network/onyx/onyx_facts.py deleted file mode 100644 index e06b4fc4cd..0000000000 --- a/plugins/modules/network/onyx/onyx_facts.py +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_facts -author: "Waleed Mousa (@waleedym), Samer Deeb (@samerd)" -short_description: Collect facts from Mellanox ONYX network devices -description: - - Collects a base set of device facts from a ONYX Mellanox network devices - This module prepends all of the base network fact keys with - C(ansible_net_). The facts module will always collect a base set of - facts from the device and can enable or disable collection of additional - facts. -notes: - - Tested against ONYX 3.6 -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, version, module, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: version -''' - -EXAMPLES = """ ---- -- name: Collect all facts from the device - onyx_facts: - gather_subset: all -- name: Collect only the interfaces facts - onyx_facts: - gather_subset: - - interfaces -- name: Do not collect version facts - onyx_facts: - gather_subset: - - "!version" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list -# version -ansible_net_version: - description: A hash of all currently running system image information - returned: when version is configured or when no gather_subset is provided - type: dict -# modules -ansible_net_modules: - description: A hash of all modules on the systeme with status - returned: when modules is configured - type: dict -# interfaces -ansible_net_interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxFactsModule(BaseOnyxModule): - - def get_runable_subset(self, 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: - self._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) - if not runable_subsets: - runable_subsets.add('version') - return runable_subsets - - def init_module(self): - """ module initialization - """ - argument_spec = dict( - gather_subset=dict(default=['version'], type='list') - ) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def run(self): - self.init_module() - gather_subset = self._module.params['gather_subset'] - runable_subsets = self.get_runable_subset(gather_subset) - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - facter_cls = FACT_SUBSETS[key] - instances.append(facter_cls(self._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 - self._module.exit_json(ansible_facts=ansible_facts) - - -class FactsBase(object): - - COMMANDS = [''] - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def _show_cmd(self, cmd): - return show_cmd(self.module, cmd, json_fmt=True) - - def populate(self): - self.responses = [] - for cmd in self.COMMANDS: - self.responses.append(self._show_cmd(cmd)) - - -class Version(FactsBase): - - COMMANDS = ['show version'] - - def populate(self): - super(Version, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = data - - -class Module(FactsBase): - - COMMANDS = ['show module'] - - def populate(self): - super(Module, self).populate() - data = self.responses[0] - if data: - self.facts['modules'] = data - - -class Interfaces(FactsBase): - - COMMANDS = ['show version', 'show interfaces ethernet'] - - def populate(self): - super(Interfaces, self).populate() - - version_data = self.responses[0] - os_version = version_data['Product release'] - data = self.responses[1] - - if data: - self.facts['interfaces'] = self.populate_interfaces(data, os_version) - - def extractIfData(self, interface_data): - return {"MAC Address": interface_data["Mac address"], - "Actual Speed": interface_data["Actual speed"], - "MTU": interface_data["MTU"], - "Admin State": interface_data["Admin state"], - "Operational State": interface_data["Operational state"]} - - def populate_interfaces(self, interfaces, os_version): - interfaces_dict = dict() - for if_data in interfaces: - if_dict = dict() - if os_version >= BaseOnyxModule.ONYX_API_VERSION: - for if_name, interface_data in iteritems(if_data): - interface_data = interface_data[0] - if_dict = self.extractIfData(interface_data) - if_name = if_dict["Interface Name"] = if_name - - else: - if_dict = self.extractIfData(if_data) - if_name = if_dict["Interface Name"] = if_data["header"] - interfaces_dict[if_name] = if_dict - return interfaces_dict - - -FACT_SUBSETS = dict( - version=Version, - modules=Module, - interfaces=Interfaces -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - """ main entry point for module execution - """ - OnyxFactsModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_igmp.py b/plugins/modules/network/onyx/onyx_igmp.py deleted file mode 100644 index 180dd92f67..0000000000 --- a/plugins/modules/network/onyx/onyx_igmp.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_igmp -author: "Samer Deeb (@samerd)" -short_description: Configures IGMP global parameters -description: - - This module provides declarative management of IGMP protocol params - on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.6107 -options: - state: - description: - - IGMP state. - required: true - choices: ['enabled', 'disabled'] - last_member_query_interval: - description: - - Configure the last member query interval, range 1-25 - mrouter_timeout: - description: - - Configure the mrouter timeout, range 60-600 - port_purge_timeout: - description: - - Configure the host port purge timeout, range 130-1225 - proxy_reporting: - description: - - Configure ip igmp snooping proxy and enable reporting mode - choices: ['enabled', 'disabled'] - report_suppression_interval: - description: - - Configure the report suppression interval, range 1-25 - unregistered_multicast: - description: - - Configure the unregistered multicast mode - Flood unregistered multicast - Forward unregistered multicast to mrouter ports - choices: ['flood', 'forward-to-mrouter-ports'] - default_version: - description: - - Configure the default operating version of the IGMP snooping - choices: ['V2','V3'] -''' - -EXAMPLES = """ -- name: configure igmp - onyx_igmp: - state: enabled - unregistered_multicast: flood -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - ip igmp snooping - - ip igmp snooping last-member-query-interval 10 - - ip igmp snooping mrouter-timeout 150 - - ip igmp snooping port-purge-timeout 150 -""" - -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxIgmpModule(BaseOnyxModule): - TIME_INTERVAL_REGEX = re.compile(r'^(\d+)\s+seconds') - - _RANGE_INTERVALS = dict( - last_member_query_interval=(1, 25, 'Last member query interval'), - mrouter_timeout=(60, 600, 'Mrouter timeout'), - port_purge_timeout=(130, 1225, 'Port purge timeout'), - report_suppression_interval=(1, 25, 'Report suppression interval'), - ) - - def init_module(self): - """ initialize module - """ - element_spec = dict( - state=dict(choices=['enabled', 'disabled'], required=True), - last_member_query_interval=dict(type='int'), - mrouter_timeout=dict(type='int'), - port_purge_timeout=dict(type='int'), - proxy_reporting=dict(choices=['enabled', 'disabled']), - report_suppression_interval=dict(type='int'), - unregistered_multicast=dict( - choices=['flood', 'forward-to-mrouter-ports']), - default_version=dict(choices=['V2', 'V3']), - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def _validate_key(self, param, key): - interval_params = self._RANGE_VALIDATORS.get(key) - if interval_params: - min_val, max_val = interval_params[0], interval_params[1] - value = param.get(key) - self._validate_range(key, min_val, max_val, value) - else: - super(OnyxIgmpModule, self)._validate_key(param, key) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def _set_igmp_config(self, igmp_config): - igmp_config = igmp_config[0] - if not igmp_config: - return - self._current_config['state'] = igmp_config.get( - 'IGMP snooping globally', 'disabled') - self._current_config['proxy_reporting'] = igmp_config.get( - 'Proxy-reporting globally', 'disabled') - self._current_config['default_version'] = igmp_config.get( - 'IGMP default version for new VLAN', 'V3') - self._current_config['unregistered_multicast'] = igmp_config.get( - 'IGMP snooping unregistered multicast', 'flood') - - for interval_name, interval_params in iteritems(self._RANGE_INTERVALS): - display_str = interval_params[2] - value = igmp_config.get(display_str, '') - match = self.TIME_INTERVAL_REGEX.match(value) - if match: - interval_value = int(match.group(1)) - else: - interval_value = None - self._current_config[interval_name] = interval_value - - def _show_igmp(self): - cmd = "show ip igmp snooping" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - igmp_config = self._show_igmp() - if igmp_config: - self._set_igmp_config(igmp_config) - - def generate_commands(self): - state = self._required_config['state'] - if state == 'enabled': - self._generate_igmp_cmds() - else: - self._generate_no_igmp_cmds() - - def _generate_igmp_cmds(self): - curr_state = self._current_config.get('state', 'disabled') - if curr_state == 'disabled': - self._commands.append('ip igmp snooping') - for interval_name in self._RANGE_INTERVALS: - req_val = self._required_config.get(interval_name) - if not req_val: - continue - curr_value = self._current_config.get(interval_name) - if curr_value == req_val: - continue - interval_cmd = interval_name.replace('_', '-') - self._commands.append( - 'ip igmp snooping %s %s' % (interval_cmd, req_val)) - - req_val = self._required_config.get('unregistered_multicast') - if req_val: - curr_value = self._current_config.get( - 'unregistered_multicast', 'flood') - if req_val != curr_value: - self._commands.append( - 'ip igmp snooping unregistered multicast %s' % req_val) - - req_val = self._required_config.get('proxy_reporting') - if req_val: - curr_value = self._current_config.get( - 'proxy_reporting', 'disabled') - if req_val != curr_value: - cmd = 'ip igmp snooping proxy reporting' - if req_val == 'disabled': - cmd = 'no %s' % cmd - self._commands.append(cmd) - - req_val = self._required_config.get('default_version') - if req_val: - curr_value = self._current_config.get( - 'default_version', 'V3') - if req_val != curr_value: - version = req_val[1] # remove the 'V' and take the number only - self._commands.append( - 'ip igmp snooping version %s' % version) - - def _generate_no_igmp_cmds(self): - curr_state = self._current_config.get('state', 'disabled') - if curr_state != 'disabled': - self._commands.append('no ip igmp snooping') - - -def main(): - """ main entry point for module execution - """ - OnyxIgmpModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_igmp_interface.py b/plugins/modules/network/onyx/onyx_igmp_interface.py deleted file mode 100644 index beafd702ea..0000000000 --- a/plugins/modules/network/onyx/onyx_igmp_interface.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_igmp_interface -author: "Anas Badaha (@anasb)" -short_description: Configures IGMP interface parameters -description: - - This module provides declarative management of IGMP interface configuration - on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.8130 -options: - name: - description: - - interface name that we want to configure IGMP on it - required: true - state: - description: - - IGMP Interface state. - choices: ['enabled', 'disabled'] - default: enabled -''' - -EXAMPLES = """ -- name: configure igmp interface - onyx_igmp_interface: - state: enabled - name: Eth1/1 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface ethernet 1/1 ip igmp snooping fast-leave -""" - -import re -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxIgmpInterfaceModule(BaseOnyxModule): - IF_NAME_REGEX = re.compile(r"^(Eth\d+\/\d+|Eth\d+\/\d+\d+)$") - - def init_module(self): - """ initialize module - """ - element_spec = dict( - state=dict(choices=['enabled', 'disabled'], default='enabled'), - name=dict(required=True) - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - match = self.IF_NAME_REGEX.match(self._required_config["name"]) - if not match: - raise AttributeError("Please Insert Valid Interface Name") - - self.validate_param_values(self._required_config) - - def _set_igmp_config(self, igmp_interfaces_config): - if not igmp_interfaces_config: - return - name = self._required_config.get('name') - interface_state = igmp_interfaces_config[name][0].get('leave-mode') - if interface_state == "Fast": - self._current_config['state'] = "enabled" - else: - self._current_config['state'] = "disabled" - - def _show_igmp_interfaces(self): - cmd = "show ip igmp snooping interfaces" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - igmp_interfaces_config = self._show_igmp_interfaces() - if igmp_interfaces_config: - self._set_igmp_config(igmp_interfaces_config) - - def generate_commands(self): - req_state = self._required_config['state'] - self._req_val = self._required_config.get('name').replace("Eth", "ethernet ") - - if req_state == 'enabled': - self._generate_igmp_interface_cmds() - else: - self._generate_no_igmp_cmds() - - def _generate_igmp_interface_cmds(self): - curr_state = self._current_config.get('state', 'enabled') - if curr_state == 'enabled': - pass - - elif curr_state == 'disabled': - self._commands.append('interface %s ip igmp snooping fast-leave' % self._req_val) - - def _generate_no_igmp_cmds(self): - curr_state = self._current_config.get('state', 'enabled') - if curr_state == 'enabled': - self._commands.append('interface %s no ip igmp snooping fast-leave' % self._req_val) - else: - pass - - -def main(): - """ main entry point for module execution - """ - OnyxIgmpInterfaceModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_igmp_vlan.py b/plugins/modules/network/onyx/onyx_igmp_vlan.py deleted file mode 100644 index 55b9cc4b89..0000000000 --- a/plugins/modules/network/onyx/onyx_igmp_vlan.py +++ /dev/null @@ -1,435 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_igmp_vlan -author: Anas Badaha (@anasbadaha) -short_description: Configures IGMP Vlan parameters -description: - - This module provides declarative management of IGMP vlan configuration on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.7.0932-01 -options: - vlan_id: - description: - - VLAN ID, vlan should exist. - required: true - state: - description: - - IGMP state. - choices: ['enabled', 'disabled'] - default: enabled - mrouter: - description: - - Configure ip igmp snooping mrouter port on vlan - suboptions: - state: - description: - - Enable IGMP snooping mrouter on vlan interface. - choices: ['enabled', 'disabled'] - default: enabled - name: - description: - - Configure mrouter interface - required: true - querier: - description: - - Configure the IGMP querier parameters - suboptions: - state: - description: - - Enable IGMP snooping querier on vlan in the switch. - choices: ['enabled', 'disabled'] - default: enabled - interval: - description: - - Update time interval between querier queries, range 60-600 - address: - description: - - Update IP address for the querier - static_groups: - description: - - List of IGMP static groups. - suboptions: - multicast_ip_address: - description: - - Configure static IP multicast group, range 224.0.1.0-239.255.255.25. - required: true - name: - description: - - interface name to configure static groups on it. - sources: - description: - - List of IP sources to be configured - version: - description: - - IGMP snooping operation version on this vlan - choices: ['V2','V3'] -''' - -EXAMPLES = """ -- name: configure igmp vlan - onyx_igmp_vlan: - state: enabled - vlan_id: 10 - version: - V2 - querier: - state: enabled - interval: 70 - address: 10.11.121.13 - mrouter: - state: disabled - name: Eth1/2 - static_groups: - - multicast_ip_address: 224.5.5.8 - name: Eth1/1 - sources: - - 1.1.1.1 - - 1.1.1.2 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - vlan 10 ip igmp snooping - - vlan 10 ip igmp snooping static-group 224.5.5.5 interface ethernet 1/1 -""" -import socket -import struct - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -def _ip_to_int(addr): - return struct.unpack("!I", socket.inet_aton(addr))[0] - - -class OnyxIgmpVlanModule(BaseOnyxModule): - MIN_MULTICAST_IP = _ip_to_int("224.0.1.0") - MAX_MULTICAST_IP = _ip_to_int("239.255.255.255") - - def init_module(self): - """ initialize module - """ - mrouter_spec = dict(name=dict(required=True), - state=dict(choices=['enabled', 'disabled'], default='enabled')) - querier_spec = dict(state=dict(choices=['enabled', 'disabled'], default='enabled'), - interval=dict(type='int'), address=dict()) - static_groups_spec = dict(multicast_ip_address=dict(required=True), - name=dict(required=True), sources=dict(type='list')) - element_spec = dict(vlan_id=dict(type='int', required=True), - state=dict(choices=['enabled', 'disabled'], default='enabled'), - querier=dict(type='dict', options=querier_spec), - static_groups=dict(type='list', elements='dict', options=static_groups_spec), - mrouter=dict(type='dict', options=mrouter_spec), - version=dict(choices=['V2', 'V3'])) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def _validate_attr_is_not_none(self, attr_name, attr_value): - if attr_name == 'vlan_id' or attr_name == 'state': - pass - elif attr_value is not None: - self._module.fail_json(msg='Can not set %s value on switch while state is disabled' % attr_name) - - def validate_param_values(self, obj, param=None): - if obj['state'] == 'disabled': - for attr_name in obj: - self._validate_attr_is_not_none(attr_name, obj[attr_name]) - super(OnyxIgmpVlanModule, self).validate_param_values(obj, param) - - def validate_querier(self, value): - interval = value.get('interval') - if interval and not 60 <= int(interval) <= 600: - self._module.fail_json(msg='query-interval value must be between 60 and 600') - - def validate_static_groups(self, value): - multicast_ip = value.get('multicast_ip_address') - multicast_ip = _ip_to_int(multicast_ip) - - if multicast_ip < self.MIN_MULTICAST_IP or multicast_ip > self.MAX_MULTICAST_IP: - self._module.fail_json(msg='multicast IP address must be in range 224.0.1.0 - 239.255.255.255') - - @staticmethod - def _get_curr_mrouter_config(mrouter_port): - if mrouter_port == "none": - return {'state': 'disabled'} - else: - return {'state': 'enabled', - 'name': mrouter_port} - - def _get_curr_querier_config(self, querier_config): - if "Non-Querier" in querier_config: - return {'state': 'disabled'} - elif "Querier" in querier_config: - igmp_querier_config = self._show_igmp_querier_config()[0] - snooping_querier_info = igmp_querier_config["Snooping querier information for VLAN %d" % ( - self._required_config['vlan_id'])] - snooping_querier_info = snooping_querier_info[1] - interval = int(snooping_querier_info["Query interval"]) - address = snooping_querier_info["Configured querier IP address"] - return {'state': 'enabled', - 'interval': interval, - 'address': address} - - @staticmethod - def _get_curr_version(version): - if "V3" in version: - return "V3" - elif "V2" in version: - return "V2" - - def _get_curr_static_group_config(self, multicast_ip_address): - sources = None - names = None - igmp_snooping_groups_config = self._show_igmp_snooping_groups_config(multicast_ip_address) - if igmp_snooping_groups_config is not None: - igmp_snooping_groups_config = igmp_snooping_groups_config[0] - snooping_group_information = igmp_snooping_groups_config.get('Snooping group ' - 'information for VLAN %d and group ' - '%s' % (self._required_config['vlan_id'], - multicast_ip_address)) - if snooping_group_information is not None: - if len(snooping_group_information) == 1: - names = snooping_group_information[0].get('V1/V2 Receiver Ports') - elif len(snooping_group_information) == 2: - sources_dict = dict() - v3_receiver_ports = snooping_group_information[1].get('V3 Receiver Ports') - ports_number = v3_receiver_ports[0].get('Port Number') - sources = v3_receiver_ports[0].get('Include sources') - if isinstance(ports_number, list): - i = 0 - for port_number in ports_number: - sources_dict[port_number] = sources[i] - i += 1 - else: - sources_dict[ports_number] = sources - names = snooping_group_information[0].get('V1/V2 Receiver Ports') - sources = sources_dict - - return {'sources': sources, - 'names': names} - else: - return None - else: - return None - - def _set_igmp_config(self, igmp_vlan_config): - igmp_vlan_config = igmp_vlan_config[0] - if not igmp_vlan_config: - return - - self._current_config['state'] = igmp_vlan_config.get('message 1') - if "enabled" in self._current_config['state']: - self._current_config['state'] = "enabled" - elif "disabled" in self._current_config['state']: - self._current_config['state'] = "disabled" - - mrouter_port = igmp_vlan_config.get('mrouter static port list') - self._current_config['mrouter'] = dict(self._get_curr_mrouter_config(mrouter_port)) - - querier_config = igmp_vlan_config.get('message 3') - self._current_config['querier'] = dict(self._get_curr_querier_config(querier_config)) - - version = igmp_vlan_config.get('message 2') - self._current_config['version'] = self._get_curr_version(version) - - req_static_groups = self._required_config.get('static_groups') - if req_static_groups is not None: - static_groups = self._current_config['static_groups'] = dict() - for static_group in req_static_groups: - static_group_config = self._get_curr_static_group_config(static_group['multicast_ip_address']) - static_groups[static_group['multicast_ip_address']] = static_group_config - - def _show_igmp_vlan(self): - cmd = ("show ip igmp snooping vlan %d" % self._required_config['vlan_id']) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def _show_igmp_querier_config(self): - cmd = ("show ip igmp snooping querier vlan %d " % self._required_config['vlan_id']) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def _show_igmp_snooping_groups_config(self, multicast_ip_address): - cmd = ("show ip igmp snooping groups vlan %d group %s" % (self._required_config['vlan_id'], - multicast_ip_address)) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - igmp_vlan_config = self._show_igmp_vlan() - if igmp_vlan_config: - self._set_igmp_config(igmp_vlan_config) - - def generate_commands(self): - req_state = self._required_config.get('state', 'enabled') - self._generate_igmp_vlan_cmds(req_state) - - _mrouter = self._required_config.get('mrouter') - if _mrouter is not None: - self._generate_igmp_mrouter_cmds(_mrouter) - - _querier = self._required_config.get('querier') - if _querier is not None: - req_querier_state = _querier.get('state', 'enabled') - self._generate_igmp_querier_cmds(req_querier_state) - - req_querier_interval = _querier.get('interval') - if req_querier_interval is not None: - self._gen_querier_attr_commands("interval", req_querier_interval, "query-interval") - - req_querier_address = _querier.get('address') - if req_querier_address is not None: - self._gen_querier_attr_commands("address", req_querier_address, "address") - - _version = self._required_config.get('version') - if _version is not None: - self._generate_igmp_version_cmds(_version) - - _static_groups = self._required_config.get('static_groups') - if _static_groups is not None: - for static_group in _static_groups: - self._generate_igmp_static_groups_cmd(static_group) - - def _add_igmp_vlan_commands(self, req_state): - if req_state == 'enabled': - igmp_vlan_cmd = 'vlan %d ip igmp snooping' % self._required_config['vlan_id'] - else: - igmp_vlan_cmd = 'vlan %d no ip igmp snooping' % self._required_config['vlan_id'] - - self._commands.append(igmp_vlan_cmd) - - def _generate_igmp_vlan_cmds(self, req_state): - curr_state = self._current_config.get('state') - if curr_state != req_state: - self._add_igmp_vlan_commands(req_state) - - def _gen_querier_attr_commands(self, attr_name, req_attr_value, attr_cmd_name): - _curr_querier = self._current_config.get('querier') - curr_querier_val = _curr_querier.get(attr_name) - if req_attr_value != curr_querier_val: - self._commands.append('vlan %d ip igmp snooping querier %s %s' % (self._required_config['vlan_id'], - attr_cmd_name, req_attr_value)) - - def _add_querier_commands(self, req_querier_state): - if req_querier_state == 'enabled': - self._commands.append('vlan %d ip igmp snooping querier' % self._required_config['vlan_id']) - elif req_querier_state == 'disabled': - self._commands.append('vlan %d no ip igmp snooping querier' % ( - self._required_config['vlan_id'])) - - def _generate_igmp_querier_cmds(self, req_querier_state): - _curr_querier = self._current_config.get('querier') - curr_querier_state = _curr_querier.get('state') - if req_querier_state != curr_querier_state: - self._add_querier_commands(req_querier_state) - - def _generate_igmp_version_cmds(self, version): - _curr_version = self._current_config.get('version') - if version != _curr_version: - self._commands.append('vlan %d ip igmp snooping version %s' % ( - self._required_config['vlan_id'], version[1])) - - def _add_mrouter_commands(self, req_mrouter, curr_mrouter): - curr_state = curr_mrouter.get('state') - curr_interface = curr_mrouter.get('name') - req_state = req_mrouter.get('state') - req_interface = req_mrouter.get('name') - mrouter_interface = req_interface.replace("Eth", "ethernet ") - if curr_state == 'enabled' and req_state == 'disabled': - self._commands.append('vlan %d no ip igmp snooping mrouter interface ' - '%s' % (self._required_config['vlan_id'], mrouter_interface)) - elif curr_state == 'disabled' and req_state == 'enabled': - self._commands.append('vlan %d ip igmp snooping mrouter interface ' - '%s' % (self._required_config['vlan_id'], mrouter_interface)) - elif req_state == 'enabled' and curr_state == 'enabled' and req_interface != curr_interface: - self._commands.append('vlan %d ip igmp snooping mrouter interface ' - '%s' % (self._required_config['vlan_id'], mrouter_interface)) - - def _generate_igmp_mrouter_cmds(self, req_mrouter): - curr_mrouter = self._current_config.get('mrouter') - if curr_mrouter != req_mrouter: - self._add_mrouter_commands(req_mrouter, curr_mrouter) - - def _add_igmp_static_groups_cmd(self, req_name, req_multicast_ip_address, curr_names): - if curr_names is None: - self._commands.append('vlan %d ip igmp snooping static-group %s interface %s' % ( - self._required_config['vlan_id'], req_multicast_ip_address, req_name.replace('Eth', 'ethernet '))) - elif req_name.replace('E', 'e') not in curr_names: - self._commands.append('vlan %d ip igmp snooping static-group %s interface %s' % ( - self._required_config['vlan_id'], req_multicast_ip_address, req_name.replace('Eth', 'ethernet '))) - - def _add_igmp_static_groups_sources_cmd(self, req_sources, req_name, req_multicast_ip_address, curr_sources): - if curr_sources is None: - for source in req_sources: - self._commands.append('vlan %d ip igmp snooping static-group %s interface %s source %s' % ( - self._required_config['vlan_id'], req_multicast_ip_address, req_name.replace('Eth', 'ethernet '), - source)) - else: - curr_sources = curr_sources.get(req_name.replace('E', 'e')) - if curr_sources is None: - curr_sources = set([]) - else: - curr_sources = set(x.strip() for x in curr_sources.split(',')) - sources_to_add = set(req_sources) - set(curr_sources) - sources_to_remove = set(curr_sources) - set(req_sources) - if len(sources_to_add) != 0: - for source in sources_to_add: - self._commands.append('vlan %d ip igmp snooping static-group %s interface %s source %s' % ( - self._required_config['vlan_id'], req_multicast_ip_address, - req_name.replace('Eth', 'ethernet '), source)) - if len(sources_to_remove) != 0: - for source in sources_to_remove: - self._commands.append('vlan %d no ip igmp snooping static-group %s interface %s source %s' % ( - self._required_config['vlan_id'], req_multicast_ip_address, - req_name.replace('Eth', 'ethernet '), - source)) - - def _generate_igmp_static_groups_cmd(self, static_group): - req_multicast_ip_address = static_group.get('multicast_ip_address') - req_name = static_group.get('name') - req_sources = static_group.get('sources') - curr_static_groups = self._current_config.get('static_groups') - curr_static_group = curr_static_groups.get(req_multicast_ip_address) - curr_names = None - curr_sources = None - if curr_static_group is not None: - curr_names = curr_static_group.get('names') - curr_sources = curr_static_group.get('sources') - - self._add_igmp_static_groups_cmd(req_name, req_multicast_ip_address, curr_names) - if req_sources is not None: - self._add_igmp_static_groups_sources_cmd(req_sources, req_name, req_multicast_ip_address, curr_sources) - - -def main(): - """ main entry point for module execution - """ - OnyxIgmpVlanModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_interface.py b/plugins/modules/network/onyx/onyx_interface.py deleted file mode 100644 index e0ef8f3d9a..0000000000 --- a/plugins/modules/network/onyx/onyx_interface.py +++ /dev/null @@ -1,501 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_interface -author: "Samer Deeb (@samerd)" -short_description: Manage Interfaces on Mellanox ONYX network devices -description: - - This module provides declarative management of Interfaces - on Mellanox ONYX network devices. -notes: -options: - name: - description: - - Name of the Interface. - required: true - description: - description: - - Description of Interface. - enabled: - description: - - Interface link status. - type: bool - speed: - description: - - Interface link speed. - choices: ['1G', '10G', '25G', '40G', '50G', '56G', '100G'] - mtu: - description: - - Maximum size of transmit packet. - aggregate: - description: List of Interfaces definitions. - duplex: - description: - - Interface link status - default: auto - choices: ['full', 'half', 'auto'] - tx_rate: - description: - - Transmit rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - rx_rate: - description: - - Receiver rate in bits per second (bps). - - This is state check parameter only. - - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html) - delay: - description: - - Time in seconds to wait before checking for the operational state on - remote device. This wait is applicable for operational state argument - which are I(state) with values C(up)/C(down). - default: 10 - purge: - description: - - Purge Interfaces not defined in the aggregate parameter. - This applies only for logical interface. - default: false - type: bool - state: - description: - - State of the Interface configuration, C(up) means present and - operationally up and C(down) means present and operationally C(down) - default: present - choices: ['present', 'absent', 'up', 'down'] -''' - -EXAMPLES = """ -- name: configure interface - onyx_interface: - name: Eth1/2 - description: test-interface - speed: 100G - mtu: 512 - -- name: make interface up - onyx_interface: - name: Eth1/2 - enabled: True - -- name: make interface down - onyx_interface: - name: Eth1/2 - enabled: False - -- name: Check intent arguments - onyx_interface: - name: Eth1/2 - state: up - -- name: Config + intent - onyx_interface: - name: Eth1/2 - enabled: False - state: down -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface ethernet 1/2 - - description test-interface - - mtu 512 - - exit -""" - -from copy import deepcopy -import re -from time import sleep - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import conditional -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_interfaces_config - - -class OnyxInterfaceModule(BaseOnyxModule): - IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|\d+\/\d+\/\d+)$") - IF_VLAN_REGEX = re.compile(r"^Vlan (\d+)$") - IF_LOOPBACK_REGEX = re.compile(r"^Loopback (\d+)$") - IF_PO_REGEX = re.compile(r"^Po(\d+)$") - - IF_TYPE_ETH = "ethernet" - IF_TYPE_LOOPBACK = "loopback" - IF_TYPE_VLAN = "vlan" - IF_TYPE_PO = "port-channel" - - IF_TYPE_MAP = { - IF_TYPE_ETH: IF_ETH_REGEX, - IF_TYPE_VLAN: IF_VLAN_REGEX, - IF_TYPE_LOOPBACK: IF_LOOPBACK_REGEX, - IF_TYPE_PO: IF_PO_REGEX - } - UNSUPPORTED_ATTRS = { - IF_TYPE_ETH: (), - IF_TYPE_VLAN: ('speed', 'rx_rate', 'tx_rate'), - IF_TYPE_LOOPBACK: ('speed', 'mtu', 'rx_rate', 'tx_rate'), - IF_TYPE_PO: ('speed', 'rx_rate', 'tx_rate'), - } - UNSUPPORTED_STATES = { - IF_TYPE_ETH: ('absent',), - IF_TYPE_VLAN: (), - IF_TYPE_LOOPBACK: ('up', 'down'), - IF_TYPE_PO: ('absent'), - } - - IF_MODIFIABLE_ATTRS = ('speed', 'description', 'mtu') - _interface_type = None - - @classmethod - def _get_element_spec(cls): - return dict( - name=dict(type='str'), - description=dict(), - speed=dict(choices=['1G', '10G', '25G', '40G', '50G', '56G', '100G']), - mtu=dict(type='int'), - enabled=dict(type='bool'), - delay=dict(default=10, type='int'), - state=dict(default='present', - choices=['present', 'absent', 'up', 'down']), - tx_rate=dict(), - rx_rate=dict(), - ) - - @classmethod - def _get_aggregate_spec(cls, element_spec): - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - return aggregate_spec - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - aggregate_spec = self._get_aggregate_spec(element_spec) - argument_spec = dict( - aggregate=dict(type='list', elements='dict', - options=aggregate_spec), - purge=dict(default=False, type='bool'), - ) - argument_spec.update(element_spec) - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - self._module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def validate_purge(self, value): - if value: - self._module.fail_json( - msg='Purge is not supported!') - - def validate_duplex(self, value): - if value != 'auto': - self._module.fail_json( - msg='Duplex is not supported!') - - def _get_interface_type(self, if_name): - if_type = None - if_id = None - for interface_type, interface_regex in iteritems(self.IF_TYPE_MAP): - match = interface_regex.match(if_name) - if match: - if_type = interface_type - if_id = match.group(1) - break - return if_type, if_id - - def _set_if_type(self, params): - if_name = params['name'] - if_type, if_id = self._get_interface_type(if_name) - if not if_id: - self._module.fail_json( - msg='unsupported interface: %s' % if_name) - params['if_type'] = if_type - params['if_id'] = if_id - - def _check_supported_attrs(self, if_obj): - unsupported_attrs = self.UNSUPPORTED_ATTRS[self._interface_type] - for attr in unsupported_attrs: - val = if_obj[attr] - if val is not None: - self._module.fail_json( - msg='attribute %s is not supported for %s interface' % ( - attr, self._interface_type)) - req_state = if_obj['state'] - unsupported_states = self.UNSUPPORTED_STATES[self._interface_type] - if req_state in unsupported_states: - self._module.fail_json( - msg='%s state is not supported for %s interface' % ( - req_state, self._interface_type)) - - def _validate_interface_type(self): - for if_obj in self._required_config: - if_type = if_obj['if_type'] - if not self._interface_type: - self._interface_type = if_type - elif self._interface_type != if_type: - self._module.fail_json( - msg='Cannot aggregate interfaces from different types') - self._check_supported_attrs(if_obj) - - def get_required_config(self): - self._required_config = list() - module_params = self._module.params - aggregate = module_params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module_params[key] - - self.validate_param_values(item, item) - req_item = item.copy() - self._set_if_type(req_item) - self._required_config.append(req_item) - else: - params = { - 'name': module_params['name'], - 'description': module_params['description'], - 'speed': module_params['speed'], - 'mtu': module_params['mtu'], - 'state': module_params['state'], - 'delay': module_params['delay'], - 'enabled': module_params['enabled'], - 'tx_rate': module_params['tx_rate'], - 'rx_rate': module_params['rx_rate'], - } - - self.validate_param_values(params) - self._set_if_type(params) - self._required_config.append(params) - self._validate_interface_type() - - @classmethod - def get_if_name(cls, item): - return cls.get_config_attr(item, "header") - - @classmethod - def get_admin_state(cls, item): - admin_state = cls.get_config_attr(item, "Admin state") - return str(admin_state).lower() == "enabled" - - @classmethod - def get_oper_state(cls, item): - oper_state = cls.get_config_attr(item, "Operational state") - if not oper_state: - oper_state = cls.get_config_attr(item, "State") - return str(oper_state).lower() - - @classmethod - def get_speed(cls, item): - speed = cls.get_config_attr(item, 'Actual speed') - if not speed: - return - try: - speed = int(speed.split()[0]) - return "%dG" % speed - except ValueError: - return None - - def _create_if_data(self, name, item): - regex = self.IF_TYPE_MAP[self._interface_type] - if_id = '' - match = regex.match(name) - if match: - if_id = match.group(1) - return dict( - name=name, - description=self.get_config_attr(item, 'Description'), - speed=self.get_speed(item), - mtu=self.get_mtu(item), - enabled=self.get_admin_state(item), - state=self.get_oper_state(item), - if_id=if_id) - - def _get_interfaces_config(self): - return get_interfaces_config(self._module, self._interface_type) - - def load_current_config(self): - self._os_version = self._get_os_version() - self._current_config = dict() - config = self._get_interfaces_config() - if not config: - return - if self._os_version < self.ONYX_API_VERSION: - for if_data in config: - if_name = self.get_if_name(if_data) - self._current_config[if_name] = self._create_if_data( - if_name, if_data) - else: - if_data = dict() - for if_config in config: - for if_name, if_attr in iteritems(if_config): - for config in if_attr: - for key, value in iteritems(config): - if_data[key] = value - self._current_config[if_name] = self._create_if_data( - if_name, if_data) - - def _generate_no_if_commands(self, req_if, curr_if): - if self._interface_type == self.IF_TYPE_ETH: - name = req_if['name'] - self._module.fail_json( - msg='cannot remove ethernet interface %s' % name) - if not curr_if: - return - if_id = req_if['if_id'] - if not if_id: - return - self._commands.append( - 'no interface %s %s' % (self._interface_type, if_id)) - - def _add_commands_to_interface(self, req_if, cmd_list): - if not cmd_list: - return - if_id = req_if['if_id'] - if not if_id: - return - self._commands.append( - 'interface %s %s' % (self._interface_type, if_id)) - self._commands.extend(cmd_list) - self._commands.append('exit') - - def _generate_if_commands(self, req_if, curr_if): - enabled = req_if['enabled'] - cmd_list = [] - for attr_name in self.IF_MODIFIABLE_ATTRS: - candidate = req_if.get(attr_name) - running = curr_if.get(attr_name) - if candidate != running: - if candidate: - cmd = attr_name + ' ' + str(candidate) - if self._interface_type == self.IF_TYPE_ETH and \ - attr_name in ('mtu', 'speed'): - cmd = cmd + ' ' + 'force' - cmd_list.append(cmd) - curr_enabled = curr_if.get('enabled', False) - if enabled is not None and enabled != curr_enabled: - cmd = 'shutdown' - if enabled: - cmd = "no %s" % cmd - cmd_list.append(cmd) - if cmd_list: - self._add_commands_to_interface(req_if, cmd_list) - - def generate_commands(self): - for req_if in self._required_config: - name = req_if['name'] - curr_if = self._current_config.get(name, {}) - if not curr_if and self._interface_type == self.IF_TYPE_ETH: - self._module.fail_json( - msg='could not find ethernet interface %s' % name) - continue - req_state = req_if['state'] - if req_state == 'absent': - self._generate_no_if_commands(req_if, curr_if) - else: - self._generate_if_commands(req_if, curr_if) - - def _get_interfaces_rates(self): - return get_interfaces_config(self._module, self._interface_type, - "rates") - - def _get_interfaces_status(self): - return get_interfaces_config(self._module, self._interface_type, - "status") - - def _check_state(self, name, want_state, statuses): - curr_if = statuses.get(name, {}) - if curr_if: - curr_if = curr_if[0] - curr_state = self.get_oper_state(curr_if).strip() - if curr_state is None or not conditional(want_state, curr_state): - return 'state eq(%s)' % want_state - - def check_declarative_intent_params(self, result): - failed_conditions = [] - delay_called = False - rates = None - statuses = None - for req_if in self._required_config: - want_state = req_if.get('state') - want_tx_rate = req_if.get('tx_rate') - want_rx_rate = req_if.get('rx_rate') - name = req_if['name'] - if want_state not in ('up', 'down') and not want_tx_rate and not \ - want_rx_rate: - continue - if not delay_called and result['changed']: - delay_called = True - delay = req_if['delay'] - if delay > 0: - sleep(delay) - if want_state in ('up', 'down'): - if statuses is None: - statuses = self._get_interfaces_status() or {} - cond = self._check_state(name, want_state, statuses) - if cond: - failed_conditions.append(cond) - if_rates = None - if want_tx_rate or want_rx_rate: - if not rates: - rates = self._get_interfaces_rates() - if_rates = rates.get(name) - if if_rates: - if_rates = if_rates[0] - if want_tx_rate: - have_tx_rate = None - if if_rates: - have_tx_rate = if_rates.get('egress rate') - if have_tx_rate: - have_tx_rate = have_tx_rate.split()[0] - if have_tx_rate is None or not \ - conditional(want_tx_rate, have_tx_rate.strip(), - cast=int): - failed_conditions.append('tx_rate ' + want_tx_rate) - - if want_rx_rate: - have_rx_rate = None - if if_rates: - have_rx_rate = if_rates.get('ingress rate') - if have_rx_rate: - have_rx_rate = have_rx_rate.split()[0] - if have_rx_rate is None or not \ - conditional(want_rx_rate, have_rx_rate.strip(), - cast=int): - failed_conditions.append('rx_rate ' + want_rx_rate) - - return failed_conditions - - -def main(): - """ main entry point for module execution - """ - OnyxInterfaceModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_l2_interface.py b/plugins/modules/network/onyx/onyx_l2_interface.py deleted file mode 100644 index b2102de220..0000000000 --- a/plugins/modules/network/onyx/onyx_l2_interface.py +++ /dev/null @@ -1,298 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_l2_interface -author: "Samer Deeb (@samerd)" -short_description: Manage Layer-2 interface on Mellanox ONYX network devices -description: - - This module provides declarative management of Layer-2 interface - on Mellanox ONYX network devices. -options: - name: - description: - - Name of the interface. - aggregate: - description: - - List of Layer-2 interface definitions. - mode: - description: - - Mode in which interface needs to be configured. - default: access - choices: ['access', 'trunk', 'hybrid'] - access_vlan: - description: - - Configure given VLAN in access port. - trunk_allowed_vlans: - description: - - List of allowed VLANs in a given trunk port. - state: - description: - - State of the Layer-2 Interface configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: configure Layer-2 interface - onyx_l2_interface: - name: Eth1/1 - mode: access - access_vlan: 30 -- name: remove Layer-2 interface configuration - onyx_l2_interface: - name: Eth1/1 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - interface ethernet 1/1 - - switchport mode access - - switchport access vlan 30 -""" -from copy import deepcopy -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_interfaces_config - - -class OnyxL2InterfaceModule(BaseOnyxModule): - IFNAME_REGEX = re.compile(r"^.*(Eth\d+\/\d+|Mpo\d+|Po\d+)") - - @classmethod - def _get_element_spec(cls): - return dict( - name=dict(), - access_vlan=dict(type='int'), - trunk_allowed_vlans=dict(type='list', elements='int'), - state=dict(default='present', - choices=['present', 'absent']), - mode=dict(default='access', - choices=['access', 'hybrid', 'trunk']), - ) - - @classmethod - def _get_aggregate_spec(cls, element_spec): - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - return aggregate_spec - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - aggregate_spec = self._get_aggregate_spec(element_spec) - argument_spec = dict( - aggregate=dict(type='list', elements='dict', - options=aggregate_spec), - ) - argument_spec.update(element_spec) - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - self._module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def get_required_config(self): - self._required_config = list() - module_params = self._module.params - aggregate = module_params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module_params[key] - self.validate_param_values(item, item) - req_item = item.copy() - self._required_config.append(req_item) - else: - params = { - 'name': module_params['name'], - 'access_vlan': module_params['access_vlan'], - 'trunk_allowed_vlans': module_params['trunk_allowed_vlans'], - 'mode': module_params['mode'], - 'state': module_params['state'], - } - self.validate_param_values(params) - self._required_config.append(params) - - def validate_access_vlan(self, value): - if value and not 1 <= int(value) <= 4094: - self._module.fail_json(msg='vlan id must be between 1 and 4094') - - @classmethod - def get_allowed_vlans(cls, if_data): - allowed_vlans = cls.get_config_attr(if_data, 'Allowed vlans') - interface_allwoed_vlans = [] - if allowed_vlans: - vlans = [x.strip() for x in allowed_vlans.split(',')] - for vlan in vlans: - if '-' not in vlan: - interface_allwoed_vlans.append(int(vlan)) - else: - vlan_range = vlan.split("-") - min_number = int(vlan_range[0].strip()) - max_number = int(vlan_range[1].strip()) - vlan_list = range(min_number, max_number + 1) - interface_allwoed_vlans.extend(vlan_list) - return interface_allwoed_vlans - - @classmethod - def get_access_vlan(cls, if_data): - access_vlan = cls.get_config_attr(if_data, 'Access vlan') - if access_vlan: - try: - return int(access_vlan) - except ValueError: - return None - - def _create_switchport_data(self, if_name, if_data): - if self._os_version >= self.ONYX_API_VERSION: - if_data = if_data[0] - - return { - 'name': if_name, - 'mode': self.get_config_attr(if_data, 'Mode'), - 'access_vlan': self.get_access_vlan(if_data), - 'trunk_allowed_vlans': self.get_allowed_vlans(if_data) - } - - def _get_switchport_config(self): - return get_interfaces_config(self._module, 'switchport') - - def load_current_config(self): - # called in base class in run function - self._os_version = self._get_os_version() - self._current_config = dict() - switchports_config = self._get_switchport_config() - if not switchports_config: - return - for if_name, if_data in iteritems(switchports_config): - self._current_config[if_name] = \ - self._create_switchport_data(if_name, if_data) - - def _get_switchport_command_name(self, if_name): - if if_name.startswith('Eth'): - return if_name.replace("Eth", "ethernet ") - if if_name.startswith('Po'): - return if_name.replace("Po", "port-channel ") - if if_name.startswith('Mpo'): - return if_name.replace("Mpo", "mlag-port-channel ") - self._module.fail_json( - msg='invalid interface name: %s' % if_name) - - def _add_interface_commands(self, if_name, commands): - if_cmd_name = self._get_switchport_command_name(if_name) - self._commands.append("interface %s" % if_cmd_name) - self._commands.extend(commands) - self._commands.append('exit') - - def _generate_no_switchport_commands(self, if_name): - commands = ['no switchport force'] - self._add_interface_commands(if_name, commands) - - def _generate_switchport_commands(self, if_name, req_conf): - commands = [] - curr_conf = self._current_config.get(if_name, {}) - curr_mode = curr_conf.get('mode') - req_mode = req_conf.get('mode') - if req_mode != curr_mode: - commands.append('switchport mode %s' % req_mode) - curr_access_vlan = curr_conf.get('access_vlan') - req_access_vlan = req_conf.get('access_vlan') - if curr_access_vlan != req_access_vlan and req_access_vlan: - commands.append('switchport access vlan %s' % req_access_vlan) - curr_trunk_vlans = curr_conf.get('trunk_allowed_vlans') or set() - if curr_trunk_vlans: - curr_trunk_vlans = set(curr_trunk_vlans) - req_trunk_vlans = req_conf.get('trunk_allowed_vlans') or set() - if req_trunk_vlans: - req_trunk_vlans = set(req_trunk_vlans) - if req_mode != 'access' and curr_trunk_vlans != req_trunk_vlans: - added_vlans = req_trunk_vlans - curr_trunk_vlans - for vlan_id in added_vlans: - commands.append('switchport %s allowed-vlan add %s' % - (req_mode, vlan_id)) - removed_vlans = curr_trunk_vlans - req_trunk_vlans - for vlan_id in removed_vlans: - commands.append('switchport %s allowed-vlan remove %s' % - (req_mode, vlan_id)) - - if commands: - self._add_interface_commands(if_name, commands) - - def generate_commands(self): - for req_conf in self._required_config: - state = req_conf['state'] - if_name = req_conf['name'] - if state == 'absent': - if if_name in self._current_config: - self._generate_no_switchport_commands(if_name) - else: - self._generate_switchport_commands(if_name, req_conf) - - def _generate_vlan_commands(self, vlan_id, req_conf): - curr_vlan = self._current_config.get(vlan_id, {}) - if not curr_vlan: - cmd = "vlan " + vlan_id - self._commands.append("vlan %s" % vlan_id) - self._commands.append("exit") - vlan_name = req_conf['vlan_name'] - if vlan_name: - if vlan_name != curr_vlan.get('vlan_name'): - self._commands.append("vlan %s name %s" % (vlan_id, vlan_name)) - curr_members = set(curr_vlan.get('interfaces', [])) - req_members = req_conf['interfaces'] - mode = req_conf['mode'] - for member in req_members: - if member in curr_members: - continue - if_name = self.get_switchport_command_name(member) - cmd = "interface %s switchport mode %s" % (if_name, mode) - self._commands.append(cmd) - cmd = "interface %s switchport %s allowed-vlan add %s" % ( - if_name, mode, vlan_id) - self._commands.append(cmd) - req_members = set(req_members) - for member in curr_members: - if member in req_members: - continue - if_name = self.get_switchport_command_name(member) - cmd = "interface %s switchport %s allowed-vlan remove %s" % ( - if_name, mode, vlan_id) - self._commands.append(cmd) - - -def main(): - """ main entry point for module execution - """ - OnyxL2InterfaceModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_l3_interface.py b/plugins/modules/network/onyx/onyx_l3_interface.py deleted file mode 100644 index 437ef236eb..0000000000 --- a/plugins/modules/network/onyx/onyx_l3_interface.py +++ /dev/null @@ -1,301 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_l3_interface -author: "Samer Deeb (@samerd)" -short_description: Manage L3 interfaces on Mellanox ONYX network devices -description: - - This module provides declarative management of L3 interfaces - on Mellanox ONYX network devices. -options: - name: - description: - - Name of the L3 interface. - ipv4: - description: - - IPv4 of the L3 interface. - ipv6: - description: - - IPv6 of the L3 interface (not supported for now). - aggregate: - description: List of L3 interfaces definitions - purge: - description: - - Purge L3 interfaces not defined in the I(aggregate) parameter. - default: false - type: bool - state: - description: - - State of the L3 interface configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: Set Eth1/1 IPv4 address - onyx_l3_interface: - name: Eth1/1 - ipv4: 192.168.0.1/24 - -- name: Remove Eth1/1 IPv4 address - onyx_l3_interface: - name: Eth1/1 - state: absent - -- name: Set IP addresses on aggregate - onyx_l3_interface: - aggregate: - - { name: Eth1/1, ipv4: 192.168.2.10/24 } - - { name: Eth1/2, ipv4: 192.168.3.10/24 } - -- name: Remove IP addresses on aggregate - onyx_l3_interface: - aggregate: - - { name: Eth1/1, ipv4: 192.168.2.10/24 } - - { name: Eth1/2, ipv4: 192.168.3.10/24 } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - interfaces ethernet 1/1 ip address 192.168.0.1 /24 -""" -import re -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_interfaces_config - - -class OnyxL3InterfaceModule(BaseOnyxModule): - IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|Eth\d+\/\d+\d+)$") - IF_VLAN_REGEX = re.compile(r"^Vlan (\d+)$") - IF_LOOPBACK_REGEX = re.compile(r"^Loopback (\d+)$") - - IF_TYPE_ETH = "ethernet" - IF_TYPE_LOOPBACK = "loopback" - IF_TYPE_VLAN = "vlan" - - IF_TYPE_MAP = { - IF_TYPE_ETH: IF_ETH_REGEX, - IF_TYPE_VLAN: IF_VLAN_REGEX, - IF_TYPE_LOOPBACK: IF_LOOPBACK_REGEX, - } - - IP_ADDR_ATTR_MAP = { - IF_TYPE_ETH: 'IP Address', - IF_TYPE_VLAN: 'Internet Address', - IF_TYPE_LOOPBACK: 'Internet Address', - } - - _purge = False - - @classmethod - def _get_element_spec(cls): - return dict( - name=dict(type='str'), - ipv4=dict(type='str'), - ipv6=dict(type='str'), - state=dict(default='present', - choices=['present', 'absent', 'enabled', 'disabled']), - ) - - @classmethod - def _get_aggregate_spec(cls, element_spec): - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - return aggregate_spec - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - aggregate_spec = self._get_aggregate_spec(element_spec) - argument_spec = dict( - aggregate=dict(type='list', elements='dict', - options=aggregate_spec), - purge=dict(default=False, type='bool'), - ) - argument_spec.update(element_spec) - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - self._module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def _get_interface_type(self, if_name): - if_type = None - if_id = None - for interface_type, interface_regex in iteritems(self.IF_TYPE_MAP): - match = interface_regex.match(if_name) - if match: - if_type = interface_type - if_id = match.group(1) - break - return if_type, if_id - - def _set_if_type(self, params): - if_name = params['name'] - if_type, if_id = self._get_interface_type(if_name) - if not if_id: - self._module.fail_json( - msg='unsupported interface: %s' % if_name) - params['if_type'] = if_type - params['if_id'] = if_id - - def get_required_config(self): - self._required_config = list() - module_params = self._module.params - aggregate = module_params.get('aggregate') - self._purge = module_params.get('purge', False) - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module_params[key] - self.validate_param_values(item, item) - req_item = item.copy() - self._set_if_type(req_item) - self._required_config.append(req_item) - else: - params = { - 'name': module_params['name'], - 'ipv4': module_params['ipv4'], - 'ipv6': module_params['ipv6'], - 'state': module_params['state'], - } - self.validate_param_values(params) - self._set_if_type(params) - self._required_config.append(params) - - def _get_interfaces_config(self, interface_type): - return get_interfaces_config(self._module, interface_type) - - def _parse_interfaces_config(self, if_type, if_config): - if self._os_version < self.ONYX_API_VERSION: - for if_data in if_config: - if_name = self.get_config_attr(if_data, 'header') - self._get_if_attributes(if_type, if_name, if_data) - else: - for if_config_item in if_config: - for if_name, if_data in iteritems(if_config_item): - if_data = if_data[0] - self._get_if_attributes(if_type, if_name, if_data) - - def _get_if_attributes(self, if_type, if_name, if_data): - ipaddr_attr = self.IP_ADDR_ATTR_MAP[if_type] - regex = self.IF_TYPE_MAP[if_type] - match = regex.match(if_name) - if not match: - return - ipv4 = self.get_config_attr(if_data, ipaddr_attr) - if ipv4: - ipv4 = ipv4.replace(' ', '') - ipv6 = self.get_config_attr(if_data, 'IPv6 address(es)') - if ipv6: - ipv6 = ipv6.replace('[primary]', '') - ipv6 = ipv6.strip() - if_id = match.group(1) - switchport = self.get_config_attr(if_data, 'Switchport mode') - if_obj = { - 'name': if_name, - 'if_id': if_id, - 'if_type': if_type, - 'ipv4': ipv4, - 'ipv6': ipv6, - 'switchport': switchport, - } - self._current_config[if_name] = if_obj - - def load_current_config(self): - # called in base class in run function - self._os_version = self._get_os_version() - self._current_config = dict() - if_types = set([if_obj['if_type'] for if_obj in self._required_config]) - for if_type in if_types: - if_config = self._get_interfaces_config(if_type) - if not if_config: - continue - self._parse_interfaces_config(if_type, if_config) - - def _generate_no_ip_commands(self, req_conf, curr_conf): - curr_ip = curr_conf.get('ipv4') - if_type = req_conf['if_type'] - if_id = req_conf['if_id'] - if curr_ip: - cmd = "interface %s %s no ip address" % (if_type, if_id) - self._commands.append(cmd) - curr_ipv6 = curr_conf.get('ipv6') - if curr_ipv6: - cmd = "interface %s %s no ipv6 address %s" % ( - if_type, if_id, curr_ipv6) - self._commands.append(cmd) - - def _generate_ip_commands(self, req_conf, curr_conf): - curr_ipv4 = curr_conf.get('ipv4') - req_ipv4 = req_conf.get('ipv4') - curr_ipv6 = curr_conf.get('ipv6') - req_ipv6 = req_conf.get('ipv6') - if_type = req_conf['if_type'] - if_id = req_conf['if_id'] - switchport = curr_conf.get('switchport') - if switchport: - cmd = "interface %s %s no switchport force" % (if_type, if_id) - self._commands.append(cmd) - if curr_ipv4 != req_ipv4: - cmd = "interface %s %s ip address %s" % (if_type, if_id, req_ipv4) - self._commands.append(cmd) - if curr_ipv6 != req_ipv6: - cmd = "interface %s %s ipv6 address %s" % ( - if_type, if_id, req_ipv6) - self._commands.append(cmd) - - def generate_commands(self): - req_interfaces = set() - for req_conf in self._required_config: - state = req_conf['state'] - if_name = req_conf['name'] - curr_conf = self._current_config.get(if_name, {}) - if state == 'absent': - self._generate_no_ip_commands(req_conf, curr_conf) - else: - req_interfaces.add(if_name) - self._generate_ip_commands(req_conf, curr_conf) - if self._purge: - for if_name, curr_conf in iteritems(self._current_config): - if if_name not in req_interfaces: - self._generate_no_ip_commands(req_conf, curr_conf) - - -def main(): - """ main entry point for module execution - """ - OnyxL3InterfaceModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_linkagg.py b/plugins/modules/network/onyx/onyx_linkagg.py deleted file mode 100644 index 35da5de939..0000000000 --- a/plugins/modules/network/onyx/onyx_linkagg.py +++ /dev/null @@ -1,353 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_linkagg -author: "Samer Deeb (@samerd)" -short_description: Manage link aggregation groups on Mellanox ONYX network devices -description: - - This module provides declarative management of link aggregation groups - on Mellanox ONYX network devices. -options: - name: - description: - - Name of the link aggregation group. - required: true - mode: - description: - - Mode of the link aggregation group. A value of C(on) will enable LACP. - C(active) configures the link to actively information about the state of the link, - or it can be configured in C(passive) mode ie. send link state information only when - received them from another link. - default: on - choices: ['on', 'active', 'passive'] - members: - description: - - List of members interfaces of the link aggregation group. The value can be - single interface or list of interfaces. - required: true - aggregate: - description: List of link aggregation definitions. - purge: - description: - - Purge link aggregation groups not defined in the I(aggregate) parameter. - default: false - type: bool - state: - description: - - State of the link aggregation group. - default: present - choices: ['present', 'absent', 'up', 'down'] -''' - -EXAMPLES = """ -- name: configure link aggregation group - onyx_linkagg: - name: Po1 - members: - - Eth1/1 - - Eth1/2 - -- name: remove configuration - onyx_linkagg: - name: Po1 - state: absent - -- name: Create aggregate of linkagg definitions - onyx_linkagg: - aggregate: - - { name: Po1, members: [Eth1/1] } - - { name: Po2, members: [Eth1/2] } - -- name: Remove aggregate of linkagg definitions - onyx_linkagg: - aggregate: - - name: Po1 - - name: Po2 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - interface port-channel 1 - - exit - - interface ethernet 1/1 channel-group 1 mode on - - interface ethernet 1/2 channel-group 1 mode on -""" - -import re -from copy import deepcopy - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import get_interfaces_config - - -class OnyxLinkAggModule(BaseOnyxModule): - LAG_ID_REGEX = re.compile(r"^\d+ (Po\d+|Mpo\d+)\(([A-Z])\)$") - LAG_NAME_REGEX = re.compile(r"^(Po|Mpo)(\d+)$") - IF_NAME_REGEX = re.compile(r"^(Eth\d+\/\d+|Eth\d+\/\d+\/\d+)(.*)$") - PORT_CHANNEL = 'port-channel' - CHANNEL_GROUP = 'channel-group' - MLAG_PORT_CHANNEL = 'mlag-port-channel' - MLAG_CHANNEL_GROUP = 'mlag-channel-group' - MLAG_SUMMARY = 'MLAG Port-Channel Summary' - - LAG_TYPE = 'lag' - MLAG_TYPE = 'mlag' - - IF_TYPE_MAP = dict( - lag=PORT_CHANNEL, - mlag=MLAG_PORT_CHANNEL - ) - - _purge = False - - @classmethod - def _get_element_spec(cls): - return dict( - name=dict(type='str'), - members=dict(type='list'), - mode=dict(default='on', choices=['active', 'on', 'passive']), - state=dict(default='present', choices=['present', 'absent']), - ) - - @classmethod - def _get_aggregate_spec(cls, element_spec): - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - return aggregate_spec - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - aggregate_spec = self._get_aggregate_spec(element_spec) - argument_spec = dict( - aggregate=dict(type='list', elements='dict', - options=aggregate_spec), - purge=dict(default=False, type='bool'), - ) - argument_spec.update(element_spec) - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - self._module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def _get_lag_type(self, lag_name): - match = self.LAG_NAME_REGEX.match(lag_name) - if match: - prefix = match.group(1) - if prefix == "Po": - return self.LAG_TYPE - return self.MLAG_TYPE - self._module.fail_json( - msg='invalid lag name: %s, lag name should start with Po or ' - 'Mpo' % lag_name) - - def get_required_config(self): - self._required_config = list() - module_params = self._module.params - aggregate = module_params.get('aggregate') - self._purge = module_params.get('purge', False) - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module_params[key] - self.validate_param_values(item, item) - req_item = item.copy() - req_item['type'] = self._get_lag_type(req_item['name']) - self._required_config.append(req_item) - else: - params = { - 'name': module_params['name'], - 'state': module_params['state'], - 'members': module_params['members'], - 'mode': module_params['mode'], - 'type': self._get_lag_type(module_params['name']), - } - self.validate_param_values(params) - self._required_config.append(params) - - @classmethod - def _extract_lag_name(cls, header): - match = cls.LAG_ID_REGEX.match(header) - state = None - lag_name = None - if match: - state = 'up' if match.group(2) == 'U' else 'down' - lag_name = match.group(1) - return lag_name, state - - @classmethod - def _extract_if_name(cls, member): - match = cls.IF_NAME_REGEX.match(member) - if match: - return match.group(1) - - @classmethod - def _extract_lag_members(cls, lag_type, lag_item): - members = "" - if lag_type == cls.LAG_TYPE: - members = cls.get_config_attr(lag_item, "Member Ports") - else: - for attr_name, attr_val in iteritems(lag_item): - if attr_name.startswith('Local Ports'): - members = attr_val - return [cls._extract_if_name(member) for member in members.split()] - - def _get_port_channels(self, if_type): - return get_interfaces_config(self._module, if_type, flags="summary") - - def _parse_port_channels_summary(self, lag_type, lag_summary): - if lag_type == self.MLAG_TYPE: - if self._os_version >= self.ONYX_API_VERSION: - found_summary = False - for summary_item in lag_summary: - if self.MLAG_SUMMARY in summary_item: - lag_summary = summary_item[self.MLAG_SUMMARY] - if lag_summary: - lag_summary = lag_summary[0] - else: - lag_summary = dict() - found_summary = True - break - if not found_summary: - lag_summary = dict() - else: - lag_summary = lag_summary.get(self.MLAG_SUMMARY, dict()) - for lag_key, lag_data in iteritems(lag_summary): - lag_name, state = self._extract_lag_name(lag_key) - if not lag_name: - continue - lag_members = self._extract_lag_members(lag_type, lag_data[0]) - lag_obj = dict( - name=lag_name, - state=state, - members=lag_members - ) - self._current_config[lag_name] = lag_obj - - def load_current_config(self): - self._current_config = dict() - self._os_version = self._get_os_version() - lag_types = set([lag_obj['type'] for lag_obj in self._required_config]) - for lag_type in lag_types: - if_type = self.IF_TYPE_MAP[lag_type] - lag_summary = self._get_port_channels(if_type) - if lag_summary: - self._parse_port_channels_summary(lag_type, lag_summary) - - def _get_interface_command_suffix(self, if_name): - if if_name.startswith('Eth'): - return if_name.replace("Eth", "ethernet ") - if if_name.startswith('Po'): - return if_name.replace("Po", "port-channel ") - if if_name.startswith('Mpo'): - return if_name.replace("Mpo", "mlag-port-channel ") - self._module.fail_json( - msg='invalid interface name: %s' % if_name) - - def _get_channel_group(self, if_name): - if if_name.startswith('Po'): - return if_name.replace("Po", "channel-group ") - if if_name.startswith('Mpo'): - return if_name.replace("Mpo", "mlag-channel-group ") - self._module.fail_json( - msg='invalid interface name: %s' % if_name) - - def _generate_no_linkagg_commands(self, lag_name): - suffix = self._get_interface_command_suffix(lag_name) - command = 'no interface %s' % suffix - self._commands.append(command) - - def _generate_linkagg_commands(self, lag_name, req_lag): - curr_lag = self._current_config.get(lag_name, {}) - if not curr_lag: - suffix = self._get_interface_command_suffix(lag_name) - self._commands.append("interface %s" % suffix) - self._commands.append("exit") - curr_members = set(curr_lag.get('members', [])) - req_members = set(req_lag.get('members') or []) - - lag_mode = req_lag['mode'] - if req_members != curr_members: - channel_group = self._get_channel_group(lag_name) - channel_group_type = channel_group.split()[0] - for member in req_members: - if member in curr_members: - continue - suffix = self._get_interface_command_suffix(member) - self._commands.append( - "interface %s %s mode %s" % - (suffix, channel_group, lag_mode)) - for member in curr_members: - if member in req_members: - continue - suffix = self._get_interface_command_suffix(member) - self._commands.append( - "interface %s no %s" % (suffix, channel_group_type)) - req_state = req_lag.get('state') - if req_state in ('up', 'down'): - curr_state = curr_lag.get('state') - if curr_state != req_state: - suffix = self._get_interface_command_suffix(lag_name) - cmd = "interface %s " % suffix - if req_state == 'up': - cmd += 'no shutdown' - else: - cmd += 'shutdown' - self._commands.append(cmd) - - def generate_commands(self): - req_lags = set() - for req_conf in self._required_config: - state = req_conf['state'] - lag_name = req_conf['name'] - if state == 'absent': - if lag_name in self._current_config: - self._generate_no_linkagg_commands(lag_name) - else: - req_lags.add(lag_name) - self._generate_linkagg_commands(lag_name, req_conf) - if self._purge: - for lag_name in self._current_config: - if lag_name not in req_lags: - self._generate_no_linkagg_commands(lag_name) - - def check_declarative_intent_params(self, result): - pass - - -def main(): - """ main entry point for module execution - """ - OnyxLinkAggModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_lldp.py b/plugins/modules/network/onyx/onyx_lldp.py deleted file mode 100644 index fc3ebe0d57..0000000000 --- a/plugins/modules/network/onyx/onyx_lldp.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/python - -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_lldp -author: "Samer Deeb (@samerd)" -short_description: Manage LLDP configuration on Mellanox ONYX network devices -description: - - This module provides declarative management of LLDP service configuration - on Mellanox ONYX network devices. -options: - state: - description: - - State of the LLDP protocol configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: Enable LLDP protocol - onyx_lldp: - state: present - -- name: Disable LLDP protocol - onyx_lldp: - state: lldp -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - lldp -""" - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxLldpModule(BaseOnyxModule): - LLDP_ENTRY = 'LLDP' - SHOW_LLDP_CMD = 'show lldp local' - - @classmethod - def _get_element_spec(cls): - return dict( - state=dict(default='present', choices=['present', 'absent']), - ) - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - self._required_config = dict() - module_params = self._module.params - params = { - 'state': module_params['state'], - } - - self.validate_param_values(params) - self._required_config.update(params) - - def _get_lldp_config(self): - return show_cmd(self._module, self.SHOW_LLDP_CMD) - - def load_current_config(self): - self._current_config = dict() - state = 'absent' - config = self._get_lldp_config() or dict() - for item in config: - lldp_state = item.get(self.LLDP_ENTRY) - if lldp_state is not None: - if lldp_state == 'enabled': - state = 'present' - break - self._current_config['state'] = state - - def generate_commands(self): - req_state = self._required_config['state'] - curr_state = self._current_config['state'] - if curr_state != req_state: - cmd = 'lldp' - if req_state == 'absent': - cmd = 'no %s' % cmd - self._commands.append(cmd) - - -def main(): - """ main entry point for module execution - """ - OnyxLldpModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_lldp_interface.py b/plugins/modules/network/onyx/onyx_lldp_interface.py deleted file mode 100644 index b6d2cdb34b..0000000000 --- a/plugins/modules/network/onyx/onyx_lldp_interface.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_lldp_interface -author: "Samer Deeb (@samerd)" -short_description: Manage LLDP interfaces configuration on Mellanox ONYX network devices -description: - - This module provides declarative management of LLDP interfaces - configuration on Mellanox ONYX network devices. -options: - name: - description: - - Name of the interface LLDP should be configured on. - aggregate: - description: List of interfaces LLDP should be configured on. - purge: - description: - - Purge interfaces not defined in the aggregate parameter. - type: bool - default: false - state: - description: - - State of the LLDP configuration. - default: present - choices: ['present', 'absent', 'enabled', 'disabled'] -''' - -EXAMPLES = """ -- name: Configure LLDP on specific interfaces - onyx_lldp_interface: - name: Eth1/1 - state: present - -- name: Disable LLDP on specific interfaces - onyx_lldp_interface: - name: Eth1/1 - state: disabled - -- name: Enable LLDP on specific interfaces - onyx_lldp_interface: - name: Eth1/1 - state: enabled - -- name: Delete LLDP on specific interfaces - onyx_lldp_interface: - name: Eth1/1 - state: absent - -- name: Create aggregate of LLDP interface configurations - onyx_lldp_interface: - aggregate: - - { name: Eth1/1 } - - { name: Eth1/2 } - state: present - -- name: Delete aggregate of LLDP interface configurations - onyx_lldp_interface: - aggregate: - - { name: Eth1/1 } - - { name: Eth1/2 } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - interface ethernet 1/1 lldp transmit - - interface ethernet 1/1 lldp receive -""" -import re -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxLldpInterfaceModule(BaseOnyxModule): - IF_NAME_REGEX = re.compile(r"^(Eth\d+\/\d+|Eth\d+\/\d+\d+)$") - _purge = False - - @classmethod - def _get_element_spec(cls): - return dict( - name=dict(type='str'), - state=dict(default='present', - choices=['present', 'absent', 'enabled', 'disabled']), - ) - - @classmethod - def _get_aggregate_spec(cls, element_spec): - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - return aggregate_spec - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - aggregate_spec = self._get_aggregate_spec(element_spec) - argument_spec = dict( - aggregate=dict(type='list', elements='dict', - options=aggregate_spec), - purge=dict(default=False, type='bool'), - ) - argument_spec.update(element_spec) - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - self._module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def get_required_config(self): - self._required_config = list() - module_params = self._module.params - aggregate = module_params.get('aggregate') - self._purge = module_params.get('purge', False) - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module_params[key] - self.validate_param_values(item, item) - req_item = item.copy() - self._required_config.append(req_item) - else: - params = { - 'name': module_params['name'], - 'state': module_params['state'], - } - self.validate_param_values(params) - self._required_config.append(params) - - def _create_if_lldp_data(self, if_name, if_lldp_data): - return { - 'name': if_name, - 'receive': self.get_config_attr(if_lldp_data, 'Receive'), - 'transmit': self.get_config_attr(if_lldp_data, 'Transmit'), - } - - def _get_lldp_config(self): - return show_cmd(self._module, "show lldp interfaces") - - def load_current_config(self): - # called in base class in run function - self._current_config = dict() - lldp_config = self._get_lldp_config() - if not lldp_config: - return - for if_name, if_lldp_data in iteritems(lldp_config): - match = self.IF_NAME_REGEX.match(if_name) - if not match: - continue - if if_lldp_data: - if_lldp_data = if_lldp_data[0] - self._current_config[if_name] = \ - self._create_if_lldp_data(if_name, if_lldp_data) - - def _get_interface_cmd_name(self, if_name): - return if_name.replace("Eth", "ethernet ") - - def _add_if_lldp_commands(self, if_name, flag, enable): - cmd_prefix = "interface %s " % self._get_interface_cmd_name(if_name) - lldp_cmd = "lldp %s" % flag - if not enable: - lldp_cmd = 'no %s' % lldp_cmd - self._commands.append(cmd_prefix + lldp_cmd) - - def _gen_lldp_commands(self, if_name, req_state, curr_conf): - curr_receive = curr_conf.get('receive') - curr_transmit = curr_conf.get('transmit') - enable = (req_state == 'Enabled') - if curr_receive != req_state: - flag = 'receive' - self._add_if_lldp_commands(if_name, flag, enable) - if curr_transmit != req_state: - flag = 'transmit' - self._add_if_lldp_commands(if_name, flag, enable) - - def generate_commands(self): - req_interfaces = set() - for req_conf in self._required_config: - state = req_conf['state'] - if_name = req_conf['name'] - if state in ('absent', 'disabled'): - req_state = 'Disabled' - else: - req_interfaces.add(if_name) - req_state = 'Enabled' - curr_conf = self._current_config.get(if_name, {}) - self._gen_lldp_commands(if_name, req_state, curr_conf) - if self._purge: - for if_name, curr_conf in iteritems(self._current_config): - if if_name not in req_interfaces: - req_state = 'Disabled' - self._gen_lldp_commands(if_name, req_state, curr_conf) - - -def main(): - """ main entry point for module execution - """ - OnyxLldpInterfaceModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_magp.py b/plugins/modules/network/onyx/onyx_magp.py deleted file mode 100644 index 528eb82e5a..0000000000 --- a/plugins/modules/network/onyx/onyx_magp.py +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_magp -author: "Samer Deeb (@samerd)" -short_description: Manage MAGP protocol on Mellanox ONYX network devices -description: - - This module provides declarative management of MAGP protocol on vlan - interface of Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.4000 -options: - magp_id: - description: - - "MAGP instance number 1-255" - required: true - interface: - description: - - VLAN Interface name. - required: true - state: - description: - - MAGP state. - default: present - choices: ['present', 'absent', 'enabled', 'disabled'] - router_ip: - description: - - MAGP router IP address. - router_mac: - description: - - MAGP router MAC address. -''' - -EXAMPLES = """ -- name: run add vlan interface with magp - onyx_magp: - magp_id: 103 - router_ip: 192.168.8.2 - router_mac: AA:1B:2C:3D:4E:5F - interface: Vlan 1002 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface vlan 234 magp 103 - - exit - - interface vlan 234 magp 103 ip virtual-router address 1.2.3.4 -""" -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxMagpModule(BaseOnyxModule): - IF_VLAN_REGEX = re.compile(r"^Vlan (\d+)$") - - @classmethod - def _get_element_spec(cls): - return dict( - magp_id=dict(type='int', required=True), - state=dict(default='present', - choices=['present', 'absent', 'enabled', 'disabled']), - interface=dict(required=True), - router_ip=dict(), - router_mac=dict(), - ) - - def init_module(self): - """ Ansible module initialization - """ - element_spec = self._get_element_spec() - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def validate_magp_id(self, value): - if value and not 1 <= int(value) <= 255: - self._module.fail_json(msg='magp id must be between 1 and 255') - - def get_required_config(self): - module_params = self._module.params - interface = module_params['interface'] - match = self.IF_VLAN_REGEX.match(interface) - vlan_id = 0 - if match: - vlan_id = int(match.group(1)) - else: - self._module.fail_json( - msg='Invalid interface name: should be "Vlan "') - - self._required_config = dict( - magp_id=module_params['magp_id'], - state=module_params['state'], - vlan_id=vlan_id, - router_ip=module_params['router_ip'], - router_mac=module_params['router_mac']) - self.validate_param_values(self._required_config) - - @classmethod - def get_magp_id(cls, item): - header = cls.get_config_attr(item, "header") - return int(header.split()[1]) - - def _create_magp_instance_data(self, magp_id, item): - vlan_id = int(self.get_config_attr(item, "Interface vlan")) - state = self.get_config_attr(item, "Admin state").lower() - return dict( - magp_id=magp_id, - state=state, - vlan_id=vlan_id, - router_ip=self.get_config_attr(item, "Virtual IP"), - router_mac=self.get_config_attr(item, "Virtual MAC")) - - def _update_magp_data(self, magp_data): - if self._os_version >= self.ONYX_API_VERSION: - for magp_config in magp_data: - for magp_name, data in iteritems(magp_config): - magp_id = int(magp_name.replace('MAGP ', '')) - self._current_config[magp_id] = \ - self._create_magp_instance_data(magp_id, data[0]) - else: - for magp_item in magp_data: - magp_id = self.get_magp_id(magp_item) - inst_data = self._create_magp_instance_data(magp_id, magp_item) - self._current_config[magp_id] = inst_data - - def _get_magp_config(self): - cmd = "show magp" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - # called in base class in run function - self._os_version = self._get_os_version() - self._current_config = dict() - magp_data = self._get_magp_config() - if magp_data: - self._update_magp_data(magp_data) - - def _generate_no_magp_commands(self): - req_vlan_id = self._required_config['vlan_id'] - req_magp_id = self._required_config['magp_id'] - curr_magp_data = self._current_config.get(req_magp_id) - if not curr_magp_data: - return - curr_vlan_id = curr_magp_data.get(req_vlan_id) - if curr_vlan_id == req_vlan_id: - cmd = 'interface vlan %s no magp %s' % (req_vlan_id, req_magp_id) - self._commands.append(cmd) - - def _generate_magp_commands(self, req_state): - req_vlan_id = self._required_config['vlan_id'] - req_magp_id = self._required_config['magp_id'] - curr_magp_data = self._current_config.get(req_magp_id, dict()) - curr_vlan_id = curr_magp_data.get('vlan_id') - magp_prefix = 'interface vlan %s magp %s' % (req_vlan_id, req_magp_id) - create_new_magp = False - if curr_vlan_id != req_vlan_id: - if curr_vlan_id: - cmd = 'interface vlan %s no magp %s' % ( - curr_vlan_id, req_magp_id) - self._commands.append(cmd) - create_new_magp = True - self._commands.append(magp_prefix) - self._commands.append('exit') - req_router_ip = self._required_config['router_ip'] - curr_router_ip = curr_magp_data.get('router_ip') - if req_router_ip: - if curr_router_ip != req_router_ip or create_new_magp: - cmd = '%s ip virtual-router address %s' % ( - magp_prefix, req_router_ip) - self._commands.append(cmd) - else: - if curr_router_ip and curr_router_ip != '0.0.0.0': - cmd = '%s no ip virtual-router address' % magp_prefix - self._commands.append(cmd) - req_router_mac = self._required_config['router_mac'] - curr_router_mac = curr_magp_data.get('router_mac') - if curr_router_mac: - curr_router_mac = curr_router_mac.lower() - if req_router_mac: - req_router_mac = req_router_mac.lower() - if curr_router_mac != req_router_mac or create_new_magp: - cmd = '%s ip virtual-router mac-address %s' % ( - magp_prefix, req_router_mac) - self._commands.append(cmd) - else: - if curr_router_mac and curr_router_mac != '00:00:00:00:00:00': - cmd = '%s no ip virtual-router mac-address' % magp_prefix - self._commands.append(cmd) - if req_state in ('enabled', 'disabled'): - curr_state = curr_magp_data.get('state', 'enabled') - if curr_state != req_state: - if req_state == 'enabled': - suffix = 'no shutdown' - else: - suffix = 'shutdown' - cmd = '%s %s' % (magp_prefix, suffix) - self._commands.append(cmd) - - def generate_commands(self): - req_state = self._required_config['state'] - if req_state == 'absent': - return self._generate_no_magp_commands() - return self._generate_magp_commands(req_state) - - -def main(): - """ main entry point for module execution - """ - OnyxMagpModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_mlag_ipl.py b/plugins/modules/network/onyx/onyx_mlag_ipl.py deleted file mode 100644 index 6a18943015..0000000000 --- a/plugins/modules/network/onyx/onyx_mlag_ipl.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_mlag_ipl -author: "Samer Deeb (@samerd)" -short_description: Manage IPL (inter-peer link) on Mellanox ONYX network devices -description: - - This module provides declarative management of IPL (inter-peer link) - management on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.4000 -options: - name: - description: - - Name of the interface (port-channel) IPL should be configured on. - required: true - vlan_interface: - description: - - Name of the IPL vlan interface. - state: - description: - - IPL state. - default: present - choices: ['present', 'absent'] - peer_address: - description: - - IPL peer IP address. -''' - -EXAMPLES = """ -- name: run configure ipl - onyx_mlag_ipl: - name: Po1 - vlan_interface: Vlan 322 - state: present - peer_address: 192.168.7.1 - -- name: run remove ipl - onyx_mlag_ipl: - name: Po1 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface port-channel 1 ipl 1 - - interface vlan 1024 ipl 1 peer-address 10.10.10.10 -""" -import re - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxMlagIplModule(BaseOnyxModule): - VLAN_IF_REGEX = re.compile(r'^Vlan \d+') - - @classmethod - def _get_element_spec(cls): - return dict( - name=dict(required=True), - state=dict(default='present', - choices=['present', 'absent']), - peer_address=dict(), - vlan_interface=dict(), - ) - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict( - name=module_params['name'], - state=module_params['state'], - peer_address=module_params['peer_address'], - vlan_interface=module_params['vlan_interface']) - self.validate_param_values(self._required_config) - - def _update_mlag_data(self, mlag_data): - if not mlag_data: - return - mlag_summary = mlag_data.get("MLAG IPLs Summary", {}) - ipl_id = "1" - ipl_list = mlag_summary.get(ipl_id) - if ipl_list: - ipl_data = ipl_list[0] - vlan_id = ipl_data.get("Vlan Interface") - vlan_interface = "" - if vlan_id != "N/A": - vlan_interface = "Vlan %s" % vlan_id - peer_address = ipl_data.get("Peer IP address") - name = ipl_data.get("Group Port-Channel") - self._current_config = dict( - name=name, - peer_address=peer_address, - vlan_interface=vlan_interface) - - def _show_mlag_data(self): - cmd = "show mlag" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - # called in base class in run function - self._current_config = dict() - mlag_data = self._show_mlag_data() - self._update_mlag_data(mlag_data) - - def _get_interface_cmd_name(self, if_name): - if if_name.startswith('Po'): - return if_name.replace("Po", "port-channel ") - self._module.fail_json( - msg='invalid interface name: %s' % if_name) - - def _generate_port_channel_command(self, if_name, enable): - if_cmd_name = self._get_interface_cmd_name(if_name) - if enable: - ipl_cmd = 'ipl 1' - else: - ipl_cmd = "no ipl 1" - cmd = "interface %s %s" % (if_cmd_name, ipl_cmd) - return cmd - - def _generate_vlan_if_command(self, if_name, enable, peer_address): - if_cmd_name = if_name.lower() - if enable: - ipl_cmd = 'ipl 1 peer-address %s' % peer_address - else: - ipl_cmd = "no ipl 1" - cmd = "interface %s %s" % (if_cmd_name, ipl_cmd) - return cmd - - def _generate_no_ipl_commands(self): - curr_interface = self._current_config.get('name') - req_interface = self._required_config.get('name') - if curr_interface == req_interface: - cmd = self._generate_port_channel_command( - req_interface, enable=False) - self._commands.append(cmd) - - def _generate_ipl_commands(self): - curr_interface = self._current_config.get('name') - req_interface = self._required_config.get('name') - if curr_interface != req_interface: - if curr_interface and curr_interface != 'N/A': - cmd = self._generate_port_channel_command( - curr_interface, enable=False) - self._commands.append(cmd) - cmd = self._generate_port_channel_command( - req_interface, enable=True) - self._commands.append(cmd) - curr_vlan = self._current_config.get('vlan_interface') - req_vlan = self._required_config.get('vlan_interface') - add_peer = False - if curr_vlan != req_vlan: - add_peer = True - if curr_vlan: - cmd = self._generate_vlan_if_command(curr_vlan, enable=False, - peer_address=None) - self._commands.append(cmd) - curr_peer = self._current_config.get('peer_address') - req_peer = self._required_config.get('peer_address') - if req_peer != curr_peer: - add_peer = True - if add_peer and req_peer: - cmd = self._generate_vlan_if_command(req_vlan, enable=True, - peer_address=req_peer) - self._commands.append(cmd) - - def generate_commands(self): - state = self._required_config['state'] - if state == 'absent': - self._generate_no_ipl_commands() - else: - self._generate_ipl_commands() - - -def main(): - """ main entry point for module execution - """ - OnyxMlagIplModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_mlag_vip.py b/plugins/modules/network/onyx/onyx_mlag_vip.py deleted file mode 100644 index 5abd6b89d5..0000000000 --- a/plugins/modules/network/onyx/onyx_mlag_vip.py +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_mlag_vip -author: "Samer Deeb (@samerd)" -short_description: Configures MLAG VIP on Mellanox ONYX network devices -description: - - This module provides declarative management of MLAG virtual IPs - on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.4000 -options: - ipaddress: - description: - - Virtual IP address of the MLAG. Required if I(state=present). - group_name: - description: - - MLAG group name. Required if I(state=present). - mac_address: - description: - - MLAG system MAC address. Required if I(state=present). - state: - description: - - MLAG VIP state. - choices: ['present', 'absent'] - delay: - description: - - Delay interval, in seconds, waiting for the changes on mlag VIP to take - effect. - default: 12 -''' - -EXAMPLES = """ -- name: configure mlag-vip - onyx_mlag_vip: - ipaddress: 50.3.3.1/24 - group_name: ansible-test-group - mac_address: 00:11:12:23:34:45 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - mlag-vip ansible_test_group ip 50.3.3.1 /24 force - - no mlag shutdown -""" - -import time - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxMLagVipModule(BaseOnyxModule): - - def init_module(self): - """ initialize module - """ - element_spec = dict( - ipaddress=dict(), - group_name=dict(), - mac_address=dict(), - delay=dict(type='int', default=12), - state=dict(choices=['present', 'absent'], default='present'), - ) - argument_spec = dict() - - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - lag_params = { - 'ipaddress': module_params['ipaddress'], - 'group_name': module_params['group_name'], - 'mac_address': module_params['mac_address'], - 'delay': module_params['delay'], - 'state': module_params['state'], - } - - self.validate_param_values(lag_params) - self._required_config = lag_params - - def _show_mlag_cmd(self, cmd): - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def _show_mlag(self): - cmd = "show mlag" - return self._show_mlag_cmd(cmd) - - def _show_mlag_vip(self): - cmd = "show mlag-vip" - return self._show_mlag_cmd(cmd) - - def load_current_config(self): - self._current_config = dict() - mlag_config = self._show_mlag() - mlag_vip_config = self._show_mlag_vip() - if mlag_vip_config: - mlag_vip = mlag_vip_config.get("MLAG-VIP", {}) - self._current_config['group_name'] = \ - mlag_vip.get("MLAG group name") - self._current_config['ipaddress'] = \ - mlag_vip.get("MLAG VIP address") - if mlag_config: - self._current_config['mac_address'] = \ - mlag_config.get("System-mac") - - def generate_commands(self): - state = self._required_config['state'] - if state == 'present': - self._generate_mlag_vip_cmds() - else: - self._generate_no_mlag_vip_cmds() - - def _generate_mlag_vip_cmds(self): - current_group = self._current_config.get('group_name') - current_ip = self._current_config.get('ipaddress') - current_mac = self._current_config.get('mac_address') - if current_mac: - current_mac = current_mac.lower() - - req_group = self._required_config.get('group_name') - req_ip = self._required_config.get('ipaddress') - req_mac = self._required_config.get('mac_address') - if req_mac: - req_mac = req_mac.lower() - - if req_ip is not None: - if req_group is None: - self._module.fail_json(msg='In order to configure Mlag-Vip you must send ' - 'group name param beside IPaddress') - ipaddr, mask = req_ip.split('/') - if req_group != current_group or req_ip != current_ip: - self._commands.append('mlag-vip %s ip %s /%s force' % (req_group, ipaddr, mask)) - elif req_group and req_group != current_group: - self._commands.append('mlag-vip %s' % req_group) - - if req_mac and req_mac != current_mac: - self._commands.append( - 'mlag system-mac %s' % (req_mac)) - if self._commands: - self._commands.append('no mlag shutdown') - - def _generate_no_mlag_vip_cmds(self): - if self._current_config.get('group_name'): - self._commands.append('no mlag-vip') - - def check_declarative_intent_params(self, result): - if not result['changed']: - return - delay_interval = self._required_config.get('delay') - if delay_interval > 0: - time.sleep(delay_interval) - for cmd in ("show mlag-vip", ""): - show_cmd(self._module, cmd, json_fmt=False, fail_on_error=False) - - -def main(): - """ main entry point for module execution - """ - OnyxMLagVipModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_ntp.py b/plugins/modules/network/onyx/onyx_ntp.py deleted file mode 100644 index 1ae9d7bdd8..0000000000 --- a/plugins/modules/network/onyx/onyx_ntp.py +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_ntp -author: "Sara-Touqan (@sarato)" -short_description: Manage NTP general configurations and ntp keys configurations on Mellanox ONYX network devices -description: - - This module provides declarative management of NTP & NTP Keys - on Mellanox ONYX network devices. -options: - state: - description: - - State of the NTP configuration. - choices: ['enabled', 'disabled'] - type: str - authenticate_state: - description: - - State of the NTP authentication configuration. - choices: ['enabled', 'disabled'] - type: str - ntp_authentication_keys: - type: list - description: - - List of ntp authentication keys - suboptions: - auth_key_id: - description: - - Configures ntp key-id, range 1-65534 - required: true - type: int - auth_key_encrypt_type: - description: - - encryption type used to configure ntp authentication key. - required: true - choices: ['md5', 'sha1'] - type: str - auth_key_password: - description: - - password used for ntp authentication key. - required: true - type: str - auth_key_state: - description: - - Used to decide if you want to delete given ntp key or not - choices: ['present', 'absent'] - type: str - trusted_keys: - type: list - description: - - List of ntp trusted keys -''' - -EXAMPLES = """ -- name: configure NTP - onyx_ntp: - state: enabled - authenticate_state: enabled - ntp_authentication_keys: - - auth_key_id: 1 - auth_key_encrypt_type: md5 - auth_key_password: 12345 - auth_key_state: absent - trusted_keys: 1,2,3 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - ntp enable - - ntp disable - - ntp authenticate - - no ntp authenticate - - ntp authentication-key 1 md5 12345 - - no ntp authentication-key 1 - - ntp trusted-key 1,2,3 -""" - - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxNTPModule(BaseOnyxModule): - - def init_module(self): - """ module initialization - """ - ntp_authentication_key_spec = dict(auth_key_id=dict(type='int', required=True), - auth_key_encrypt_type=dict(required=True, choices=['md5', 'sha1']), - auth_key_password=dict(required=True), - auth_key_state=dict(choices=['present', 'absent'])) - element_spec = dict( - state=dict(choices=['enabled', 'disabled']), - authenticate_state=dict(choices=['enabled', 'disabled']), - ntp_authentication_keys=dict(type='list', elements='dict', options=ntp_authentication_key_spec), - trusted_keys=dict(type='list', elements='int') - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def _validate_key_id(self): - keys_id_list = self._required_config.get("ntp_authentication_keys") - if keys_id_list: - for key_item in keys_id_list: - key_id = key_item.get("auth_key_id") - if (key_id < 1) or (key_id > 65534): - self._module.fail_json( - msg='Invalid Key value, value should be in the range 1-65534') - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - self._validate_key_id() - - def _show_ntp_config(self): - show_cmds = [] - cmd = "show ntp" - show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)) - cmd = "show ntp keys" - show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)) - return show_cmds - - def _set_ntp_keys_config(self, ntp_config): - if not ntp_config: - return - for req_ntp_auth_key in ntp_config: - ecryption_type = req_ntp_auth_key.get("Encryption Type") - self._current_config[req_ntp_auth_key.get("header")] = ecryption_type - - def _set_ntp_config(self, ntp_config): - ntp_config = ntp_config[0] - if not ntp_config: - return - self._current_config['state'] = ntp_config.get("NTP is administratively") - self._current_config['authenticate_state'] = ntp_config.get("NTP Authentication administratively") - - def load_current_config(self): - self._current_config = dict() - ntp_config = self._show_ntp_config() - if ntp_config: - if ntp_config[0]: - self._set_ntp_config(ntp_config[0]) - if ntp_config[1]: - self._set_ntp_keys_config(ntp_config[1]) - - def generate_commands(self): - current_state = self._current_config.get("state") - state = self._required_config.get("state") - if state is None: - state = current_state - if state is not None: - if current_state != state: - if state == 'enabled': - self._commands.append('ntp enable') - else: - self._commands.append('no ntp enable') - authenticate_state = self._required_config.get("authenticate_state") - if authenticate_state: - current_authenticate_state = self._current_config.get("authenticate_state") - if authenticate_state is not None: - if current_authenticate_state != authenticate_state: - if authenticate_state == 'enabled': - self._commands.append('ntp authenticate') - else: - self._commands.append('no ntp authenticate') - req_ntp_auth_keys = self._required_config.get('ntp_authentication_keys') - if req_ntp_auth_keys: - if req_ntp_auth_keys is not None: - for req_ntp_auth_key in req_ntp_auth_keys: - req_key_id = req_ntp_auth_key.get('auth_key_id') - req_key = 'NTP Key ' + str(req_key_id) - current_req_key = self._current_config.get(req_key) - auth_key_state = req_ntp_auth_key.get('auth_key_state') - req_encrypt_type = req_ntp_auth_key.get('auth_key_encrypt_type') - req_password = req_ntp_auth_key.get('auth_key_password') - if current_req_key: - if req_encrypt_type == current_req_key: - if auth_key_state: - if auth_key_state == 'absent': - self._commands.append('no ntp authentication-key {0}' .format(req_key_id)) - else: - continue - else: - if auth_key_state: - if auth_key_state == 'present': - self._commands.append('ntp authentication-key {0} {1} {2}' - .format(req_key_id, - req_encrypt_type, - req_password)) - else: - self._commands.append('ntp authentication-key {0} {1} {2}' - .format(req_key_id, - req_encrypt_type, - req_password)) - - else: - if auth_key_state: - if auth_key_state == 'present': - self._commands.append('ntp authentication-key {0} {1} {2}' - .format(req_key_id, - req_encrypt_type, - req_password)) - else: - self._commands.append('ntp authentication-key {0} {1} {2}' - .format(req_key_id, - req_encrypt_type, - req_password)) - - req_trusted_keys = self._required_config.get('trusted_keys') - if req_trusted_keys: - for key in req_trusted_keys: - self._commands.append('ntp trusted-key {0}' .format(key)) - - -def main(): - """ main entry point for module execution - """ - OnyxNTPModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_ntp_servers_peers.py b/plugins/modules/network/onyx/onyx_ntp_servers_peers.py deleted file mode 100644 index ddebb63825..0000000000 --- a/plugins/modules/network/onyx/onyx_ntp_servers_peers.py +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_ntp_servers_peers -author: "Sara-Touqan (@sarato)" -short_description: Configures NTP peers and servers parameters -description: - - This module provides declarative management of NTP peers and servers configuration on Mellanox ONYX network devices. -options: - peer: - type: list - description: - - List of ntp peers. - suboptions: - ip_or_name: - description: - - Configures ntp peer name or ip. - required: true - type: str - enabled: - description: - - Disables/Enables ntp peer state - type: bool - version: - description: - - version number for the ntp peer - choices: [3, 4] - type: int - key_id: - description: - - Used to configure the key-id for the ntp peer - type: int - state: - description: - - Indicates if the ntp peer exists or should be deleted - choices: ['present', 'absent'] - type: str - server: - type: list - description: - - List of ntp servers. - suboptions: - ip_or_name: - description: - - Configures ntp server name or ip. - required: true - type: str - enabled: - description: - - Disables/Enables ntp server - type: bool - trusted_enable: - description: - - Disables/Enables the trusted state for the ntp server. - type: bool - version: - description: - - version number for the ntp server - choices: [3, 4] - type: int - key_id: - description: - - Used to configure the key-id for the ntp server - type: int - state: - description: - - Indicates if the ntp peer exists or should be deleted. - choices: ['present', 'absent'] - type: str - ntpdate: - description: - - Sets system clock once from a remote server using NTP. - type: str -''' - -EXAMPLES = """ -- name: configure NTP peers and servers - onyx_ntp_peers_servers: - peer: - - ip_or_name: 1.1.1.1 - enabled: yes - version: 4 - key_id: 6 - state: present - server: - - ip_or_name: 2.2.2.2 - enabled: true - version: 3 - key_id: 8 - trusted_enable: no - state: present - ntpdate: 192.168.10.10 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - ntp peer 1.1.1.1 disable - no ntp peer 1.1.1.1 disable - ntp peer 1.1.1.1 keyId 6 - ntp peer 1.1.1.1 version 4 - no ntp peer 1.1.1.1 - ntp server 2.2.2.2 disable - no ntp server 2.2.2.2 disable - ntp server 2.2.2.2 keyID 8 - ntp server 2.2.2.2 version 3 - ntp server 2.2.2.2 trusted-enable - no ntp server 2.2.2.2 - ntp server 192.168.10.10 - ntpdate 192.168.10.10 -""" - -from copy import deepcopy -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxNTPServersPeersModule(BaseOnyxModule): - - def init_module(self): - """ module initialization - """ - peer_spec = dict(ip_or_name=dict(required=True), - enabled=dict(type='bool'), - version=dict(type='int', choices=[3, 4]), - key_id=dict(type='int'), - state=dict(choices=['present', 'absent'])) - server_spec = dict(ip_or_name=dict(required=True), - enabled=dict(type='bool'), - version=dict(type='int', choices=[3, 4]), - trusted_enable=dict(type='bool'), - key_id=dict(type='int'), - state=dict(choices=['present', 'absent'])) - element_spec = dict(peer=dict(type='list', elements='dict', options=peer_spec), - server=dict(type='list', elements='dict', options=server_spec), - ntpdate=dict()) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def _show_peers_servers_config(self): - cmd = "show ntp configured" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def _set_servers_config(self, peers_servers_config): - servers = dict() - peers = dict() - if not peers_servers_config: - return - index = 0 - for peer_server in peers_servers_config: - if (index == 0): - index += 1 - continue - else: - header_list = peer_server.get("header").split(" ") - header_type = header_list[1] - if peer_server.get("Enabled") == "yes": - enabled_state = True - else: - enabled_state = False - if (header_type == 'server'): - trusted_state = peer_server.get("Trusted") - if trusted_state == 'yes': - trusted_state = True - else: - trusted_state = False - server_entry = {"version": peer_server.get("NTP version"), - "enabled": enabled_state, - "trusted_enable": trusted_state, - "key_id": peer_server.get("Key ID")} - servers[header_list[2]] = server_entry - else: - peer_entry = {"version": peer_server.get("NTP version"), - "enabled": enabled_state, - "key_id": peer_server.get("Key ID")} - peers[header_list[2]] = peer_entry - index += 1 - self._current_config = dict(server=servers, - peer=peers) - - def load_current_config(self): - servers = dict() - peers = dict() - self._current_config = dict(server=servers, - peer=peers) - peers_servers_config = self._show_peers_servers_config() - if peers_servers_config: - self._set_servers_config(peers_servers_config) - - def generate_commands(self): - for option in self._current_config: - req_ntp = self._required_config.get(option) - if req_ntp is not None: - for ntp_peer in req_ntp: - peer_name = ntp_peer.get('ip_or_name') - peer_key = ntp_peer.get('key_id') - peer_state = ntp_peer.get("state") - peer_enabled = ntp_peer.get("enabled") - peer_version = ntp_peer.get("version") - peer_key = ntp_peer.get("key_id") - curr_name = self._current_config.get(option).get(peer_name) - peer_version = ntp_peer.get('version') - if self._current_config.get(option) and curr_name: - if peer_state: - if(peer_state == "absent"): - self._commands.append('no ntp {0} {1}' .format(option, peer_name)) - continue - if peer_enabled is not None: - if curr_name.get("enabled") != peer_enabled: - if(peer_enabled is True): - self._commands.append('no ntp {0} {1} disable' .format(option, peer_name)) - else: - self._commands.append('ntp {0} {1} disable' .format(option, peer_name)) - if peer_version: - if (int(curr_name.get("version")) != peer_version): - self._commands.append('ntp {0} {1} version {2}' .format(option, peer_name, peer_version)) - if peer_key: - if curr_name.get("key_id") != "none": - if (int(curr_name.get("key_id")) != peer_key): - self._commands.append('ntp {0} {1} keyID {2}' .format(option, peer_name, peer_key)) - else: - self._commands.append('ntp {0} {1} keyID {2}' .format(option, peer_name, peer_key)) - if option == "server": - server_trusted = ntp_peer.get("trusted_enable") - if server_trusted is not None: - if (curr_name.get("trusted_enable") != server_trusted): - if server_trusted is True: - self._commands.append('ntp {0} {1} trusted-enable' .format(option, peer_name)) - else: - self._commands.append('no ntp {0} {1} trusted-enable' .format(option, peer_name)) - else: - if peer_state: - if(peer_state == "absent"): - continue - if peer_enabled is not None: - if(peer_enabled is True): - self._commands.append('no ntp {0} {1} disable' .format(option, peer_name)) - else: - self._commands.append('ntp {0} {1} disable' .format(option, peer_name)) - else: - self._commands.append('ntp {0} {1} disable' .format(option, peer_name)) - if peer_version: - self._commands.append('ntp {0} {1} version {2}' .format(option, peer_name, peer_version)) - if peer_key: - self._commands.append('ntp {0} {1} keyID {2}' .format(option, peer_name, peer_key)) - - ntpdate = self._required_config.get("ntpdate") - if ntpdate is not None: - self._commands.append('ntpdate {0}' .format(ntpdate)) - - -def main(): - """ main entry point for module execution - """ - OnyxNTPServersPeersModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_ospf.py b/plugins/modules/network/onyx/onyx_ospf.py deleted file mode 100644 index e7cc2e1e5c..0000000000 --- a/plugins/modules/network/onyx/onyx_ospf.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_ospf -author: "Samer Deeb (@samerd)" -short_description: Manage OSPF protocol on Mellanox ONYX network devices -description: - - This module provides declarative management and configuration of OSPF - protocol on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.4000 -options: - ospf: - description: - - "OSPF instance number 1-65535" - required: true - router_id: - description: - - OSPF router ID. Required if I(state=present). - interfaces: - description: - - List of interfaces and areas. Required if I(state=present). - suboptions: - name: - description: - - Interface name. - required: true - area: - description: - - OSPF area. - required: true - state: - description: - - OSPF state. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: add ospf router to interface - onyx_ospf: - ospf: 2 - router_id: 192.168.8.2 - interfaces: - - name: Eth1/1 - - area: 0.0.0.0 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - router ospf 2 - - router-id 192.168.8.2 - - exit - - interface ethernet 1/1 ip ospf area 0.0.0.0 -""" -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxOspfModule(BaseOnyxModule): - OSPF_IF_REGEX = re.compile( - r'^(Loopback\d+|Eth\d+\/\d+|Vlan\d+|Po\d+)\s+(\S+).*') - OSPF_ROUTER_REGEX = re.compile(r'^Routing Process (\d+).*ID\s+(\S+).*') - - @classmethod - def _get_element_spec(cls): - interface_spec = dict( - name=dict(required=True), - area=dict(required=True), - ) - element_spec = dict( - ospf=dict(type='int', required=True), - router_id=dict(), - interfaces=dict(type='list', elements='dict', - options=interface_spec), - state=dict(choices=['present', 'absent'], default='present'), - ) - return element_spec - - def init_module(self): - """ Ansible module initialization - """ - element_spec = self._get_element_spec() - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def validate_ospf(self, value): - if value and not 1 <= int(value) <= 65535: - self._module.fail_json(msg='ospf id must be between 1 and 65535') - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict( - ospf=module_params['ospf'], - router_id=module_params['router_id'], - state=module_params['state'], - ) - interfaces = module_params['interfaces'] or list() - req_interfaces = self._required_config['interfaces'] = dict() - for interface_data in interfaces: - req_interfaces[interface_data['name']] = interface_data['area'] - self.validate_param_values(self._required_config) - - def _update_ospf_data(self, ospf_data): - match = self.OSPF_ROUTER_REGEX.match(ospf_data) - if match: - ospf_id = int(match.group(1)) - router_id = match.group(2) - self._current_config['ospf'] = ospf_id - self._current_config['router_id'] = router_id - - def _update_ospf_interfaces(self, ospf_interfaces): - interfaces = self._current_config['interfaces'] = dict() - lines = ospf_interfaces.split('\n') - for line in lines: - line = line.strip() - match = self.OSPF_IF_REGEX.match(line) - if match: - name = match.group(1) - area = match.group(2) - for prefix in ("Vlan", "Loopback"): - if name.startswith(prefix): - name = name.replace(prefix, prefix + ' ') - interfaces[name] = area - - def _get_ospf_config(self, ospf_id): - cmd = 'show ip ospf %s | include Process' % ospf_id - return show_cmd(self._module, cmd, json_fmt=False, fail_on_error=False) - - def _get_ospf_interfaces_config(self, ospf_id): - cmd = 'show ip ospf interface %s brief' % ospf_id - return show_cmd(self._module, cmd, json_fmt=False, fail_on_error=False) - - def load_current_config(self): - # called in base class in run function - ospf_id = self._required_config['ospf'] - self._current_config = dict() - ospf_data = self._get_ospf_config(ospf_id) - if ospf_data: - self._update_ospf_data(ospf_data) - ospf_interfaces = self._get_ospf_interfaces_config(ospf_id) - if ospf_interfaces: - self._update_ospf_interfaces(ospf_interfaces) - - def _generate_no_ospf_commands(self): - req_ospf_id = self._required_config['ospf'] - curr_ospf_id = self._current_config.get('ospf') - if curr_ospf_id == req_ospf_id: - cmd = 'no router ospf %s' % req_ospf_id - self._commands.append(cmd) - - def _get_interface_command_name(self, if_name): - if if_name.startswith('Eth'): - return if_name.replace("Eth", "ethernet ") - if if_name.startswith('Po'): - return if_name.replace("Po", "port-channel ") - if if_name.startswith('Vlan'): - return if_name.replace("Vlan", "vlan") - if if_name.startswith('Loopback'): - return if_name.replace("Loopback", "loopback") - self._module.fail_json( - msg='invalid interface name: %s' % if_name) - - def _get_interface_area_cmd(self, if_name, area): - interface_prefix = self._get_interface_command_name(if_name) - if area: - area_cmd = 'ip ospf area %s' % area - else: - area_cmd = 'no ip ospf area' - cmd = 'interface %s %s' % (interface_prefix, area_cmd) - return cmd - - def _generate_ospf_commands(self): - req_router_id = self._required_config['router_id'] - req_ospf_id = self._required_config['ospf'] - curr_router_id = self._current_config.get('router_id') - curr_ospf_id = self._current_config.get('ospf') - if curr_ospf_id != req_ospf_id or req_router_id != curr_router_id: - cmd = 'router ospf %s' % req_ospf_id - self._commands.append(cmd) - if req_router_id != curr_router_id: - if req_router_id: - cmd = 'router-id %s' % req_router_id - else: - cmd = 'no router-id' - self._commands.append(cmd) - self._commands.append('exit') - req_interfaces = self._required_config['interfaces'] - curr_interfaces = self._current_config.get('interfaces', dict()) - for if_name, area in iteritems(req_interfaces): - curr_area = curr_interfaces.get(if_name) - if curr_area != area: - cmd = self._get_interface_area_cmd(if_name, area) - self._commands.append(cmd) - for if_name in curr_interfaces: - if if_name not in req_interfaces: - cmd = self._get_interface_area_cmd(if_name, None) - self._commands.append(cmd) - - def generate_commands(self): - req_state = self._required_config['state'] - if req_state == 'absent': - return self._generate_no_ospf_commands() - return self._generate_ospf_commands() - - -def main(): - """ main entry point for module execution - """ - OnyxOspfModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_pfc_interface.py b/plugins/modules/network/onyx/onyx_pfc_interface.py deleted file mode 100644 index 5b7adb62c3..0000000000 --- a/plugins/modules/network/onyx/onyx_pfc_interface.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_pfc_interface -author: "Samer Deeb (@samerd)" -short_description: Manage priority flow control on ONYX network devices -description: - - This module provides declarative management of priority flow control (PFC) - on interfaces of Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.4000 -options: - name: - description: - - Name of the interface PFC should be configured on. - aggregate: - description: List of interfaces PFC should be configured on. - purge: - description: - - Purge interfaces not defined in the aggregate parameter. - type: bool - default: false - state: - description: - - State of the PFC configuration. - default: enabled - choices: ['enabled', 'disabled'] -''' - -EXAMPLES = """ -- name: configure PFC - onyx_pfc_interface: - name: Eth1/1 - state: enabled -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface ethernet 1/17 dcb priority-flow-control mode on -""" -from copy import deepcopy -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible.module_utils.six import iteritems - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxPfcInterfaceModule(BaseOnyxModule): - PFC_IF_REGEX = re.compile( - r"^(Eth\d+\/\d+)|(Eth\d+\/\d+\/\d+)|(Po\d+)|(Mpo\d+)$") - - _purge = False - - @classmethod - def _get_element_spec(cls): - return dict( - name=dict(type='str'), - state=dict(default='enabled', - choices=['enabled', 'disabled']), - ) - - @classmethod - def _get_aggregate_spec(cls, element_spec): - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - return aggregate_spec - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - aggregate_spec = self._get_aggregate_spec(element_spec) - argument_spec = dict( - aggregate=dict(type='list', elements='dict', - options=aggregate_spec), - purge=dict(default=False, type='bool'), - ) - argument_spec.update(element_spec) - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - self._module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def get_required_config(self): - self._required_config = list() - module_params = self._module.params - aggregate = module_params.get('aggregate') - self._purge = module_params.get('purge', False) - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module_params[key] - self.validate_param_values(item, item) - req_item = item.copy() - self._required_config.append(req_item) - else: - params = { - 'name': module_params['name'], - 'state': module_params['state'], - } - self.validate_param_values(params) - self._required_config.append(params) - - def _create_if_pfc_data(self, if_name, if_pfc_data): - state = self.get_config_attr(if_pfc_data, "PFC oper") - state = state.lower() - return dict( - name=if_name, - state=state) - - def _get_pfc_config(self): - return show_cmd(self._module, "show dcb priority-flow-control") - - def load_current_config(self): - # called in base class in run function - self._os_version = self._get_os_version() - self._current_config = dict() - pfc_config = self._get_pfc_config() - if not pfc_config: - return - if self._os_version >= self.ONYX_API_VERSION: - if len(pfc_config) >= 3: - pfc_config = pfc_config[2] - else: - pfc_config = dict() - else: - if 'Table 2' in pfc_config: - pfc_config = pfc_config['Table 2'] - - for if_name, if_pfc_data in iteritems(pfc_config): - match = self.PFC_IF_REGEX.match(if_name) - if not match: - continue - if if_pfc_data: - if_pfc_data = if_pfc_data[0] - self._current_config[if_name] = \ - self._create_if_pfc_data(if_name, if_pfc_data) - - def _get_interface_cmd_name(self, if_name): - if if_name.startswith('Eth'): - return if_name.replace("Eth", "ethernet ") - if if_name.startswith('Po'): - return if_name.replace("Po", "port-channel ") - if if_name.startswith('Mpo'): - return if_name.replace("Mpo", "mlag-port-channel ") - self._module.fail_json( - msg='invalid interface name: %s' % if_name) - - def _add_if_pfc_commands(self, if_name, req_state): - cmd_prefix = "interface %s " % self._get_interface_cmd_name(if_name) - - if req_state == 'disabled': - pfc_cmd = 'no dcb priority-flow-control mode force' - else: - pfc_cmd = 'dcb priority-flow-control mode on force' - self._commands.append(cmd_prefix + pfc_cmd) - - def _gen_pfc_commands(self, if_name, curr_conf, req_state): - curr_state = curr_conf.get('state', 'disabled') - if curr_state != req_state: - self._add_if_pfc_commands(if_name, req_state) - - def generate_commands(self): - req_interfaces = set() - for req_conf in self._required_config: - req_state = req_conf['state'] - if_name = req_conf['name'] - if req_state == 'enabled': - req_interfaces.add(if_name) - curr_conf = self._current_config.get(if_name, {}) - self._gen_pfc_commands(if_name, curr_conf, req_state) - if self._purge: - for if_name, curr_conf in iteritems(self._current_config): - if if_name not in req_interfaces: - req_state = 'disabled' - self._gen_pfc_commands(if_name, curr_conf, req_state) - - -def main(): - """ main entry point for module execution - """ - OnyxPfcInterfaceModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_protocol.py b/plugins/modules/network/onyx/onyx_protocol.py deleted file mode 100644 index da1a18cb91..0000000000 --- a/plugins/modules/network/onyx/onyx_protocol.py +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_protocol -author: "Samer Deeb (@samerd)" -short_description: Enables/Disables protocols on Mellanox ONYX network devices -description: - - This module provides a mechanism for enabling and disabling protocols - Mellanox on ONYX network devices. -notes: - - Tested on ONYX 3.6.4000 -options: - mlag: - description: MLAG protocol - choices: ['enabled', 'disabled'] - magp: - description: MAGP protocol - choices: ['enabled', 'disabled'] - spanning_tree: - description: Spanning Tree support - choices: ['enabled', 'disabled'] - dcb_pfc: - description: DCB priority flow control - choices: ['enabled', 'disabled'] - igmp_snooping: - description: IP IGMP snooping - choices: ['enabled', 'disabled'] - lacp: - description: LACP protocol - choices: ['enabled', 'disabled'] - ip_l3: - description: IP L3 support - choices: ['enabled', 'disabled'] - ip_routing: - description: IP routing support - choices: ['enabled', 'disabled'] - lldp: - description: LLDP protocol - choices: ['enabled', 'disabled'] - bgp: - description: BGP protocol - choices: ['enabled', 'disabled'] - ospf: - description: OSPF protocol - choices: ['enabled', 'disabled'] - nve: - description: nve protocol - choices: ['enabled', 'disabled'] - bfd: - description: bfd protocol - choices: ['enabled', 'disabled'] -''' - -EXAMPLES = """ -- name: enable protocols for MLAG - onyx_protocol: - lacp: enabled - spanning_tree: disabled - ip_routing: enabled - mlag: enabled - dcb_pfc: enabled -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - no spanning-tree - - protocol mlag -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxProtocolModule(BaseOnyxModule): - - PROTOCOL_MAPPING = dict( - mlag=dict(name="mlag", enable="protocol mlag", - disable="no protocol mlag"), - magp=dict(name="magp", enable="protocol magp", - disable="no protocol magp"), - spanning_tree=dict(name="spanning-tree", enable="spanning-tree", - disable="no spanning-tree"), - dcb_pfc=dict(name="priority-flow-control", - enable="dcb priority-flow-control enable force", - disable="no dcb priority-flow-control enable force"), - igmp_snooping=dict(name="igmp-snooping", enable="ip igmp snooping", - disable="no ip igmp snooping"), - lacp=dict(name="lacp", enable="lacp", disable="no lacp"), - ip_l3=dict(name="IP L3", enable="ip l3", - disable="no ip l3"), - ip_routing=dict(name="IP routing", enable="ip routing", - disable="no ip routing"), - lldp=dict(name="lldp", enable="lldp", disable="no lldp"), - bgp=dict(name="bgp", enable="protocol bgp", disable="no protocol bgp"), - ospf=dict(name="ospf", enable="protocol ospf", - disable="no protocol ospf"), - nve=dict(name="nve", enable="protocol nve", - disable="no protocol nve"), - bfd=dict(name="bfd", enable="protocol bfd", - disable="no protocol bfd"), - ) - - @classmethod - def _get_element_spec(cls): - element_spec = dict() - for protocol in cls.PROTOCOL_MAPPING: - element_spec[protocol] = dict(choices=['enabled', 'disabled']) - return element_spec - - def init_module(self): - """ Ansible module initialization - """ - element_spec = self._get_element_spec() - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - - def get_required_config(self): - self._required_config = dict() - module_params = self._module.params - for key, val in iteritems(module_params): - if key in self.PROTOCOL_MAPPING and val is not None: - self._required_config[key] = val - - def _get_protocols(self): - return show_cmd(self._module, "show protocols") - - def _get_ip_routing(self): - return show_cmd(self._module, 'show ip routing | include "IP routing"', - json_fmt=False) - - def load_current_config(self): - self._current_config = dict() - protocols_config = self._get_protocols() - if not protocols_config: - protocols_config = dict() - ip_config = self._get_ip_routing() - if ip_config: - lines = ip_config.split('\n') - for line in lines: - line = line.strip() - line_attr = line.split(':') - if len(line_attr) == 2: - attr = line_attr[0].strip() - val = line_attr[1].strip() - protocols_config[attr] = val - for protocol, protocol_metadata in iteritems(self.PROTOCOL_MAPPING): - protocol_json_attr = protocol_metadata['name'] - val = protocols_config.get(protocol_json_attr, 'disabled') - if val not in ('enabled', 'disabled'): - val = 'enabled' - self._current_config[protocol] = val - - def generate_commands(self): - for protocol, req_val in iteritems(self._required_config): - protocol_metadata = self.PROTOCOL_MAPPING[protocol] - curr_val = self._current_config.get(protocol, 'disabled') - if curr_val != req_val: - if req_val == 'disabled': - command = protocol_metadata['disable'] - else: - command = protocol_metadata['enable'] - self._commands.append(command) - - -def main(): - """ main entry point for module execution - """ - OnyxProtocolModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_ptp_global.py b/plugins/modules/network/onyx/onyx_ptp_global.py deleted file mode 100644 index 3b41ab10e5..0000000000 --- a/plugins/modules/network/onyx/onyx_ptp_global.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_ptp_global -author: "Anas Badaha (@anasb)" -short_description: Configures PTP Global parameters -description: - - This module provides declarative management of PTP Global configuration - on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.8130 - ptp and ntp protocols cannot be enabled at the same time -options: - ptp_state: - description: - - PTP state. - choices: ['enabled', 'disabled'] - default: enabled - ntp_state: - description: - - NTP state. - choices: ['enabled', 'disabled'] - domain: - description: - - "set PTP domain number Range 0-127" - primary_priority: - description: - - "set PTP primary priority Range 0-225" - secondary_priority: - description: - - "set PTP secondary priority Range 0-225" -''' - -EXAMPLES = """ -- name: configure PTP - onyx_ptp_global: - ntp_state: enabled - ptp_state: disabled - domain: 127 - primary_priority: 128 - secondary_priority: 128 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - no ntp enable - - protocol ptp - - ptp domain 127 -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxPtpGlobalModule(BaseOnyxModule): - - def init_module(self): - """ initialize module - """ - element_spec = dict( - ntp_state=dict(choices=['enabled', 'disabled']), - ptp_state=dict(choices=['enabled', 'disabled'], default='enabled'), - domain=dict(type=int), - primary_priority=dict(type=int), - secondary_priority=dict(type=int) - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self._validate_param_values(self._required_config) - - def _validate_param_values(self, obj, param=None): - super(OnyxPtpGlobalModule, self).validate_param_values(obj, param) - if obj['ntp_state'] == 'enabled' and obj['ptp_state'] == 'enabled': - self._module.fail_json(msg='PTP State and NTP State Can not be enabled at the same time') - - def validate_domain(self, value): - if value and not 0 <= int(value) <= 127: - self._module.fail_json(msg='domain must be between 0 and 127') - - def validate_primary_priority(self, value): - if value and not 0 <= int(value) <= 255: - self._module.fail_json(msg='Primary Priority must be between 0 and 255') - - def validate_secondary_priority(self, value): - if value and not 0 <= int(value) <= 255: - self._module.fail_json(msg='Secondary Priority must be between 0 and 255') - - def _set_ntp_config(self, ntp_config): - ntp_config = ntp_config[0] - if not ntp_config: - return - ntp_state = ntp_config.get('NTP enabled') - if ntp_state == "yes": - self._current_config['ntp_state'] = "enabled" - else: - self._current_config['ntp_state'] = "disabled" - - def _set_ptp_config(self, ptp_config): - if ptp_config is None: - self._current_config['ptp_state'] = 'disabled' - else: - self._current_config['ptp_state'] = 'enabled' - self._current_config['domain'] = int(ptp_config['Domain']) - self._current_config['primary_priority'] = int(ptp_config['Priority1']) - self._current_config['secondary_priority'] = int(ptp_config['Priority2']) - - def _show_ntp_config(self): - cmd = "show ntp configured" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def _show_ptp_config(self): - cmd = "show ptp clock" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - - ntp_config = self._show_ntp_config() - self._set_ntp_config(ntp_config) - - ptp_config = self._show_ptp_config() - self._set_ptp_config(ptp_config) - - def generate_commands(self): - ntp_state = self._required_config.get("ntp_state") - if ntp_state == "enabled": - self._enable_ntp() - elif ntp_state == "disabled": - self._disable_ntp() - - ptp_state = self._required_config.get("ptp_state", "enabled") - if ptp_state == "enabled": - self._enable_ptp() - else: - self._disable_ptp() - - domain = self._required_config.get("domain") - if domain is not None: - curr_domain = self._current_config.get("domain") - if domain != curr_domain: - self._commands.append('ptp domain %d' % domain) - - primary_priority = self._required_config.get("primary_priority") - if primary_priority is not None: - curr_primary_priority = self._current_config.get("primary_priority") - if primary_priority != curr_primary_priority: - self._commands.append('ptp priority1 %d' % primary_priority) - - secondary_priority = self._required_config.get("secondary_priority") - if secondary_priority is not None: - curr_secondary_priority = self._current_config.get("secondary_priority") - if secondary_priority != curr_secondary_priority: - self._commands.append('ptp priority2 %d' % secondary_priority) - - def _enable_ptp(self): - curr_ptp_state = self._current_config['ptp_state'] - if curr_ptp_state == 'disabled': - self._commands.append('protocol ptp') - - def _disable_ptp(self): - curr_ptp_state = self._current_config['ptp_state'] - if curr_ptp_state == 'enabled': - self._commands.append('no protocol ptp') - - def _enable_ntp(self): - curr_ntp_state = self._current_config.get('ntp_state') - if curr_ntp_state == 'disabled': - self._commands.append('ntp enable') - - def _disable_ntp(self): - curr_ntp_state = self._current_config['ntp_state'] - if curr_ntp_state == 'enabled': - self._commands.append('no ntp enable') - - -def main(): - """ main entry point for module execution - """ - OnyxPtpGlobalModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_ptp_interface.py b/plugins/modules/network/onyx/onyx_ptp_interface.py deleted file mode 100644 index 6d207ae28a..0000000000 --- a/plugins/modules/network/onyx/onyx_ptp_interface.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_ptp_interface -author: 'Anas Badaha (@anasb)' -short_description: 'Configures PTP on interface' -description: - - "This module provides declarative management of PTP interfaces configuration -on Mellanox ONYX network devices." -notes: - - 'Tested on ONYX 3.6.8130' - - 'PTP Protocol must be enabled on switch.' - - 'Interface must not be a switch port interface.' -options: - name: - description: - - 'ethernet or vlan interface name that we want to configure PTP on it' - required: true - state: - description: - - 'Enable/Disable PTP on Interface' - default: enabled - choices: - - enabled - - disabled - delay_request: - description: - - 'configure PTP delay request interval, Range 0-5' - announce_interval: - description: - - 'configure PTP announce setting for interval, Range -3-1' - announce_timeout: - description: - - 'configure PTP announce setting for timeout, Range 2-10' - sync_interval: - description: - - 'configure PTP sync interval, Range -7--1' -''' - -EXAMPLES = """ -- name: configure PTP interface - onyx_ptp_interface: - state: enabled - name: Eth1/1 - delay_request: 0 - announce_interval: -2 - announce_timeout: 3 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface ethernet 1/16 ptp enable - - interface ethernet 1/16 ptp delay-req interval 0 - - interface ethernet 1/16 ptp announce interval -1 -""" - -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxPtpInterfaceModule(BaseOnyxModule): - IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|Eth\d+\/\d+\d+)$") - IF_VLAN_REGEX = re.compile(r"^Vlan (\d+)$") - - IF_TYPE_ETH = "ethernet" - IF_TYPE_VLAN = "vlan" - - IF_TYPE_MAP = { - IF_TYPE_ETH: IF_ETH_REGEX, - IF_TYPE_VLAN: IF_VLAN_REGEX - } - - RANGE_ATTR = { - "delay_request": (0, 5), - "announce_interval": (-3, -1), - "announce_timeout": (2, 10), - "sync_interval": (-7, -1) - } - - _interface_type = None - _interface_id = None - - def init_module(self): - """ initialize module - """ - element_spec = dict( - name=dict(required=True), - state=dict(choices=['enabled', 'disabled'], default='enabled'), - delay_request=dict(type=int), - announce_interval=dict(type=int), - announce_timeout=dict(type=int), - sync_interval=dict(type=int) - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - @classmethod - def _get_interface_type(cls, if_name): - if_type = None - if_id = None - for interface_type, interface_regex in iteritems(cls.IF_TYPE_MAP): - match = interface_regex.match(if_name) - if match: - if_type = interface_type - if_id = match.group(1) - break - return if_type, if_id - - def _set_if_type(self, module_params): - if_name = module_params['name'] - self._interface_type, self._interface_id = self._get_interface_type(if_name) - if not self._interface_id: - self._module.fail_json( - msg='unsupported interface name/type: %s' % if_name) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self._set_if_type(self._required_config) - self.validate_param_values(self._required_config) - - def _validate_attr_is_not_none(self, attr_name, attr_value): - if attr_value is not None: - self._module.fail_json(msg='Can not set %s value on switch while state is disabled' % attr_name) - - def validate_param_values(self, obj, param=None): - if obj['state'] == 'disabled': - for attr_name in self.RANGE_ATTR: - self._validate_attr_is_not_none(attr_name, obj[attr_name]) - super(OnyxPtpInterfaceModule, self).validate_param_values(obj, param) - - def _validate_range(self, value, attr_name): - min_value, max_value = self.RANGE_ATTR[attr_name] - if value and not min_value <= int(value) <= max_value: - self._module.fail_json(msg='%s value must be between %d and %d' % (attr_name, min_value, max_value)) - - def validate_delay_request(self, value): - self._validate_range(value, "delay_request") - - def validate_announce_interval(self, value): - self._validate_range(value, "announce_interval") - - def validate_announce_timeout(self, value): - self._validate_range(value, "announce_timeout") - - def validate_sync_interval(self, value): - self._validate_range(value, "sync_interval") - - def _set_ptp_interface_config(self, ptp_interface_config): - if ptp_interface_config is None: - self._current_config['state'] = 'disabled' - return - ptp_interface_config = ptp_interface_config[0] - self._current_config['state'] = 'enabled' - self._current_config['delay_request'] = int(ptp_interface_config['Delay request interval(log mean)']) - self._current_config['announce_interval'] = int(ptp_interface_config['Announce interval(log mean)']) - self._current_config['announce_timeout'] = int(ptp_interface_config['Announce receipt time out']) - self._current_config['sync_interval'] = int(ptp_interface_config['Sync interval(log mean)']) - - def _show_ptp_interface_config(self): - cmd = "show ptp interface %s %s" % (self._interface_type, self._interface_id) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - ptp_interface_config = self._show_ptp_interface_config() - self._set_ptp_interface_config(ptp_interface_config) - - def _generate_attr_command(self, attr_name, attr_cmd_name): - attr_val = self._required_config.get(attr_name) - if attr_val is not None: - curr_val = self._current_config.get(attr_name) - if attr_val != curr_val: - self._commands.append( - 'interface %s %s ptp %s %d' % (self._interface_type, self._interface_id, attr_cmd_name, attr_val)) - - def generate_commands(self): - state = self._required_config.get("state", "enabled") - self._gen_ptp_commands(state) - - self._generate_attr_command("delay_request", "delay-req interval") - self._generate_attr_command("announce_interval", "announce interval") - self._generate_attr_command("announce_timeout", "announce timeout") - self._generate_attr_command("sync_interval", "sync interval") - - def _add_if_ptp_cmd(self, req_state): - if req_state == 'enabled': - if_ptp_cmd = 'interface %s %s ptp enable' % (self._interface_type, self._interface_id) - else: - if_ptp_cmd = 'no interface %s %s ptp enable' % (self._interface_type, self._interface_id) - self._commands.append(if_ptp_cmd) - - def _gen_ptp_commands(self, req_state): - curr_state = self._current_config.get('state') - if curr_state != req_state: - self._add_if_ptp_cmd(req_state) - - -def main(): - """ main entry point for module execution - """ - OnyxPtpInterfaceModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_qos.py b/plugins/modules/network/onyx/onyx_qos.py deleted file mode 100644 index ee4efd04ac..0000000000 --- a/plugins/modules/network/onyx/onyx_qos.py +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_qos -author: "Anas Badaha (@anasb)" -short_description: Configures QoS -description: - - This module provides declarative management of Onyx QoS configuration - on Mellanox ONYX network devices. -notes: - - Tested on ONYX 3.6.8130 -options: - interfaces: - description: - - list of interfaces name. - required: true - trust: - description: - - trust type. - choices: ['L2', 'L3', 'both'] - default: L2 - rewrite_pcp: - description: - - rewrite with type pcp. - choices: ['enabled', 'disabled'] - default: disabled - rewrite_dscp: - description: - - rewrite with type dscp. - choices: ['enabled', 'disabled'] - default: disabled -''' - -EXAMPLES = """ -- name: configure QoS - onyx_QoS: - interfaces: - - Mpo7 - - Mpo7 - trust: L3 - rewrite_pcp: disabled - rewrite_dscp: enabled - -- name: configure QoS - onyx_QoS: - interfaces: - - Eth1/1 - - Eth1/2 - trust: both - rewrite_pcp: disabled - rewrite_dscp: enabled -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface ethernet 1/16 qos trust L3 - - interface mlag-port-channel 7 qos trust L3 - - interface port-channel 1 qos trust L3 - - interface mlag-port-channel 7 qos trust L2 - - interface mlag-port-channel 7 qos rewrite dscp - - interface ethernet 1/16 qos rewrite pcp - - interface ethernet 1/1 no qos rewrite pcp -""" - -import re -from ansible.module_utils.six import iteritems -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxQosModule(BaseOnyxModule): - TRUST_CMD = "interface {0} {1} qos trust {2}" - NO_REWRITE_PCP_CMD = "interface {0} {1} no qos rewrite pcp" - NO_REWRITE_DSCP_CMD = "interface {0} {1} no qos rewrite dscp" - REWRITE_PCP_CMD = "interface {0} {1} qos rewrite pcp" - REWRITE_DSCP_CMD = "interface {0} {1} qos rewrite dscp" - - REWRITE_PCP = "pcp" - REWRITE_DSCP = "dscp" - - IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|Eth\d+\/\d+\d+)$") - IF_PO_REGEX = re.compile(r"^Po(\d+)$") - MLAG_NAME_REGEX = re.compile(r"^Mpo(\d+)$") - - IF_TYPE_ETH = "ethernet" - PORT_CHANNEL = "port-channel" - MLAG_PORT_CHANNEL = "mlag-port-channel" - - IF_TYPE_MAP = { - IF_TYPE_ETH: IF_ETH_REGEX, - PORT_CHANNEL: IF_PO_REGEX, - MLAG_PORT_CHANNEL: MLAG_NAME_REGEX - } - - def init_module(self): - """ initialize module - """ - element_spec = dict( - interfaces=dict(type='list', required=True), - trust=dict(choices=['L2', 'L3', 'both'], default='L2'), - rewrite_pcp=dict(choices=['enabled', 'disabled'], default='disabled'), - rewrite_dscp=dict(choices=['enabled', 'disabled'], default='disabled') - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def _get_interface_type(self, if_name): - if_type = None - if_id = None - for interface_type, interface_regex in iteritems(self.IF_TYPE_MAP): - match = interface_regex.match(if_name) - if match: - if_type = interface_type - if_id = match.group(1) - break - return if_type, if_id - - def _set_interface_qos_config(self, interface_qos_config, interface, if_type, if_id): - interface_qos_config = interface_qos_config[0].get(interface) - trust = interface_qos_config[0].get("Trust mode") - rewrite_dscp = interface_qos_config[0].get("DSCP rewrite") - rewrite_pcp = interface_qos_config[0].get("PCP,DEI rewrite") - - self._current_config[interface] = dict(trust=trust, rewrite_dscp=rewrite_dscp, - rewrite_pcp=rewrite_pcp, if_type=if_type, if_id=if_id) - - def _show_interface_qos(self, if_type, interface): - cmd = "show qos interface {0} {1}".format(if_type, interface) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - for interface in self._required_config.get("interfaces"): - if_type, if_id = self._get_interface_type(interface) - if not if_id: - self._module.fail_json( - msg='unsupported interface: {0}'.format(interface)) - interface_qos_config = self._show_interface_qos(if_type, if_id) - if interface_qos_config is not None: - self._set_interface_qos_config(interface_qos_config, interface, if_type, if_id) - else: - self._module.fail_json( - msg='Interface {0} does not exist on switch'.format(interface)) - - def generate_commands(self): - trust = self._required_config.get("trust") - rewrite_pcp = self._required_config.get("rewrite_pcp") - rewrite_dscp = self._required_config.get("rewrite_dscp") - for interface in self._required_config.get("interfaces"): - ignored1, ignored2, current_trust, if_type, if_id = self._get_current_rewrite_config(interface) - self._add_interface_trust_cmds(if_type, if_id, interface, trust, current_trust) - self._add_interface_rewrite_cmds(if_type, if_id, interface, - rewrite_pcp, rewrite_dscp) - - def _get_current_rewrite_config(self, interface): - current_interface_qos_config = self._current_config.get(interface) - current_rewrite_pcp = current_interface_qos_config.get('rewrite_pcp') - current_rewrite_dscp = current_interface_qos_config.get('rewrite_dscp') - if_type = current_interface_qos_config.get("if_type") - if_id = current_interface_qos_config.get("if_id") - current_trust = current_interface_qos_config.get('trust') - - return current_rewrite_pcp, current_rewrite_dscp, current_trust, if_type, if_id - - def _add_interface_trust_cmds(self, if_type, if_id, interface, trust, current_trust): - - current_rewrite_pcp, current_rewrite_dscp, ignored1, ignored2, ignored3 = self._get_current_rewrite_config( - interface) - - if trust == "L3" and trust != current_trust: - self._add_no_rewrite_cmd(if_type, if_id, interface, self.REWRITE_DSCP, current_rewrite_dscp) - self._commands.append(self.TRUST_CMD.format(if_type, if_id, trust)) - elif trust == "L2" and trust != current_trust: - self._add_no_rewrite_cmd(if_type, if_id, interface, self.REWRITE_PCP, current_rewrite_pcp) - self._commands.append(self.TRUST_CMD.format(if_type, if_id, trust)) - elif trust == "both" and trust != current_trust: - self._add_no_rewrite_cmd(if_type, if_id, interface, self.REWRITE_DSCP, current_rewrite_dscp) - self._add_no_rewrite_cmd(if_type, if_id, interface, self.REWRITE_PCP, current_rewrite_pcp) - self._commands.append(self.TRUST_CMD.format(if_type, if_id, trust)) - - def _add_interface_rewrite_cmds(self, if_type, if_id, interface, rewrite_pcp, rewrite_dscp): - current_rewrite_pcp, current_rewrite_dscp, ignored1, ignored2, ignored3 = self._get_current_rewrite_config( - interface) - - if rewrite_pcp == "enabled" and rewrite_pcp != current_rewrite_pcp: - self._commands.append(self.REWRITE_PCP_CMD.format(if_type, if_id)) - elif rewrite_pcp == "disabled" and rewrite_pcp != current_rewrite_pcp: - self._commands.append(self.NO_REWRITE_PCP_CMD.format(if_type, if_id)) - - if rewrite_dscp == "enabled" and rewrite_dscp != current_rewrite_dscp: - self._commands.append(self.REWRITE_DSCP_CMD.format(if_type, if_id)) - elif rewrite_dscp == "disabled" and rewrite_dscp != current_rewrite_dscp: - self._commands.append(self.NO_REWRITE_DSCP_CMD.format(if_type, if_id)) - - def _add_no_rewrite_cmd(self, if_type, if_id, interface, rewrite_type, current_rewrite): - if rewrite_type == self.REWRITE_PCP and current_rewrite == "enabled": - self._commands.append(self.NO_REWRITE_PCP_CMD.format(if_type, if_id)) - self._current_config[interface]["rewrite_pcp"] = "disabled" - elif rewrite_type == self.REWRITE_DSCP and current_rewrite == "enabled": - self._commands.append(self.NO_REWRITE_DSCP_CMD.format(if_type, if_id)) - self._current_config[interface]["rewrite_dscp"] = "disabled" - - -def main(): - """ main entry point for module execution - """ - OnyxQosModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_snmp.py b/plugins/modules/network/onyx/onyx_snmp.py deleted file mode 100644 index 92019272cf..0000000000 --- a/plugins/modules/network/onyx/onyx_snmp.py +++ /dev/null @@ -1,426 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_snmp -author: "Sara-Touqan (@sarato)" -short_description: Manages SNMP general configurations on Mellanox ONYX network devices -description: - - This module provides declarative management of SNMP - on Mellanox ONYX network devices. -options: - state_enabled: - description: - - Enables/Disables the state of the SNMP configuration. - type: bool - contact_name: - description: - - Sets the SNMP contact name. - type: str - location: - description: - - Sets the SNMP location. - type: str - communities_enabled: - description: - - Enables/Disables community-based authentication on the system. - type: bool - multi_communities_enabled: - description: - - Enables/Disables multiple communities to be configured. - type: bool - snmp_communities: - type: list - description: - - List of snmp communities - suboptions: - community_name: - description: - - Configures snmp community name. - required: true - type: str - community_type: - description: - - Add this community as either a read-only or read-write community. - choices: ['read-only', 'read-write'] - type: str - state: - description: - - Used to decide if you want to delete the given snmp community or not - choices: ['present', 'absent'] - type: str - notify_enabled: - description: - - Enables/Disables sending of SNMP notifications (traps and informs) from thee system. - type: bool - notify_port: - description: - - Sets the default port to which notifications are sent. - type: str - notify_community: - description: - - Sets the default community for SNMP v1 and v2c notifications sent to hosts which do not have a community override set. - type: str - notify_send_test: - description: - - Sends a test notification. - type: str - choices: ['yes','no'] - notify_event: - description: - - Specifys which events will be sent as SNMP notifications. - type: str - choices: ['asic-chip-down', 'dcbx-pfc-port-oper-state-trap', 'insufficient-power', 'mstp-new-bridge-root', - 'ospf-lsdb-approaching-overflow', 'sm-stop', 'user-logout', 'cli-line-executed', 'dcbx-pfc-port-peer-state-trap', - 'interface-down', 'mstp-new-root-port', 'ospf-lsdb-overflow', 'snmp-authtrap', 'xstp-new-root-bridge', - 'cpu-util-high', 'disk-io-high', 'interface-up', 'mstp-topology-change', 'ospf-nbr-state-change', - 'temperature-too-high', 'xstp-root-port-change', 'dcbx-ets-module-state-change', 'disk-space-low', - 'internal-bus-error', 'netusage-high', 'paging-high', 'topology_change', 'xstp-topology-change', - 'dcbx-ets-port-admin-state-trap', 'entity-state-change', 'internal-link-speed-mismatch', 'new_root', - 'power-redundancy-mismatch', 'unexpected-cluster-join', 'dcbx-ets-port-oper-state-trap', 'expected-shutdown', - 'liveness-failure', 'ospf-auth-fail', 'process-crash', 'unexpected-cluster-leave', 'dcbx-ets-port-peer-state-trap', - 'health-module-status', 'low-power', 'ospf-config-error', 'process-exit', 'unexpected-cluster-size', - 'dcbx-pfc-module-state-change', 'insufficient-fans', 'low-power-recover', 'ospf-if-rx-bad-packet', - 'sm-restart', 'unexpected-shutdown', 'dcbx-pfc-port-admin-state-trap', 'insufficient-fans-recover', 'memusage-high', - 'ospf-if-state-change', 'sm-start', 'user-login'] - engine_id_reset: - description: - - Sets SNMPv3 engineID to node unique value. - type: bool - snmp_permissions: - type: list - description: - - Allow SNMPSET requests for items in a MIB. - suboptions: - state_enabled: - description: - - Enables/Disables the request. - required: true - type: bool - permission_type: - description: - - Configures the request type. - choices: ['MELLANOX-CONFIG-DB-MIB', 'MELLANOX-EFM-MIB','MELLANOX-POWER-CYCLE','MELLANOX-SW-UPDATE','RFC1213-MIB'] - type: str -''' - -EXAMPLES = """ -- name: configure SNMP - onyx_snmp: - state_enabled: yes - contact_name: sara - location: Nablus - communities_enabled: no - multi_communities_enabled: no - notify_enabled: yes - notify_port: 1 - notify_community: community_1 - notify_send_test: yes - notify_event: temperature-too-high - snmp_communities: - - community_name: public - community_type: read-only - state: absent - snmp_permissions: - - state_enabled: yes - permission_type: MELLANOX-CONFIG-DB-MIB -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - snmp-server enable - - no snmp-server enable - - snmp-server location - - snmp-server contact - - snmp-server enable communities - - no snmp-server enable communities - - snmp-server enable mult-communities - - no snmp-server enable mult-communities - - snmp-server enable notify - - snmp-server notify port - - snmp-server notify community - - snmp-server notify send-test - - snmp-server notify event - - snmp-server enable set-permission - - no snmp-server enable set-permission - - snmp-server community - - no snmp-server community . - - snmp-server engineID reset. -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxSNMPModule(BaseOnyxModule): - - def init_module(self): - """ module initialization - """ - - community_spec = dict(community_name=dict(required=True), - community_type=dict(choices=['read-only', 'read-write']), - state=dict(choices=['present', 'absent'])) - - snmp_permission_spec = dict(state_enabled=dict(type='bool', required=True), - permission_type=dict(choices=['MELLANOX-CONFIG-DB-MIB', 'MELLANOX-EFM-MIB', 'MELLANOX-POWER-CYCLE', - 'MELLANOX-SW-UPDATE', 'RFC1213-MIB'])) - - event_choices = ['asic-chip-down', 'dcbx-pfc-port-oper-state-trap', 'insufficient-power', 'mstp-new-bridge-root', - 'ospf-lsdb-approaching-overflow', 'sm-stop', 'user-logout', 'cli-line-executed', 'dcbx-pfc-port-peer-state-trap', - 'interface-down', 'mstp-new-root-port', 'ospf-lsdb-overflow', 'snmp-authtrap', 'xstp-new-root-bridge', - 'cpu-util-high', 'disk-io-high', 'interface-up', 'mstp-topology-change', 'ospf-nbr-state-change', - 'temperature-too-high', 'xstp-root-port-change', 'dcbx-ets-module-state-change', 'disk-space-low', - 'internal-bus-error', 'netusage-high', 'paging-high', 'topology_change', 'xstp-topology-change', - 'dcbx-ets-port-admin-state-trap', 'entity-state-change', 'internal-link-speed-mismatch', 'new_root', - 'power-redundancy-mismatch', 'unexpected-cluster-join', 'dcbx-ets-port-oper-state-trap', 'expected-shutdown', - 'liveness-failure', 'ospf-auth-fail', 'process-crash', 'unexpected-cluster-leave', 'dcbx-ets-port-peer-state-trap', - 'health-module-status', 'low-power', 'ospf-config-error', 'process-exit', 'unexpected-cluster-size', - 'dcbx-pfc-module-state-change', 'insufficient-fans', 'low-power-recover', 'ospf-if-rx-bad-packet', - 'sm-restart', 'unexpected-shutdown', 'dcbx-pfc-port-admin-state-trap', 'insufficient-fans-recover', 'memusage-high', - 'ospf-if-state-change', 'sm-start', 'user-login'] - element_spec = dict( - state_enabled=dict(type='bool'), - contact_name=dict(type='str'), - location=dict(type='str'), - communities_enabled=dict(type='bool'), - multi_communities_enabled=dict(type='bool'), - snmp_communities=dict(type='list', elements='dict', options=community_spec), - notify_enabled=dict(type='bool'), - notify_port=dict(type='str'), - notify_community=dict(type='str'), - notify_send_test=dict(type='str', choices=['yes', 'no']), - notify_event=dict(type='str', choices=event_choices), - engine_id_reset=dict(type='bool'), - snmp_permissions=dict(type='list', elements='dict', options=snmp_permission_spec) - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def _show_snmp_config(self): - show_cmds = [] - cmd = "show snmp" - show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)) - cmd = "show running-config | include snmp" - show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)) - return show_cmds - - def _set_snmp_config(self, all_snmp_config): - ro_communities_list = [] - rw_communities_list = [] - snmp_config = all_snmp_config[0] - if not snmp_config: - return - if snmp_config.get("SNMP enabled") == 'yes': - self._current_config['state_enabled'] = True - else: - self._current_config['state_enabled'] = False - self._current_config['contact_name'] = snmp_config.get("System contact") - self._current_config['location'] = snmp_config.get("System location") - curr_ro_comm = snmp_config.get("Read-only community") - if curr_ro_comm: - ro_arr = curr_ro_comm.split(' ') - rw_arr = snmp_config.get("Read-write community").split(' ') - ro_communities_list = ro_arr[0] - rw_communities_list = rw_arr[0] - if (len(ro_arr) == 2): - self._current_config['communities_enabled'] = False - else: - self._current_config['communities_enabled'] = True - else: - read_only_communities = all_snmp_config[1] - read_write_communities = all_snmp_config[2] - if not read_only_communities: - return - read_only_comm = read_only_communities.get("Read-only communities") - if read_only_comm: - self._current_config['communities_enabled'] = True - ro_communities_list = read_only_comm[0].get("Lines") - else: - self._current_config['communities_enabled'] = False - ro_comm_disabled = read_only_communities.get("Read-only communities (DISABLED)") - if ro_comm_disabled: - ro_communities_list = ro_comm_disabled[0].get("Lines") - if not read_write_communities: - return - read_write_comm = read_write_communities.get("Read-write communities") - if read_write_comm: - self._current_config['communities_enabled'] = True - rw_communities_list = read_write_comm[0].get("Lines") - else: - self._current_config['communities_enabled'] = False - rw_comm_disabled = read_write_communities.get("Read-write communities (DISABLED)") - if rw_comm_disabled: - rw_communities_list = rw_comm_disabled[0].get("Lines") - self._current_config['ro_communities_list'] = ro_communities_list - self._current_config['rw_communities_list'] = rw_communities_list - - def _set_snmp_running_config(self, snmp_running_config): - self._current_config['multi_comm_enabled'] = True - self._current_config['notify_enabled'] = True - curr_config_arr = [] - snmp_lines = snmp_running_config.get('Lines') - for runn_config in snmp_lines: - curr_config_arr.append(runn_config.strip()) - if 'no snmp-server enable mult-communities' in snmp_lines: - self._current_config['multi_comm_enabled'] = False - if 'no snmp-server enable notify' in snmp_lines: - self._current_config['notify_enabled'] = False - self._current_config['snmp_running_config'] = curr_config_arr - - def load_current_config(self): - self._current_config = dict() - snmp_config = self._show_snmp_config() - if snmp_config[0]: - self._set_snmp_config(snmp_config[0]) - if snmp_config[1]: - self._set_snmp_running_config(snmp_config[1]) - - def generate_commands(self): - current_state = self._current_config.get("state_enabled") - state = current_state - req_state = self._required_config.get("state_enabled") - if req_state is not None: - state = req_state - if state is not None: - if current_state != state: - if state is True: - self._commands.append('snmp-server enable') - else: - self._commands.append('no snmp-server enable') - - contact_name = self._required_config.get("contact_name") - if contact_name: - current_contact_name = self._current_config.get("contact_name") - if contact_name is not None: - if current_contact_name != contact_name: - self._commands.append('snmp-server contact {0}' .format(contact_name)) - - location = self._required_config.get("location") - if location: - current_location = self._current_config.get("location") - if location is not None: - if current_location != location: - self._commands.append('snmp-server location {0}' .format(location)) - - communities_enabled = self._required_config.get("communities_enabled") - if communities_enabled is not None: - current_communities_enabled = self._current_config.get("communities_enabled") - if communities_enabled is not None: - if current_communities_enabled != communities_enabled: - if communities_enabled is True: - self._commands.append('snmp-server enable communities') - else: - self._commands.append('no snmp-server enable communities') - - ro_communities = self._current_config.get("ro_communities_list") - rw_communities = self._current_config.get("rw_communities_list") - snmp_communities = self._required_config.get("snmp_communities") - if snmp_communities: - if snmp_communities is not None: - for community in snmp_communities: - community_name = community.get("community_name") - state = community.get("state") - if state: - if state == 'absent': - self._commands.append('no snmp-server community {0}' .format(community_name)) - continue - community_type = community.get("community_type") - if community_type: - if community_type == 'read-only': - if community_name not in ro_communities: - self._commands.append('snmp-server community {0} ro' .format(community_name)) - else: - if community_name not in rw_communities: - self._commands.append('snmp-server community {0} rw' .format(community_name)) - else: - if community_name not in ro_communities: - self._commands.append('snmp-server community {0}' .format(community_name)) - - engine_id_reset = self._required_config.get("engine_id_reset") - if engine_id_reset is not None: - if engine_id_reset: - self._commands.append('snmp-server engineID reset') - - current_multi_comm_state = self._current_config.get("multi_comm_enabled") - multi_communities_enabled = self._required_config.get("multi_communities_enabled") - if multi_communities_enabled is not None: - if current_multi_comm_state != multi_communities_enabled: - if multi_communities_enabled is True: - self._commands.append('snmp-server enable mult-communities') - else: - self._commands.append('no snmp-server enable mult-communities') - - notify_enabled = self._required_config.get("notify_enabled") - if notify_enabled is not None: - current_notify_state = self._current_config.get("notify_enabled") - if current_notify_state != notify_enabled: - if notify_enabled is True: - self._commands.append('snmp-server enable notify') - else: - self._commands.append('no snmp-server enable notify') - - snmp_permissions = self._required_config.get("snmp_permissions") - if snmp_permissions is not None: - for permission in snmp_permissions: - permission_type = permission.get('permission_type') - if permission.get('state_enabled') is True: - self._commands.append('snmp-server enable set-permission {0}' .format(permission_type)) - else: - self._commands.append('no snmp-server enable set-permission {0}' .format(permission_type)) - - snmp_running_config = self._current_config.get("snmp_running_config") - notify_port = self._required_config.get("notify_port") - if notify_port is not None: - notified_cmd = 'snmp-server notify port {0}' .format(notify_port) - if notified_cmd not in snmp_running_config: - self._commands.append('snmp-server notify port {0}' .format(notify_port)) - - notify_community = self._required_config.get("notify_community") - if notify_community is not None: - notified_cmd = 'snmp-server notify community {0}' .format(notify_community) - if notified_cmd not in snmp_running_config: - self._commands.append('snmp-server notify community {0}' .format(notify_community)) - - notify_send_test = self._required_config.get("notify_send_test") - if notify_send_test is not None: - if notify_send_test == 'yes': - self._commands.append('snmp-server notify send-test') - - notify_event = self._required_config.get("notify_event") - if notify_event is not None: - self._commands.append('snmp-server notify event {0}' .format(notify_event)) - - -def main(): - """ main entry point for module execution - """ - OnyxSNMPModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_snmp_hosts.py b/plugins/modules/network/onyx/onyx_snmp_hosts.py deleted file mode 100644 index 81e49b2525..0000000000 --- a/plugins/modules/network/onyx/onyx_snmp_hosts.py +++ /dev/null @@ -1,424 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_snmp_hosts -author: "Sara Touqan (@sarato)" -short_description: Configures SNMP host parameters -description: - - This module provides declarative management of SNMP hosts protocol params - on Mellanox ONYX network devices. -options: - hosts: - type: list - description: - - List of snmp hosts - suboptions: - name: - description: - - Specifies the name of the host. - required: true - type: str - enabled: - description: - - Temporarily Enables/Disables sending of all notifications to this host. - type: bool - notification_type: - description: - - Configures the type of sending notification to the specified host. - choices: ['trap', 'inform'] - type: str - port: - description: - - Overrides default target port for this host. - type: str - version: - description: - - Specifys SNMP version of informs to send. - choices: ['1', '2c', '3'] - type: str - user_name: - description: - - Specifys username for this inform sink. - type: str - auth_type: - description: - - Configures SNMP v3 security parameters, specifying passwords in a nother parameter (auth_password) (passwords are always stored encrypted). - choices: ['md5', 'sha', 'sha224', 'sha256', 'sha384', 'sha512'] - type: str - auth_password: - description: - - The password needed to configure the auth type. - type: str - privacy_type: - description: - - Specifys SNMP v3 privacy settings for this user. - choices: ['3des', 'aes-128', 'aes-192', 'aes-192-cfb', 'aes-256', 'aes-256-cfb', 'des'] - type: str - privacy_password: - description: - - The password needed to configure the privacy type. - type: str - state: - description: - - Used to decide if you want to delete the specified host or not. - choices: ['present' , 'absent'] - type: str -''' - -EXAMPLES = """ -- name: enables snmp host - onyx_snmp_hosts: - hosts: - - name: 1.1.1.1 - enabled: true - -- name: configures snmp host with version 2c - onyx_snmp_hosts: - hosts: - - name: 2.3.2.4 - enabled: true - notification_type: trap - port: 66 - version: 2c - -- name: configures snmp host with version 3 and configures it with user as sara - onyx_snmp_hosts: - hosts: - - name: 2.3.2.4 - enabled: true - notification_type: trap - port: 66 - version: 3 - user_name: sara - auth_type: sha - auth_password: jnbdfijbdsf - privacy_type: 3des - privacy_password: nojfd8uherwiugfh - -- name: deletes the snmp host - onyx_snmp_hosts: - hosts: - - name: 2.3.2.4 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - snmp-server host disable - - no snmp-server host disable - - snmp-server host informs port version - - snmp-server host traps port version - - snmp-server host informs port version user auth - priv - - snmp-server host traps port version user auth - priv - - no snmp-server host . -""" - -import re - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxSNMPHostsModule(BaseOnyxModule): - - def init_module(self): - """ initialize module - """ - host_spec = dict(name=dict(required=True), - enabled=dict(type='bool'), - notification_type=dict(type='str', choices=['trap', 'inform']), - port=dict(type='str'), - version=dict(type='str', choices=['1', '2c', '3']), - user_name=dict(type='str'), - auth_type=dict(type='str', choices=['md5', 'sha', 'sha224', 'sha256', 'sha384', 'sha512']), - privacy_type=dict(type='str', choices=['3des', 'aes-128', 'aes-192', 'aes-192-cfb', 'aes-256', 'aes-256-cfb', 'des']), - privacy_password=dict(type='str', no_log=True), - auth_password=dict(type='str', no_log=True), - state=dict(type='str', choices=['present', 'absent']) - ) - element_spec = dict( - hosts=dict(type='list', elements='dict', options=host_spec), - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def validate_snmp_required_params(self): - req_hosts = self._required_config.get("hosts") - if req_hosts: - for host in req_hosts: - version = host.get('version') - if version: - if version == '3': - if host.get('user_name') is None or host.get('auth_type') is None or host.get('auth_password') is None: - self._module.fail_json(msg='user_name, auth_type and auth_password are required when version number is 3.') - - if host.get('notification_type') is not None: - if host.get('version') is None or host.get('port') is None: - self._module.fail_json(msg='port and version are required when notification_type is provided.') - - if host.get('auth_type') is not None: - if host.get('auth_password') is None: - self._module.fail_json(msg='auth_password is required when auth_type is provided.') - - if host.get('privacy_type') is not None: - if host.get('privacy_password') is None: - self._module.fail_json(msg='privacy_password is required when privacy_type is provided.') - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - self.validate_snmp_required_params() - - def _set_host_config(self, hosts_config): - hosts = hosts_config.get('Notification sinks') - if hosts[0].get('Lines'): - self._current_config['current_hosts'] = dict() - self._current_config['host_names'] = [] - return - - current_hosts = dict() - host_names = [] - for host in hosts: - host_info = dict() - for host_name in host: - host_names.append(host_name) - enabled = True - first_entry = host.get(host_name)[0] - if first_entry: - if first_entry.get('Enabled') == 'no': - enabled = False - notification_type = first_entry.get('Notification type') - notification_type = notification_type.split() - host_info['notification_type'] = notification_type[2] - version = notification_type[1][1:] - host_info['port'] = first_entry.get('Port') - host_info['name'] = host_name - host_info['enabled'] = enabled - host_info['version'] = version - if first_entry.get('Community') is None: - if len(first_entry) == 8: - host_info['user_name'] = first_entry.get('Username') - host_info['auth_type'] = first_entry.get('Authentication type') - host_info['privacy_type'] = first_entry.get('Privacy type') - elif len(host.get(host_name)) == 2: - second_entry = host.get(host_name)[1] - host_info['user_name'] = second_entry.get('Username') - host_info['auth_type'] = second_entry.get('Authentication type') - host_info['privacy_type'] = second_entry.get('Privacy type') - else: - host_info['user_name'] = '' - host_info['auth_type'] = '' - host_info['privacy_type'] = '' - else: - host_info['user_name'] = '' - host_info['auth_type'] = '' - host_info['privacy_type'] = '' - current_hosts[host_name] = host_info - self._current_config['current_hosts'] = current_hosts - self._current_config['host_names'] = host_names - - def _show_hosts_config(self): - cmd = "show snmp host" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - hosts_config = self._show_hosts_config() - if hosts_config[1]: - self._set_host_config(hosts_config[1]) - - def generate_snmp_commands_with_current_config(self, host): - host_id = host.get('name') - host_notification_type = host.get('notification_type') - host_enabled = host.get("enabled") - host_port = host.get('port') - host_version = host.get('version') - host_username = host.get('user_name') - host_auth_type = host.get('auth_type') - host_auth_pass = host.get('auth_password') - host_priv_type = host.get('privacy_type') - host_priv_pass = host.get('privacy_password') - present_state = host.get('state') - current_hosts = self._current_config.get("current_hosts") - current_entry = current_hosts.get(host_id) - if present_state is not None: - if present_state == 'absent': - self._commands.append('no snmp-server host {0}' .format(host_id)) - return - if host_enabled is not None: - if current_entry.get('enabled') != host_enabled: - if host_enabled is True: - self._commands.append('no snmp-server host {0} disable' .format(host_id)) - else: - self._commands.append('snmp-server host {0} disable' .format(host_id)) - if host_notification_type is not None: - current_port = current_entry.get('port') - current_version = current_entry.get('version') - current_priv_type = current_entry.get('privacy_type') - current_username = current_entry.get('user_name') - current_auth_type = current_entry.get('auth_type') - current_noti_type = current_entry.get('notification_type') - if host_port is not None: - if host_version is not None: - if host_version == '3': - if (host_priv_type is not None and host_priv_pass is not None): - if((current_noti_type != host_notification_type) or - ((current_port != host_port)) or - (current_version != host_version) or - (current_priv_type != host_priv_type) or - (current_username != host_username) or - (current_auth_type != host_auth_type)): - self._commands.append('snmp-server host {0} {1}s port {2} version {3} user {4} auth {5} {6} priv {7} {8}' - .format(host_id, host_notification_type, host_port, - host_version, host_username, host_auth_type, host_auth_pass, - host_priv_type, host_priv_pass)) - else: - if((current_noti_type != host_notification_type) or - ((current_port != host_port)) or - (current_version != host_version) or - (current_username != host_username) or - (current_auth_type != host_auth_type)): - self._commands.append('snmp-server host {0} {1}s port {2} version {3} user {4} auth {5} {6}' - .format(host_id, host_notification_type, - host_port, host_version, host_username, - host_auth_type, host_auth_pass)) - else: - if((current_noti_type != host_notification_type) or - ((current_port != host_port)) or - (current_version != host_version)): - self._commands.append('snmp-server host {0} {1}s port {2} version {3}' - .format(host_id, host_notification_type, - host_port, host_version)) - else: - if ((current_noti_type != host_notification_type) or - ((current_port != host_port))): - self._commands.append('snmp-server host {0} {1}s port {2}' - .format(host_id, host_notification_type, host_port)) - else: - if host_version is not None: - if host_version == '3': - if (host_priv_type is not None and host_priv_pass is not None): - if ((current_noti_type != host_notification_type) or - ((current_version != host_version)) or - (current_username != host_username) or - ((current_auth_type != host_auth_type)) or - (current_priv_type != host_priv_type)): - self._commands.append('snmp-server host {0} {1}s version {2} user {3} auth {4} {5} priv {6} {7}' - .format(host_id, host_notification_type, host_version, host_username, - host_auth_type, host_auth_pass, host_priv_type, host_priv_pass)) - - else: - if ((current_noti_type != host_notification_type) or - ((current_version != host_version)) or - (current_username != host_username) or - ((current_auth_type != host_auth_type))): - self._commands.append('snmp-server host {0} {1}s version {2} user {3} auth {4} {5}' - .format(host_id, host_notification_type, - host_version, host_username, host_auth_type, host_auth_pass)) - - else: - if ((current_noti_type != host_notification_type) or - ((current_version != host_version))): - self._commands.append('snmp-server host {0} {1}s version {2}' .format(host_id, - host_notification_type, host_version)) - - def generate_snmp_commands_without_current_config(self, host): - host_id = host.get('name') - host_notification_type = host.get('notification_type') - host_enabled = host.get("enabled") - host_port = host.get('port') - host_version = host.get('version') - host_username = host.get('user_name') - host_auth_type = host.get('auth_type') - host_auth_pass = host.get('auth_password') - host_priv_type = host.get('privacy_type') - host_priv_pass = host.get('privacy_password') - present_state = host.get('state') - present_state = host.get('state') - if present_state is not None: - if present_state == 'absent': - return - if host_enabled is not None: - if host_enabled is True: - self._commands.append('no snmp-server host {0} disable' .format(host_id)) - else: - self._commands.append('snmp-server host {0} disable' .format(host_id)) - - if host_notification_type is not None: - if host_port is not None: - if host_version is not None: - if host_version == '3': - if (host_priv_type is not None and host_priv_pass is not None): - self._commands.append('snmp-server host {0} {1}s port {2} version {3} user {4} auth {5} {6} priv {7} {8}' - .format(host_id, host_notification_type, host_port, host_version, host_username, - host_auth_type, host_auth_pass, host_priv_type, host_priv_pass)) - else: - self._commands.append('snmp-server host {0} {1}s port {2} version {3} user {4} auth {5} {6}' - .format(host_id, host_notification_type, host_port, host_version, host_username, - host_auth_type, host_auth_pass)) - else: - self._commands.append('snmp-server host {0} {1}s port {2} version {3}' .format(host_id, - host_notification_type, host_port, host_version)) - else: - self._commands.append('snmp-server host {0} {1}s port {2}' .format(host_id, - host_notification_type, host_port)) - else: - if host_version is not None: - if host_version == '3': - if (host_priv_type is not None and host_priv_pass is not None): - self._commands.append('snmp-server host {0} {1}s version {2} user {3} auth {4} {5} priv {6} {7}' - .format(host_id, host_notification_type, host_version, host_username, - host_auth_type, host_auth_pass, host_priv_type, host_priv_pass)) - else: - self._commands.append('snmp-server host {0} {1}s version {2} user {3} auth {4} {5}' .format(host_id, - host_notification_type, host_version, host_username, - host_auth_type, host_auth_pass)) - else: - self._commands.append('snmp-server host {0} {1}s version {2}' .format(host_id, - host_notification_type, host_version)) - - def generate_commands(self): - req_hosts = self._required_config.get("hosts") - host_names = self._current_config.get("host_names") - - if req_hosts: - for host in req_hosts: - host_id = host.get('name') - if host_id: - if host_names and (host_id in host_names): - self.generate_snmp_commands_with_current_config(host) - else: - self.generate_snmp_commands_without_current_config(host) - - -def main(): - """ main entry point for module execution - """ - OnyxSNMPHostsModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_snmp_users.py b/plugins/modules/network/onyx/onyx_snmp_users.py deleted file mode 100644 index c4c0c19b6f..0000000000 --- a/plugins/modules/network/onyx/onyx_snmp_users.py +++ /dev/null @@ -1,277 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_snmp_users -author: "Sara Touqan (@sarato)" -short_description: Configures SNMP User parameters -description: - - This module provides declarative management of SNMP Users protocol params - on Mellanox ONYX network devices. -options: - users: - type: list - description: - - List of snmp users - suboptions: - name: - description: - - Specifies the name of the user. - required: true - type: str - enabled: - description: - - Enables/Disables SNMP v3 access for the user. - type: bool - set_access_enabled: - description: - - Enables/Disables SNMP SET requests for the user. - type: bool - require_privacy: - description: - - Enables/Disables the Require privacy (encryption) for requests from this user - type: bool - auth_type: - description: - - Configures the hash type used to configure SNMP v3 security parameters. - choices: ['md5', 'sha', 'sha224', 'sha256', 'sha384', 'sha512'] - type: str - auth_password: - description: - - The password needed to configure the hash type. - type: str - capability_level: - description: - - Sets capability level for SET requests. - choices: ['admin','monitor','unpriv','v_admin'] - type: str -''' - -EXAMPLES = """ -- name: enables snmp user - onyx_snmp_users: - users: - - name: sara - enabled: true - -- name: enables snmp set requests - onyx_snmp_users: - users: - - name: sara - set_access_enabled: yes - -- name: enables user require privacy - onyx_snmp_users: - users: - - name: sara - require_privacy: true - -- name: configures user hash type - onyx_snmp_users: - users: - - auth_type: md5 - auth_password: 1297sara1234sara - -- name: configures user capability_level - onyx_snmp_users: - users: - - name: sara - capability_level: admin -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - snmp-server user v3 enable - - no snmp-server user v3 enable - - snmp-server user v3 enable sets - - no snmp-server user v3 enable sets - - snmp-server user v3 require-privacy - - no snmp-server user v3 require-privacy - - snmp-server user v3 capability - - snmp-server user v3 auth -""" - -import re - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxSNMPUsersModule(BaseOnyxModule): - - def init_module(self): - """ initialize module - """ - user_spec = dict(name=dict(required=True), - enabled=dict(type='bool'), - set_access_enabled=dict(type='bool'), - require_privacy=dict(type='bool'), - auth_type=dict(type='str', choices=['md5', 'sha', 'sha224', 'sha256', 'sha384', 'sha512']), - auth_password=dict(type='str'), - capability_level=dict(type='str', choices=['admin', 'monitor', 'unpriv', 'v_admin']), - ) - element_spec = dict( - users=dict(type='list', elements='dict', options=user_spec), - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def _set_snmp_config(self, users_config): - if users_config[0]: - if users_config[0].get('Lines'): - return - current_users = [] - count = 0 - enabled = True - set_access_enabled = True - require_privacy = True - auth_type = '' - capability_level = '' - name = '' - all_users_names = [] - for user in users_config: - user_dict = {} - entry_dict = {} - for entry in user: - name = entry.split()[2] - if user.get(entry): - if user.get(entry)[0]: - enabled = user.get(entry)[0].get('Enabled overall') - if enabled == 'no': - enabled = False - else: - enabled = True - set_access_enabled = user.get(entry)[1].get('SET access')[0].get('Enabled') - if set_access_enabled == 'no': - set_access_enabled = False - else: - set_access_enabled = True - require_privacy = user.get(entry)[0].get('Require privacy') - if require_privacy == 'yes': - require_privacy = True - else: - require_privacy = False - capability_level = user.get(entry)[1].get('SET access')[0].get('Capability level') - auth_type = user.get(entry)[0].get('Authentication type') - user_dict['enabled'] = enabled - user_dict['set_access_enabled'] = set_access_enabled - user_dict['auth_type'] = auth_type - user_dict['capability_level'] = capability_level - user_dict['require_privacy'] = require_privacy - entry_dict[name] = user_dict - all_users_names.append(name) - current_users.append(entry_dict) - self._current_config['users'] = current_users - self._current_config['current_names'] = all_users_names - - def _show_users(self): - cmd = "show snmp user" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - users_config = self._show_users() - if users_config: - self._set_snmp_config(users_config) - - def generate_commands(self): - req_uers = self._required_config.get("users") - current_users = self._current_config.get("users") - current_names = self._current_config.get("current_names") - if req_uers: - for user in req_uers: - user_id = user.get('name') - if user_id: - if current_names and (user_id in current_names): - for user_entry in current_users: - for user_name in user_entry: - if user_name == user_id: - user_state = user.get("enabled") - user_entry_name = user_entry.get(user_name) - if user_state is not None: - if user_state != user_entry_name.get("enabled"): - if user_state is True: - self._commands.append('snmp-server user {0} v3 enable' .format(user_id)) - else: - self._commands.append('no snmp-server user {0} v3 enable' .format(user_id)) - set_state = user.get("set_access_enabled") - if set_state is not None: - if set_state != user_entry_name.get("set_access_enabled"): - if set_state is True: - self._commands.append('snmp-server user {0} v3 enable sets' .format(user_id)) - else: - self._commands.append('no snmp-server user {0} v3 enable sets' .format(user_id)) - auth_type = user.get("auth_type") - if auth_type is not None: - if user.get("auth_password") is not None: - if auth_type != user_entry_name.get("auth_type"): - self._commands.append('snmp-server user {0} v3 auth {1} {2}' - .format(user_id, user.get('auth_type'), user.get('auth_password'))) - cap_level = user.get("capability_level") - if cap_level is not None: - if cap_level != user_entry_name.get("capability_level"): - self._commands.append('snmp-server user {0} v3 capability {1}' - .format(user_id, user.get('capability_level'))) - req_priv = user.get("require_privacy") - if req_priv is not None: - if req_priv != user_entry_name.get("require_privacy"): - if req_priv is True: - self._commands.append('snmp-server user {0} v3 require-privacy' .format(user_id)) - else: - self._commands.append('no snmp-server user {0} v3 require-privacy' .format(user_id)) - - else: - user_state = user.get("enabled") - if user_state is not None: - if user_state is True: - self._commands.append('snmp-server user {0} v3 enable' .format(user_id)) - else: - self._commands.append('no snmp-server user {0} v3 enable' .format(user_id)) - set_state = user.get("set_access_enabled") - if set_state is not None: - if set_state is True: - self._commands.append('snmp-server user {0} v3 enable sets' .format(user_id)) - else: - self._commands.append('no snmp-server user {0} v3 enable sets' .format(user_id)) - if user.get("capability_level") is not None: - self._commands.append('snmp-server user {0} v3 capability {1}' .format(user_id, user.get('capability_level'))) - req_priv = user.get("require_privacy") - if req_priv is not None: - if req_priv is True: - self._commands.append('snmp-server user {0} v3 require-privacy' .format(user_id)) - else: - self._commands.append('no snmp-server user {0} v3 require-privacy' .format(user_id)) - - -def main(): - """ main entry point for module execution - """ - OnyxSNMPUsersModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_syslog_files.py b/plugins/modules/network/onyx/onyx_syslog_files.py deleted file mode 100644 index 2cc974a2c2..0000000000 --- a/plugins/modules/network/onyx/onyx_syslog_files.py +++ /dev/null @@ -1,251 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' -module: onyx_syslog_files -author: "Anas Shami (@anass)" -short_description: Configure file management syslog module -description: - - This module provides declarative management of syslog - on Mellanox ONYX network devices. -notes: -options: - debug: - description: - - Configure settings for debug log files - type: bool - default: False - delete_group: - description: - - Delete certain log files - choices: ['current', 'oldest'] - type: str - rotation: - description: - - rotation related attributes - type: dict - suboptions: - frequency: - description: - - Rotate log files on a fixed time-based schedule - choices: ['daily', 'weekly', 'monthly'] - type: str - force: - description: - - force an immediate rotation of log files - type: bool - max_num: - description: - - Sepcify max_num of old log files to keep - type: int - size: - description: - - Rotate files when they pass max size - type: float - size_pct: - description: - - Rotatoe files when they pass percent of HD - type: float - upload_url: - description: - - upload local log files to remote host (ftp, scp, sftp, tftp) with format protocol://username[:password]@server/path - type: str - upload_file: - description: - - Upload compressed log file (current or filename) - type: str -''' - -EXAMPLES = """ -- name: syslog delete old files -- onyx_syslog_files: - delete_group: oldest -- name: syslog upload file -- onyx_syslog_files: - upload_url: scp://username:password@hostnamepath/filename - upload_file: current -- name: syslog rotation force, frequency and max number -- onyx_syslog_files: - rotation: - force: true - max_num: 30 - frequency: daily - size: 128 -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - logging files delete current - - logging files rotate criteria - - logging files upload current url -""" -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxSyslogFilesModule(BaseOnyxModule): - MAX_FILES = 999999 - URL_REGEX = re.compile( - r'^(ftp|scp|ftps):\/\/[a-z0-9\.]*:(.*)@(.*):([a-zA-Z\/\/])*$') - FREQUANCIES = ['daily', 'weekly', 'monthly'] - ROTATION_KEYS = ['frequency', 'max_num', 'size', 'size_pct', 'force'] - ROTATION_CMDS = {'size': 'logging {0} rotation criteria size {1}', - 'frequency': 'logging {0} rotation criteria frequency {1}', - 'max_num': 'logging {0} rotation max-num {1}', - 'size_pct': 'logging {0} rotation criteria size-pct {1}', - 'force': 'logging {0} rotation force'} - - def init_module(self): - """" Ansible module initialization - """ - rotation_spec = dict(frequency=dict(choices=self.FREQUANCIES), - max_num=dict(type="int"), - force=dict(type="bool"), - size=dict(type="float"), - size_pct=dict(type="float")) - - element_spec = dict(delete_group=dict(choices=['oldest', 'current']), - rotation=dict(type="dict", options=rotation_spec), - upload_file=dict(type="str"), - upload_url=dict(type="str"), - debug=dict(type="bool", default=False)) - - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_together=[['upload_file', 'upload_url']]) - - def validate_rotation(self, rotation): - size_pct = rotation.get('size_pct', None) - max_num = rotation.get('max_num', None) - if size_pct is not None and (float(size_pct) < 0 or float(size_pct) > 100): - self._module.fail_json( - msg='logging size_pct must be in range 0-100') - elif max_num is not None and (int(max_num) < 0 or int(max_num) > self.MAX_FILES): - self._module.fail_json( - msg='logging max_num must be positive number less than {0}'.format(self.MAX_FILES)) - - def validate_upload_url(self, upload_url): - check = self.URL_REGEX.match(upload_url) - if upload_url and not check: - self._module.fail_json( - msg='Invalid url, make sure that you use "[ftp, scp, tftp, sftp]://username:password@hostname:/location" format') - - def show_logging(self): - show_logging = show_cmd(self._module, "show logging", json_fmt=True, fail_on_error=False) - running_config = show_cmd(self._module, "show running-config | include .*logging.*debug-files.*", json_fmt=True, fail_on_error=False) - - if len(show_logging) > 0: - show_logging[0]['debug'] = running_config['Lines'] if 'Lines' in running_config else [] - else: - show_logging = [{ - 'debug': running_config['Lines'] if 'Lines' in running_config else [] - }] - return show_logging - - def load_current_config(self): - self._current_config = dict() - current_config = self.show_logging()[0] - freq = current_config.get('Log rotation frequency') # daily (Once per day at midnight) - size = current_config.get('Log rotation size threshold') # 19.07 megabytes or 10.000% of partition (987.84 megabytes) - max_num = current_config.get('Number of archived log files to keep') - if freq is not None: - freq_str = freq.split()[0] - self._current_config['frequency'] = freq_str - - if size is not None: - size_arr = size.split(' ') - if '%' in size: - size_pct_value = size_arr[0].replace('%', '') - self._current_config['size_pct'] = float(size_pct_value) - size_value = re.sub(r'(\(|\)|megabytes)', '', size_arr[-2]).strip() - self._current_config['size'] = float(size_value) - else: - size_value = size_arr[0] - self._current_config['size'] = float(size_value) - - if max_num is not None: - self._current_config['max_num'] = int(max_num) - - '''debug params''' - for line in current_config['debug']: - if 'size' in line: - self._current_config['debug_size'] = float(line.split(' ')[-1]) - elif 'frequency' in line: - self._current_config['debug_frequency'] = line.split(' ')[-1] - elif 'size-pct' in line: - self._current_config['debug_size_pct'] = float(line.split(' ')[-1]) - elif 'max-num' in line: - self._current_config['debug_max_num'] = int(line.split(' ')[-1]) - - def get_required_config(self): - self._required_config = dict() - required_config = dict() - module_params = self._module.params - - delete_group = module_params.get('delete_group') - upload_file = module_params.get('upload_file') - rotation = module_params.get('rotation') - if delete_group: - required_config['delete_group'] = delete_group - if upload_file: - required_config.update({'upload_file': upload_file, - 'upload_url': module_params.get('upload_url')}) - if rotation: - required_config['rotation'] = rotation - required_config['debug'] = module_params['debug'] - - self.validate_param_values(required_config) - self._required_config = required_config - - def generate_commands(self): - required_config = self._required_config - current_config = self._current_config - - logging_files_type = 'debug-files' if required_config['debug'] else 'files' - debug_prefix = 'debug_' if required_config['debug'] else '' - - rotation = required_config.get('rotation') - if rotation: - for key in rotation: - if rotation.get(key) and current_config.get(debug_prefix + key) != rotation.get(key): - cmd = self.ROTATION_CMDS[key].format(logging_files_type, rotation[key]) if key != 'force' else\ - self.ROTATION_CMDS[key].format(logging_files_type) - self._commands.append(cmd) - - delete_group = required_config.get('delete_group') - if delete_group: - self._commands.append('logging {0} delete {1}'.format(logging_files_type, - delete_group)) - - upload_file = required_config.get('upload_file') - if upload_file: - self._commands.append('logging {0} upload {1} {2}'.format(logging_files_type, - upload_file, required_config.get('upload_url'))) - - -def main(): - """ main entry point for module execution - """ - OnyxSyslogFilesModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_syslog_remote.py b/plugins/modules/network/onyx/onyx_syslog_remote.py deleted file mode 100644 index 507d684252..0000000000 --- a/plugins/modules/network/onyx/onyx_syslog_remote.py +++ /dev/null @@ -1,349 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' -module: onyx_syslog_remote -author: "Anas Shami (@anass)" -short_description: Configure remote syslog module -description: - - This module provides declarative management of syslog - on Mellanox ONYX network devices. -notes: -options: - enabled: - description: - - Disable/Enable logging to given remote host - default: true - type: bool - host: - description: - - Send event logs to this server using the syslog protocol - required: true - type: str - port: - description: - - Set remote server destination port for log messages - type: int - trap: - description: - - Minimum severity level for messages to this syslog server - choices: ['none', 'debug', 'info', 'notice', 'alert', 'warning', 'err', 'emerg', 'crit'] - type: str - trap_override: - description: - - Override log levels for this sink on a per-class basis - type: list - suboptions: - override_class: - description: - - Specify a class whose log level to override - choices: ['mgmt-front', 'mgmt-back', 'mgmt-core', 'events', 'debug-module', 'sx-sdk', 'mlx-daemons', 'protocol-stack'] - required: True - type: str - override_priority: - description: - -Specify a priority whose log level to override - choices: ['none', 'debug', 'info', 'notice', 'alert', 'warning', 'err', 'emerg', 'crit'] - type: str - override_enabled: - description: - - disable override priorities for specific class. - default: True - type: bool - - filter: - description: - - Specify a filter type - choices: ['include', 'exclude'] - type: str - filter_str: - description: - - Specify a regex filter string - type: str -''' - -EXAMPLES = """ -- name: remote logging port 8080 -- onyx_syslog_remote: - host: 10.10.10.10 - port: 8080 - -- name: remote logging trap override -- onyx_syslog_remote: - host: 10.10.10.10 - trap_override: - - override_class: events - override_priority: emerg - -- name: remote logging trap emerg -- onyx_syslog_remote: - host: 10.10.10.10 - trap: emerg - -- name: remote logging filter include 'ERR' -- onyx_syslog_remote: - host: 10.10.10.10 - filter: include - filter_str: /ERR/ - -- name: disable remote logging with class events -- onyx_syslog_remote: - enabled: False - host: 10.10.10.10 - class: events -- name : disable remote logging -- onyx_syslog_remote: - enabled: False - host: 10.10.10.10 - -- name : enable/disable override class -- onyx_syslog_remote: - host: 10.7.144.71 - trap_override: - - override_class: events - override_priority: emerg - override_enabled: False - - override_class: mgmt-front - override_priority: alert -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - logging x port 8080 - - logging 10.10.10.10 trap override class events priority emerg - - no logging 10.10.10.10 trap override class events - - logging 10.10.10.10 trap emerg - - logging 10.10.10.10 filter [include | exclude] ERR -""" - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxSyslogRemoteModule(BaseOnyxModule): - MAX_PORT = 65535 - LEVELS = ['none', 'debug', 'info', 'notice', 'alert', 'warning', 'err', 'emerg', 'crit'] - CLASSES = ['mgmt-front', 'mgmt-back', 'mgmt-core', 'events', 'debug-module', 'sx-sdk', 'mlx-daemons', 'protocol-stack'] - FILTER = ['include', 'exclude'] - - LOGGING_HOST = re.compile(r'^logging ([a-z0-9\.]+)$') - LOGGING_PORT = re.compile(r'^logging ([a-z0-9\.]+) port ([0-9]+)$') - LOGGING_TRAP = re.compile(r'^logging ([a-z0-9\.]+) trap ([a-z]+)$') - LOGGING_TRAP_OVERRIDE = re.compile(r'^logging ([a-z0-9\.]+) trap override class ([a-z\-]+) priority ([a-z]+)$') - LOGGING_FILTER = re.compile(r'^logging ([a-z0-9\.]+) filter (include|exclude) "([\D\d]+)"$') - - def init_module(self): - """" Ansible module initialization - """ - override_spec = dict(override_priority=dict(choices=self.LEVELS), - override_class=dict(choices=self.CLASSES, required=True), - override_enabled=dict(default=True, type="bool")) - - element_spec = dict(enabled=dict(type="bool", default=True), - host=dict(type="str", required=True), - port=dict(type="int"), - trap=dict(choices=self.LEVELS), - trap_override=dict(type="list", elements='dict', options=override_spec), - filter=dict(choices=self.FILTER), - filter_str=dict(type="str")) - - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_together=[ - ['filter', 'filter_str'] - ]) - - def validate_port(self, port): - if port and (port < 1 or port > self.MAX_PORT): - self._module.fail_json(msg='logging port must be between 1 and {0}'.format(self.MAX_PORT)) - - def show_logging(self): - # we can't use show logging it has lack of information - return show_cmd(self._module, "show running-config | include .*logging.*", json_fmt=False, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - current_config = self.show_logging().split('\n') - for line in current_config: - line = line.strip() - match = self.LOGGING_HOST.match(line) - if match: - host = match.group(1) - self._current_config[host] = dict() - continue - - match = self.LOGGING_PORT.match(line) - if match: - host = match.group(1) - port = int(match.group(2)) - if host in self._current_config: - self._current_config[host]['port'] = port - else: - self._current_config[host] = dict(port=port) - continue - - match = self.LOGGING_TRAP.match(line) - if match: - host = match.group(1) - trap = match.group(2) - host_config = self._current_config.get(host) - if host_config: - self._current_config[host]['trap'] = trap - else: - self._current_config[host] = dict(trap=trap) - continue - - match = self.LOGGING_TRAP_OVERRIDE.match(line) - if match: - host = match.group(1) - override_class = match.group(2) - override_priority = match.group(3) - host_config = self._current_config.get(host) - - if host_config: - if 'trap_override' in host_config: - self._current_config[host]['trap_override'].append(dict(override_class=override_class, override_priority=override_priority)) - else: - self._current_config[host]['trap_override'] = [dict(override_class=override_class, override_priority=override_priority)] - else: - self._current_config[host] = {'trap_override': [dict(override_class=override_class, override_priority=override_priority)]} - continue - - match = self.LOGGING_FILTER.match(line) - if match: - host = match.group(1) - filter_type = match.group(2) - filter_str = match.group(3) - if host in self._current_config: - self._current_config[host].update({'filter': filter_type, 'filter_str': filter_str}) - else: - self._current_config[host] = dict(filter=filter_type, filter_str=filter_str) - - def get_required_config(self): - self._required_config = dict() - required_config = dict() - module_params = self._module.params - port = module_params.get('port') - trap = module_params.get('trap') - trap_override = module_params.get('trap_override') - filtered = module_params.get('filter') - - required_config['host'] = module_params.get('host') - required_config['enabled'] = module_params.get('enabled') - - if port: - required_config['port'] = port - if trap: - required_config['trap'] = trap - if trap_override: - required_config['trap_override'] = trap_override - if filtered: - required_config['filter'] = filtered - required_config['filter_str'] = module_params.get('filter_str', '') - - self.validate_param_values(required_config) - self._required_config = required_config - - def generate_commands(self): - required_config = self._required_config - current_config = self._current_config - host = required_config.get('host') - enabled = required_config['enabled'] - ''' - cases: - if host in current config and current config != required config and its enable - if host in current config and its disable - if host in current and it has override_class with disable flag - ''' - host_config = current_config.get(host, dict()) - - if host in current_config and not enabled: - self._commands.append('no logging {0}'.format(host)) - else: - if host not in current_config: - self._commands.append('logging {0}'.format(host)) - if 'port' in required_config: - if required_config['port'] != host_config.get('port', None) or not host_config: - '''Edit/Create new one''' - self._commands.append('logging {0} port {1}'.format(host, required_config['port'])) - - if 'trap' in required_config or 'trap_override' in required_config: - trap_commands = self._get_trap_commands(host) - self._commands += trap_commands - - if 'filter' in required_config: - is_change = host_config.get('filter', None) != required_config['filter'] or \ - host_config.get('filter_str', None) != required_config['filter_str'] - if is_change or not host_config: - self._commands.append('logging {0} filter {1} {2}'.format(host, required_config['filter'], required_config['filter_str'])) - - ''' ********** private methods ********** ''' - def _get_trap_commands(self, host): - current_config = self._current_config - required_config = self._required_config - trap_commands = [] - host_config = current_config.get(host, dict()) - - override_list = required_config.get('trap_override') - if override_list: - current_override_list = host_config.get('trap_override', []) - - for override_trap in override_list: - override_class = override_trap.get('override_class') - override_priority = override_trap.get('override_priority') - override_enabled = override_trap.get('override_enabled') - found, found_class = False, False - for current_override in current_override_list: - if current_override.get('override_class') == override_class: - found_class = True - if not override_enabled: - break - if override_priority and current_override.get('override_priority') == override_priority: - found = True - break - - if override_enabled: - if not found and override_priority: - trap_commands.append('logging {0} trap override class {1} priority {2}'.format( - host, override_class, override_priority)) - elif found_class: # disabled option will use only class - trap_commands.append('no logging {0} trap override class {1}'.format( - host, override_class)) - - else: - if required_config['enabled']: # no disabled option for this, just override trap level can be disabled - trap = required_config.get('trap') - if trap and (trap != host_config.get('trap', None) or not host_config): - trap_commands.append('logging {0} trap {1}'.format( - host, trap)) - '''no disable for trap''' - - return trap_commands - - -def main(): - """ main entry point for module execution - """ - OnyxSyslogRemoteModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_traffic_class.py b/plugins/modules/network/onyx/onyx_traffic_class.py deleted file mode 100644 index cb2b7f4322..0000000000 --- a/plugins/modules/network/onyx/onyx_traffic_class.py +++ /dev/null @@ -1,325 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_traffic_class -author: "Anas Badaha (@anasb)" -short_description: Configures Traffic Class -description: - - This module provides declarative management of Traffic Class configuration - on Mellanox ONYX network devices. -options: - state: - description: - - enable congestion control on interface. - choices: ['enabled', 'disabled'] - default: enabled - interfaces: - description: - - list of interfaces name. - required: true - tc: - description: - - traffic class, range 0-7. - required: true - congestion_control: - description: - - configure congestion control on interface. - suboptions: - control: - description: - - congestion control type. - choices: ['red', 'ecn', 'both'] - required: true - threshold_mode: - description: - - congestion control threshold mode. - choices: ['absolute', 'relative'] - required: true - min_threshold: - description: - - Set minimum-threshold value (in KBs) for marking traffic-class queue. - required: true - max_threshold: - description: - - Set maximum-threshold value (in KBs) for marking traffic-class queue. - required: true - dcb: - description: - - configure dcb control on interface. - suboptions: - mode: - description: - - dcb control mode. - choices: ['strict', 'wrr'] - required: true - weight: - description: - - Relevant only for wrr mode. -''' - -EXAMPLES = """ -- name: configure traffic class - onyx_traffic_class: - interfaces: - - Eth1/1 - - Eth1/2 - tc: 3 - congestion_control: - control: ecn - threshold_mode: absolute - min_threshold: 500 - max_threshold: 1500 - dcb: - mode: strict -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface ethernet 1/15 traffic-class 3 congestion-control ecn minimum-absolute 150 maximum-absolute 1500 - - interface ethernet 1/16 traffic-class 3 congestion-control ecn minimum-absolute 150 maximum-absolute 1500 - - interface mlag-port-channel 7 traffic-class 3 congestion-control ecn minimum-absolute 150 maximum-absolute 1500 - - interface port-channel 1 traffic-class 3 congestion-control ecn minimum-absolute 150 maximum-absolute 1500 - - interface ethernet 1/15 traffic-class 3 dcb ets strict - - interface ethernet 1/16 traffic-class 3 dcb ets strict - - interface mlag-port-channel 7 traffic-class 3 dcb ets strict - - interface port-channel 1 traffic-class 3 dcb ets strict -""" - -import re -from ansible.module_utils.six import iteritems -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxTrafficClassModule(BaseOnyxModule): - - IF_ETH_REGEX = re.compile(r"^Eth(\d+\/\d+|Eth\d+\/\d+\d+)$") - IF_PO_REGEX = re.compile(r"^Po(\d+)$") - MLAG_NAME_REGEX = re.compile(r"^Mpo(\d+)$") - - IF_TYPE_ETH = "ethernet" - PORT_CHANNEL = "port-channel" - MLAG_PORT_CHANNEL = "mlag-port-channel" - - IF_TYPE_MAP = { - IF_TYPE_ETH: IF_ETH_REGEX, - PORT_CHANNEL: IF_PO_REGEX, - MLAG_PORT_CHANNEL: MLAG_NAME_REGEX - } - - def init_module(self): - """ initialize module - """ - congestion_control_spec = dict(control=dict(choices=['red', 'ecn', 'both'], required=True), - threshold_mode=dict(choices=['absolute', 'relative'], required=True), - min_threshold=dict(type=int, required=True), - max_threshold=dict(type=int, required=True)) - - dcb_spec = dict(mode=dict(choices=['strict', 'wrr'], required=True), - weight=dict(type=int)) - - element_spec = dict( - interfaces=dict(type='list', required=True), - tc=dict(type=int, required=True), - congestion_control=dict(type='dict', options=congestion_control_spec), - dcb=dict(type='dict', options=dcb_spec), - state=dict(choices=['enabled', 'disabled'], default='enabled')) - - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def validate_tc(self, value): - if value and not 0 <= int(value) <= 7: - self._module.fail_json(msg='tc value must be between 0 and 7') - - def validate_param_values(self, obj, param=None): - dcb = obj.get("dcb") - if dcb is not None: - dcb_mode = dcb.get("mode") - weight = dcb.get("weight") - if dcb_mode == "wrr" and weight is None: - self._module.fail_json(msg='User should send weight attribute when dcb mode is wrr') - super(OnyxTrafficClassModule, self).validate_param_values(obj, param) - - def _get_interface_type(self, if_name): - if_type = None - if_id = None - for interface_type, interface_regex in iteritems(self.IF_TYPE_MAP): - match = interface_regex.match(if_name) - if match: - if_type = interface_type - if_id = match.group(1) - break - return if_type, if_id - - def _set_interface_congestion_control_config(self, interface_congestion_control_config, - interface, if_type, if_id): - tc = self._required_config.get("tc") - interface_dcb_ets = self._show_interface_dcb_ets(if_type, if_id)[0].get(interface) - if interface_dcb_ets is None: - dcb = dict() - else: - ets_per_tc = interface_dcb_ets[2].get("ETS per TC") - tc_config = ets_per_tc[0].get(str(tc)) - dcb_mode = tc_config[0].get("S.Mode") - dcb_weight = int(tc_config[0].get("W")) - dcb = dict(mode=dcb_mode.lower(), weight=dcb_weight) - - interface_congestion_control_config = interface_congestion_control_config[tc + 1] - mode = interface_congestion_control_config.get("Mode") - if mode == "none": - self._current_config[interface] = dict(state="disabled", dcb=dcb, if_type=if_type, if_id=if_id) - return - - threshold_mode = interface_congestion_control_config.get("Threshold mode") - max_threshold = interface_congestion_control_config.get("Maximum threshold") - min_threshold = interface_congestion_control_config.get("Minimum threshold") - - if threshold_mode == "absolute": - delimiter = ' ' - else: - delimiter = '%' - min_value = int(min_threshold.split(delimiter)[0]) - max_malue = int(max_threshold.split(delimiter)[0]) - congestion_control = dict(control=mode.lower(), threshold_mode=threshold_mode, - min_threshold=min_value, max_threshold=max_malue) - - self._current_config[interface] = dict(state="enabled", congestion_control=congestion_control, - dcb=dcb, if_type=if_type, if_id=if_id) - - def _show_interface_congestion_control(self, if_type, interface): - cmd = "show interfaces {0} {1} congestion-control".format(if_type, interface) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def _show_interface_dcb_ets(self, if_type, interface): - cmd = "show dcb ets interface {0} {1}".format(if_type, interface) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - for interface in self._required_config.get("interfaces"): - if_type, if_id = self._get_interface_type(interface) - if not if_id: - self._module.fail_json( - msg='unsupported interface: {0}'.format(interface)) - interface_congestion_control_config = self._show_interface_congestion_control(if_type, if_id) - if interface_congestion_control_config is not None: - self._set_interface_congestion_control_config(interface_congestion_control_config, - interface, if_type, if_id) - else: - self._module.fail_json( - msg='Interface {0} does not exist on switch'.format(interface)) - - def generate_commands(self): - state = self._required_config.get("state") - tc = self._required_config.get("tc") - interfaces = self._required_config.get("interfaces") - for interface in interfaces: - current_interface = self._current_config.get(interface) - current_state = current_interface.get("state") - if_type = current_interface.get("if_type") - if_id = current_interface.get("if_id") - if state == "disabled": - if current_state == "enabled": - self._commands.append('interface {0} {1} no traffic-class {2} congestion-control'.format(if_type, if_id, tc)) - continue - - congestion_control = self._required_config.get("congestion_control") - - if congestion_control is not None: - control = congestion_control.get("control") - current_congestion_control = current_interface.get("congestion_control") - threshold_mode = congestion_control.get("threshold_mode") - min_threshold = congestion_control.get("min_threshold") - max_threshold = congestion_control.get("max_threshold") - if current_congestion_control is None: - self._threshold_mode_generate_cmds_mappers(threshold_mode, if_type, if_id, tc, - control, min_threshold, max_threshold) - else: - current_control = current_congestion_control.get("control") - curr_threshold_mode = current_congestion_control.get("threshold_mode") - curr_min_threshold = current_congestion_control.get("min_threshold") - curr_max_threshold = current_congestion_control.get("max_threshold") - - if control != current_control: - self._threshold_mode_generate_cmds_mappers(threshold_mode, if_type, if_id, tc, - control, min_threshold, max_threshold) - else: - if threshold_mode != curr_threshold_mode: - self._threshold_mode_generate_cmds_mappers(threshold_mode, if_type, if_id, tc, - control, min_threshold, max_threshold) - elif min_threshold != curr_min_threshold or max_threshold != curr_max_threshold: - self._threshold_mode_generate_cmds_mappers(threshold_mode, if_type, if_id, tc, - control, min_threshold, max_threshold) - - dcb = self._required_config.get("dcb") - if dcb is not None: - dcb_mode = dcb.get("mode") - current_dcb = current_interface.get("dcb") - current_dcb_mode = current_dcb.get("mode") - if dcb_mode == "strict" and dcb_mode != current_dcb_mode: - self._commands.append('interface {0} {1} traffic-class {2} ' - 'dcb ets {3}'.format(if_type, if_id, tc, dcb_mode)) - elif dcb_mode == "wrr": - weight = dcb.get("weight") - current_weight = current_dcb.get("weight") - if dcb_mode != current_dcb_mode or weight != current_weight: - self._commands.append('interface {0} {1} traffic-class {2} ' - 'dcb ets {3} {4}'.format(if_type, if_id, tc, dcb_mode, weight)) - - def _threshold_mode_generate_cmds_mappers(self, threshold_mode, if_type, if_id, tc, - control, min_threshold, max_threshold): - if threshold_mode == 'absolute': - self._generate_congestion_control_absolute_cmds(if_type, if_id, tc, control, - min_threshold, max_threshold) - else: - self._generate_congestion_control_relative_cmds(if_type, if_id, tc, control, - min_threshold, max_threshold) - - def _generate_congestion_control_absolute_cmds(self, if_type, if_id, tc, control, - min_absolute, max_absolute): - self._commands.append('interface {0} {1} traffic-class {2} ' - 'congestion-control {3} minimum-absolute {4} ' - 'maximum-absolute {5}'.format(if_type, if_id, tc, control, - min_absolute, max_absolute)) - - def _generate_congestion_control_relative_cmds(self, if_type, if_id, tc, control, - min_relative, max_relative): - self._commands.append('interface {0} {1} traffic-class {2} ' - 'congestion-control {3} minimum-relative {4} ' - 'maximum-relative {5}'.format(if_type, if_id, tc, control, - min_relative, max_relative)) - - -def main(): - """ main entry point for module execution - """ - OnyxTrafficClassModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_username.py b/plugins/modules/network/onyx/onyx_username.py deleted file mode 100644 index 5395ebb61f..0000000000 --- a/plugins/modules/network/onyx/onyx_username.py +++ /dev/null @@ -1,289 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_username -author: "Anas Shami (@anass)" -short_description: Configure username module -description: - - This module provides declarative management of users/roles - on Mellanox ONYX network devices. -notes: -options: - username: - description: - - Create/Edit user using username - type: str - required: True - full_name: - description: - - Set the full name of this user - type: str - nopassword: - description: - - Clear password for such user - type: bool - default: False - password: - description: - - Set password fot such user - type: str - encrypted_password: - description: - - Decide the type of setted password (plain text or encrypted) - type: bool - default: False - capability: - description: - - Grant capability to this user account - type: str - choices: ['monitor', 'unpriv', 'v_admin', 'admin'] - reset_capability: - description: - - Reset capability to this user account - type: bool - default: False - disconnected: - description: - - Disconnect all sessions of this user - type: bool - default: False - disabled: - description: - - Disable means of logging into this account - type: str - choices: ['none', 'login', 'password', 'all'] - state: - description: - - Set state of the given account - default: present - type: str - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: create new user - onyx_username: - username: anass - -- name: set the user full-name - onyx_username: - username: anass - full_name: anasshami - -- name: set the user encrypted password - onyx_username: - username: anass - password: 12345 - encrypted_password: True - -- name: set the user capability - onyx_username: - username: anass - capability: monitor - -- name: reset the user capability - onyx_username: - username: anass - reset_capability: True - -- name: remove the user configuration - onyx_username: - username: anass - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - username * - - username * password * - - username * nopassword - - username * disable login - - username * capability admin - - no username * - - no username * disable - -""" -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule, show_cmd - - -class OnyxUsernameModule(BaseOnyxModule): - ACCOUNT_STATE = { - 'Account locked out': dict(disabled='all'), - 'No password required for login': dict(nopassword=True), - 'Local password login disabled': dict(disabled='password'), - 'Account disabled': dict(disabled='all') - } - ENCRYPTED_ID = 7 - - def init_module(self): - """ - module initialization - """ - element_spec = dict() - - argument_spec = dict(state=dict(choices=['absent', 'present'], default='present'), - username=dict(type='str', required=True), - disabled=dict(choices=['none', 'login', 'password', 'all']), - capability=dict(choices=['monitor', 'unpriv', 'v_admin', 'admin']), - nopassword=dict(type='bool', default=False), - password=dict(type='str', no_log=True), - encrypted_password=dict(type='bool', default=False), - reset_capability=dict(type="bool", default=False), - disconnected=dict(type='bool', default=False), - full_name=dict(type='str')) - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - mutually_exclusive=[['password', 'nopassword']]) - - def get_required_config(self): - self._required_config = dict() - module_params = self._module.params - params = {} - ''' Requred/Default fields ''' - params['username'] = module_params.get('username') - params['state'] = module_params.get('state') - params['encrypted_password'] = module_params.get('encrypted_password') - params['reset_capability'] = module_params.get('reset_capability') - ''' Other fields ''' - for key, value in module_params.items(): - if value is not None: - params[key] = value - self.validate_param_values(params) - self._required_config = params - - def _get_username_config(self): - return show_cmd(self._module, "show usernames", json_fmt=True, fail_on_error=False) - - def _set_current_config(self, users_config): - ''' - users_config ex: - { - admin": [ - { - "CAPABILITY": "admin", - "ACCOUNT STATUS": "No password required for login", - "FULL NAME": "System Administrator" - } - ], - } - ''' - if not users_config: - return - current_config = self._current_config - for username, config in users_config.items(): - config_json = config[0] - current_config[username] = current_config.get(username, {}) - account_status = config_json.get('ACCOUNT STATUS') - status_value = self.ACCOUNT_STATE.get(account_status) - - if status_value is not None: - # None for enabled account with password account "Password set (SHA512 | MD5)" so we won't change any attribute here. - current_config[username].update(status_value) - current_config[username].update({ - 'capability': config_json.get('CAPABILITY'), - 'full_name': config_json.get('FULL NAME') - }) - - def load_current_config(self): - self._current_config = dict() - users_config = self._get_username_config() - self._set_current_config(users_config) - - def generate_commands(self): - current_config, required_config = self._current_config, self._required_config - username = required_config.get('username') - current_user = current_config.get(username) - if current_user is not None: - '''created account we just need to edit his attributes''' - full_name = required_config.get('full_name') - if full_name is not None and current_user.get('full_name') != full_name: - self._commands.append("username {0} full-name {1}".format(username, full_name)) - - disabled = required_config.get('disabled') - if disabled is not None and current_user.get('disabled') != disabled: - if disabled == 'none': - self._commands.append("no username {0} disable".format(username)) - elif disabled == 'all': - self._commands.append("username {0} disable".format(username)) - else: - self._commands.append("username {0} disable {1}".format(username, disabled)) - - state = required_config.get('state') - if state == 'absent': # this will remove the user - self._commands.append("no username {0}".format(username)) - - capability = required_config.get('capability') - if capability is not None and current_user.get('capability') != capability: - self._commands.append("username {0} capability {1}".format(username, capability)) - - reset_capability = required_config.get('reset_capability') - if reset_capability: - self._commands.append("no username {0} capability".format(username)) - - password = required_config.get('password') - if password is not None: - encrypted = required_config.get('encrypted_password') - if encrypted: - self._commands.append("username {0} password {1} {2}".format(username, self.ENCRYPTED_ID, password)) - else: - self._commands.append("username {0} password {1}".format(username, password)) - - nopassword = required_config.get('nopassword') - if nopassword and nopassword != current_user.get('nopassword', False): - self._commands.append("username {0} nopassword".format(username)) - - disconnected = required_config.get('disconnected') - if disconnected: - self._commands.append("username {0} disconnect".format(username)) - else: - '''create new account if we have valid inforamtion, just check for username, capability, full_name, password''' - - capability = required_config.get('capability') - password = required_config.get('password') - full_name = required_config.get('full_name') - if capability is not None or password is not None or full_name is not None: - if capability is not None: - self._commands.append("username {0} capability {1}".format(username, capability)) - - if password is not None: - encrypted = required_config.get('encrypted_password') - if encrypted: - self._commands.append("username {0} password {1} {2} ".format(username, self.ENCRYPTED_ID, password)) - else: - self._commands.append("username {0} password {1}".format(username, password)) - - if full_name is not None: - self._commands.append("username {0} full-name {1}".format(username, full_name)) - - else: - self._commands.append("username {0}".format(username)) - - -def main(): - """ main entry point for module execution - """ - OnyxUsernameModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_vlan.py b/plugins/modules/network/onyx/onyx_vlan.py deleted file mode 100644 index d0016fb4bb..0000000000 --- a/plugins/modules/network/onyx/onyx_vlan.py +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_vlan -author: "Samer Deeb (@samerd) Alex Tabachnik (@atabachnik)" -short_description: Manage VLANs on Mellanox ONYX network devices -description: - - This module provides declarative management of VLANs - on Mellanox ONYX network devices. -options: - name: - description: - - Name of the VLAN. - vlan_id: - description: - - ID of the VLAN. - aggregate: - description: List of VLANs definitions. - purge: - description: - - Purge VLANs not defined in the I(aggregate) parameter. - default: no - type: bool - state: - description: - - State of the VLAN configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: configure VLAN ID and name - onyx_vlan: - vlan_id: 20 - name: test-vlan - -- name: remove configuration - onyx_vlan: - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always. - type: list - sample: - - vlan 20 - - name test-vlan - - exit -""" - -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec - -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd - - -class OnyxVlanModule(BaseOnyxModule): - _purge = False - - @classmethod - def _get_element_spec(cls): - return dict( - vlan_id=dict(type='int'), - name=dict(type='str'), - state=dict(default='present', choices=['present', 'absent']), - ) - - @classmethod - def _get_aggregate_spec(cls, element_spec): - aggregate_spec = deepcopy(element_spec) - aggregate_spec['vlan_id'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - return aggregate_spec - - def init_module(self): - """ module initialization - """ - element_spec = self._get_element_spec() - aggregate_spec = self._get_aggregate_spec(element_spec) - argument_spec = dict( - aggregate=dict(type='list', elements='dict', - options=aggregate_spec), - purge=dict(default=False, type='bool'), - ) - argument_spec.update(element_spec) - required_one_of = [['vlan_id', 'aggregate']] - mutually_exclusive = [['vlan_id', 'aggregate']] - self._module = AnsibleModule( - argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - def validate_vlan_id(self, value): - if value and not 1 <= int(value) <= 4094: - self._module.fail_json(msg='vlan id must be between 1 and 4094') - - def get_required_config(self): - self._required_config = list() - module_params = self._module.params - aggregate = module_params.get('aggregate') - self._purge = module_params.get('purge', False) - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module_params[key] - self.validate_param_values(item, item) - req_item = item.copy() - req_item['vlan_id'] = int(req_item['vlan_id']) - self._required_config.append(req_item) - else: - params = { - 'vlan_id': module_params['vlan_id'], - 'name': module_params['name'], - 'state': module_params['state'], - } - self.validate_param_values(params) - self._required_config.append(params) - - def _create_vlan_data(self, vlan_id, vlan_data): - if self._os_version >= self.ONYX_API_VERSION: - vlan_data = vlan_data[0] - return { - 'vlan_id': vlan_id, - 'name': self.get_config_attr(vlan_data, 'Name') - } - - def _get_vlan_config(self): - return show_cmd(self._module, "show vlan") - - def load_current_config(self): - # called in base class in run function - self._os_version = self._get_os_version() - self._current_config = dict() - vlan_config = self._get_vlan_config() - if not vlan_config: - return - for vlan_id, vlan_data in iteritems(vlan_config): - try: - vlan_id = int(vlan_id) - except ValueError: - continue - self._current_config[vlan_id] = \ - self._create_vlan_data(vlan_id, vlan_data) - - def generate_commands(self): - req_vlans = set() - for req_conf in self._required_config: - state = req_conf['state'] - vlan_id = req_conf['vlan_id'] - if state == 'absent': - if vlan_id in self._current_config: - self._commands.append('no vlan %s' % vlan_id) - else: - req_vlans.add(vlan_id) - self._generate_vlan_commands(vlan_id, req_conf) - if self._purge: - for vlan_id in self._current_config: - if vlan_id not in req_vlans: - self._commands.append('no vlan %s' % vlan_id) - - def _generate_vlan_commands(self, vlan_id, req_conf): - curr_vlan = self._current_config.get(vlan_id, {}) - if not curr_vlan: - self._commands.append("vlan %s" % vlan_id) - self._commands.append("exit") - req_name = req_conf['name'] - curr_name = curr_vlan.get('name') - if req_name: - if req_name != curr_name: - self._commands.append("vlan %s name %s" % (vlan_id, req_name)) - elif req_name is not None: - if curr_name: - self._commands.append("vlan %s no name" % vlan_id) - - -def main(): - """ main entry point for module execution - """ - OnyxVlanModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_vxlan.py b/plugins/modules/network/onyx/onyx_vxlan.py deleted file mode 100644 index 0fc406aabf..0000000000 --- a/plugins/modules/network/onyx/onyx_vxlan.py +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_vxlan -author: "Anas Badaha (@anasb)" -short_description: Configures Vxlan -description: - - This module provides declarative management of Vxlan configuration - on Mellanox ONYX network devices. -notes: - - Tested on ONYX evpn_dev.031. - - nve protocol must be enabled. -options: - nve_id: - description: - - nve interface ID. - required: true - loopback_id: - description: - - loopback interface ID. - bgp: - description: - - configure bgp on nve interface. - type: bool - default: true - mlag_tunnel_ip: - description: - - vxlan Mlag tunnel IP - vni_vlan_list: - description: - - Each item in the list has two attributes vlan_id, vni_id. - arp_suppression: - description: - - A flag telling if to configure arp suppression. - type: bool - default: false -''' - -EXAMPLES = """ -- name: configure Vxlan - onyx_vxlan: - nve_id: 1 - loopback_id: 1 - bgp: yes - mlag-tunnel-ip: 100.0.0.1 - vni_vlan_list: - - vlan_id: 10 - vni_id: 10010 - - vlan_id: 6 - vni_id: 10060 - arp_suppression: yes -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - interface nve 1 - - interface nve 1 vxlan source interface loopback 1 - - interface nve 1 nve controller bgp - - interface nve 1 vxlan mlag-tunnel-ip 100.0.0.1 - - interface nve 1 nve vni 10010 vlan 10 - - interface nve 1 nve vni 10060 vlan 6 - - interface nve 1 nve neigh-suppression - - interface vlan 6 - - interface vlan 10 -""" - -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import show_cmd -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule - - -class OnyxVxlanModule(BaseOnyxModule): - - LOOPBACK_REGEX = re.compile(r'^loopback (\d+).*') - NVE_ID_REGEX = re.compile(r'^Interface NVE (\d+).*') - - def init_module(self): - """ initialize module - """ - vni_vlan_spec = dict(vlan_id=dict(type=int), - vni_id=dict(type=int)) - element_spec = dict( - nve_id=dict(type=int), - loopback_id=dict(type=int), - bgp=dict(default=True, type='bool'), - mlag_tunnel_ip=dict(type='str'), - vni_vlan_list=dict(type='list', - elements='dict', - options=vni_vlan_spec), - arp_suppression=dict(default=False, type='bool') - ) - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True) - - def get_required_config(self): - module_params = self._module.params - self._required_config = dict(module_params) - self.validate_param_values(self._required_config) - - def _set_vxlan_config(self, vxlan_config): - vxlan_config = vxlan_config[0] - if not vxlan_config: - return - nve_header = vxlan_config.get("header") - match = self.NVE_ID_REGEX.match(nve_header) - if match: - current_nve_id = int(match.group(1)) - self._current_config['nve_id'] = current_nve_id - if int(current_nve_id) != self._required_config.get("nve_id"): - return - - self._current_config['mlag_tunnel_ip'] = vxlan_config.get("Mlag tunnel IP") - controller_mode = vxlan_config.get("Controller mode") - if controller_mode == "BGP": - self._current_config['bgp'] = True - else: - self._current_config['bgp'] = False - - loopback_str = vxlan_config.get("Source interface") - match = self.LOOPBACK_REGEX.match(loopback_str) - if match: - loopback_id = match.group(1) - self._current_config['loopback_id'] = int(loopback_id) - - self._current_config['global_neigh_suppression'] = vxlan_config.get("Global Neigh-Suppression") - - vni_vlan_mapping = self._current_config['vni_vlan_mapping'] = dict() - nve_detail = self._show_nve_detail() - - if nve_detail is not None: - nve_detail = nve_detail[0] - - if nve_detail: - for vlan_id in nve_detail: - vni_vlan_mapping[int(vlan_id)] = dict( - vni_id=int(nve_detail[vlan_id][0].get("VNI")), - arp_suppression=nve_detail[vlan_id][0].get("Neigh Suppression")) - - def _show_vxlan_config(self): - cmd = "show interfaces nve" - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def _show_nve_detail(self): - cmd = "show interface nve {0} detail".format(self._required_config.get("nve_id")) - return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) - - def load_current_config(self): - self._current_config = dict() - vxlan_config = self._show_vxlan_config() - if vxlan_config: - self._set_vxlan_config(vxlan_config) - - def generate_commands(self): - nve_id = self._required_config.get("nve_id") - current_nve_id = self._current_config.get("nve_id") - - if current_nve_id is None: - self._add_nve_commands(nve_id) - elif current_nve_id != nve_id: - self._add_no_nve_commands(current_nve_id) - self._add_nve_commands(nve_id) - - bgp = self._required_config.get("bgp") - if bgp is not None: - curr_bgp = self._current_config.get("bgp") - if bgp and bgp != curr_bgp: - self._commands.append('interface nve {0} nve controller bgp'.format(nve_id)) - - loopback_id = self._required_config.get("loopback_id") - if loopback_id is not None: - curr_loopback_id = self._current_config.get("loopback_id") - if loopback_id != curr_loopback_id: - self._commands.append('interface nve {0} vxlan source interface ' - 'loopback {1} '.format(nve_id, loopback_id)) - - mlag_tunnel_ip = self._required_config.get("mlag_tunnel_ip") - if mlag_tunnel_ip is not None: - curr_mlag_tunnel_ip = self._current_config.get("mlag_tunnel_ip") - if mlag_tunnel_ip != curr_mlag_tunnel_ip: - self._commands.append('interface nve {0} vxlan ' - 'mlag-tunnel-ip {1}'.format(nve_id, mlag_tunnel_ip)) - - vni_vlan_list = self._required_config.get("vni_vlan_list") - arp_suppression = self._required_config.get("arp_suppression") - if vni_vlan_list is not None: - self._generate_vni_vlan_cmds(vni_vlan_list, nve_id, arp_suppression) - - def _generate_vni_vlan_cmds(self, vni_vlan_list, nve_id, arp_suppression): - - current_global_arp_suppression = self._current_config.get('global_neigh_suppression') - if arp_suppression is True and current_global_arp_suppression != "Enable": - self._commands.append('interface nve {0} nve neigh-suppression'.format(nve_id)) - - current_vni_vlan_mapping = self._current_config.get('vni_vlan_mapping') - if current_vni_vlan_mapping is None: - for vni_vlan in vni_vlan_list: - vlan_id = vni_vlan.get("vlan_id") - vni_id = vni_vlan.get("vni_id") - self._add_vni_vlan_cmds(nve_id, vni_id, vlan_id) - self._add_arp_suppression_cmds(arp_suppression, vlan_id) - else: - for vni_vlan in vni_vlan_list: - vlan_id = vni_vlan.get("vlan_id") - vni_id = vni_vlan.get("vni_id") - - currt_vlan_id = current_vni_vlan_mapping.get(vlan_id) - - if currt_vlan_id is None: - self._add_vni_vlan_cmds(nve_id, vni_id, vlan_id) - self._add_arp_suppression_cmds(arp_suppression, vlan_id) - else: - current_vni_id = currt_vlan_id.get("vni_id") - current_arp_suppression = currt_vlan_id.get("arp_suppression") - - if int(current_vni_id) != vni_id: - self._add_vni_vlan_cmds(nve_id, vni_id, vlan_id) - - if current_arp_suppression == "Disable": - self._add_arp_suppression_cmds(arp_suppression, vlan_id) - - def _add_no_nve_commands(self, current_nve_id): - self._commands.append('no interface nve {0}'.format(current_nve_id)) - - def _add_nve_commands(self, nve_id): - self._commands.append('interface nve {0}'.format(nve_id)) - self._commands.append('exit') - - def _add_vni_vlan_cmds(self, nve_id, vni_id, vlan_id): - self._commands.append('interface nve {0} nve vni {1} ' - 'vlan {2}'.format(nve_id, vni_id, vlan_id)) - - def _add_arp_suppression_cmds(self, arp_suppression, vlan_id): - if arp_suppression is True: - self._commands.append('interface vlan {0}'.format(vlan_id)) - self._commands.append('exit') - - -def main(): - """ main entry point for module execution - """ - OnyxVxlanModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/onyx/onyx_wjh.py b/plugins/modules/network/onyx/onyx_wjh.py deleted file mode 100644 index 32f7125584..0000000000 --- a/plugins/modules/network/onyx/onyx_wjh.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: onyx_wjh -author: "Anas Shami (@anass)" -short_description: Configure what-just-happend module -description: - - This module provides declarative management of wjh - on Mellanox ONYX network devices. -notes: -options: - group: - description: - - Name of wjh group. - choices: ['all', 'forwarding', 'acl'] - type: str - enabled: - description: - - wjh group status - type: bool - auto_export: - description: - - wjh group auto export pcap file status - type: bool - export_group: - description: - - wjh group auto export group - choices: ['all', 'forwarding', 'acl'] - type: str - clear_group: - description: - - clear pcap file by group - choices: ['all', 'user', 'auto-export'] - type: str -''' - -EXAMPLES = """ -- name: enable wjh - onyx_wjh: - group: forwarding - enabled: True - -- name: disable wjh - onyx_wjh: - group: forwarding - enabled: False - -- name: enable auto-export - onyx_wjh: - auto_export: True - export_group: forwarding -- name: disable auto-export - onyx_wjh: - auto_export: False - export_group: forwarding -- name: clear pcap file - onyx_wjh: - clear_group: auto-export -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always - type: list - sample: - - what-just-happend forwarding enable - - what-just-happend auto-export forwarding enable - - clear what-just-happend pcap-file user -""" -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.network.onyx.onyx import BaseOnyxModule, show_cmd - - -class OnyxWJHModule(BaseOnyxModule): - WJH_DISABLED_REGX = re.compile(r'^no what-just-happened ([a-z]+) enable.*') - WJH_DISABLED_AUTO_EXPORT_REGX = re.compile(r'^no what-just-happened auto-export ([a-z]+) enable.*') - - WJH_CMD_FMT = '{0}what-just-happened {1} enable' - WJH_EXPORT_CMD_FMT = '{0}what-just-happened auto-export {1} enable' - WJH_CLEAR_CMD_FMT = 'clear what-just-happened pcap-files {0}' - - WJH_GROUPS = ['all', 'forwarding', 'acl'] - CLEAR_GROUPS = ['all', 'user', 'auto-export'] - - def init_module(self): - """ - module initialization - """ - element_spec = dict(group=dict(choices=self.WJH_GROUPS), - enabled=dict(type='bool'), - auto_export=dict(type='bool'), - export_group=dict(choices=self.WJH_GROUPS), - clear_group=dict(choices=self.CLEAR_GROUPS)) - - argument_spec = dict() - argument_spec.update(element_spec) - self._module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - required_together=[ - ['group', 'enabled'], - ['auto_export', 'export_group'] - ]) - - def get_required_config(self): - self._required_config = dict() - module_params = self._module.params - group = module_params.get('group') - export_group = module_params.get('export_group') - clear_group = module_params.get('clear_group') - - params = dict() - if group: - enabled = module_params.get('enabled') - params.update({ - 'group': group, - 'enabled': enabled - }) - - if export_group: - auto_export = module_params.get('auto_export') - params.update({ - 'export_group': export_group, - 'auto_export': auto_export - }) - - if clear_group: - params.update({ - 'clear_group': clear_group - }) - - self.validate_param_values(params) - self._required_config = params - - def _get_wjh_config(self): - return show_cmd(self._module, "show running-config | include .*what-just-happened.*", json_fmt=False, fail_on_error=False) - - def _set_current_config(self, config): - if not config: - return - current_config = self._current_config - lines = config.split('\n') - for line in lines: - if line.startswith('#'): - continue - match = self.WJH_DISABLED_REGX.match(line) - if match: - # wjh is disabled - group = match.group(1) - current_config[group] = False - - match = self.WJH_DISABLED_AUTO_EXPORT_REGX.match(line) - if match: - # wjh auto export is disabled - export_group = match.group(1) + '_export' - current_config[export_group] = False - - ''' - show running config will contains [no wjh * group enable] if disabled - default config is enabled - ''' - def load_current_config(self): - self._current_config = dict() - config_lines = self._get_wjh_config() - if config_lines: - self._set_current_config(config_lines) - - def wjh_group_status(self, current_config, group_value, suffix=''): - current_enabled = False - if group_value == 'all': - # no disabled group so all would be false - current_enabled = not all([ - (group + suffix) in current_config for group in self.WJH_GROUPS]) - else: - # if no current-value its enabled - current_enabled = current_config[group_value + suffix] if((group_value + suffix) in current_config) else True - return current_enabled - - ''' - wjh is enabled "by default" - when wjh disable we will find no wjh commands in running config - ''' - def generate_commands(self): - current_config, required_config = self._current_config, self._required_config - group = required_config.get('group') - export_group = required_config.get('export_group') - clear_group = required_config.get('clear_group') - if group: - current_enabled = self.wjh_group_status(current_config, group) - if(required_config['enabled'] != current_enabled): - self._commands.append(self.WJH_CMD_FMT - .format(('' if required_config['enabled'] else 'no '), group)) - if export_group: - current_enabled = self.wjh_group_status(current_config, required_config['export_group'], '_export') - if(required_config['auto_export'] != current_enabled): - self._commands.append(self.WJH_EXPORT_CMD_FMT - .format(('' if required_config['auto_export'] else 'no '), export_group)) - if clear_group: - # clear pcap files - self._commands.append(self.WJH_CLEAR_CMD_FMT.format(clear_group)) - - -def main(): - """ main entry point for module execution - """ - OnyxWJHModule.main() - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/opx/opx_cps.py b/plugins/modules/network/opx/opx_cps.py deleted file mode 100644 index 7b80a75ffb..0000000000 --- a/plugins/modules/network/opx/opx_cps.py +++ /dev/null @@ -1,393 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2018 Dell Inc. or its subsidiaries. All Rights Reserved. -# -# This file is part of Ansible by Red Hat -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: opx_cps -author: "Senthil Kumar Ganesan (@skg-net)" -short_description: CPS operations on networking device running Openswitch (OPX) -description: - - Executes the given operation on the YANG object, using CPS API in the - networking device running OpenSwitch (OPX). It uses the YANG models - provided in https://github.com/open-switch/opx-base-model. -options: - module_name: - description: - - Yang path to be configured. - attr_type: - description: - - Attribute Yang type. - attr_data: - description: - - Attribute Yang path and their corresponding data. - operation: - description: - - Operation to be performed on the object. - default: create - choices: ['delete', 'create', 'set', 'action', 'get'] - db: - description: - - Queries/Writes the specified yang path from/to the db. - type: bool - default: 'no' - qualifier: - description: - - A qualifier provides the type of object data to retrieve or act on. - default: target - choices: ['target', 'observed', 'proposed', 'realtime', 'registration', 'running', 'startup'] - commit_event: - description: - - Attempts to force the auto-commit event to the specified yang object. - type: bool - default: 'no' -requirements: - - "cps" - - "cps_object" - - "cps_utils" -''' - -EXAMPLES = """ -- name: Create VLAN - opx_cps: - module_name: "dell-base-if-cmn/if/interfaces/interface" - attr_data: { - "base-if-vlan/if/interfaces/interface/id": 230, - "if/interfaces/interface/name": "br230", - "if/interfaces/interface/type": "ianaift:l2vlan" - } - operation: "create" -- name: Get VLAN - opx_cps: - module_name: "dell-base-if-cmn/if/interfaces/interface" - attr_data: { - "if/interfaces/interface/name": "br230", - } - operation: "get" -- name: Modify some attributes in VLAN - opx_cps: - module_name: "dell-base-if-cmn/if/interfaces/interface" - attr_data: { - "cps/key_data": - { "if/interfaces/interface/name": "br230" }, - "dell-if/if/interfaces/interface/untagged-ports": ["e101-008-0"], - } - operation: "set" -- name: Delete VLAN - opx_cps: - module_name: "dell-base-if-cmn/if/interfaces/interface" - attr_data: { - "if/interfaces/interface/name": "br230", - } - operation: "delete" -""" - -RETURN = """ -response: - description: Output from the CPS transaction. - Output of CPS Get operation if CPS set/create/delete not done. - returned: when a CPS transaction is successfully performed. - type: list - sample: - [{ - "data": { - "base-if-vlan/if/interfaces/interface/id": 230, - "cps/object-group/return-code": 0, - "dell-base-if-cmn/if/interfaces/interface/if-index": 46, - "if/interfaces/interface/name": "br230", - "if/interfaces/interface/type": "ianaift:l2vlan" - }, - "key": "target/dell-base-if-cmn/if/interfaces/interface" - }] -cps_curr_config: - description: Returns the CPS Get output i.e. the running configuration - before CPS operation of set/delete is performed - returned: when CPS operations set, delete - type: dict - sample: - [{ - "data": { - "base-if-vlan/if/interfaces/interface/id": 230, - "cps/key_data": { - "if/interfaces/interface/name": "br230" - }, - "dell-base-if-cmn/if/interfaces/interface/if-index": 44, - "dell-if/if/interfaces/interface/learning-mode": 1, - "dell-if/if/interfaces/interface/mtu": 1532, - "dell-if/if/interfaces/interface/phys-address": "", - "dell-if/if/interfaces/interface/vlan-type": 1, - "if/interfaces/interface/enabled": 0, - "if/interfaces/interface/type": "ianaift:l2vlan" - }, - "key": "target/dell-base-if-cmn/if/interfaces/interface" - }] -diff: - description: The actual configuration that will be pushed comparing - the running configuration and input attributes - returned: when CPS operations set, delete - type: dict - sample: - { - "cps/key_data": { - "if/interfaces/interface/name": "br230" - }, - "dell-if/if/interfaces/interface/untagged-ports": [ - "e101-007-0" - ] - } -db: - description: Denotes if CPS DB transaction was performed - returned: when db is set to True in module options - type: bool - sample: True -commit_event: - description: Denotes if auto-commit event is set - returned: when commit_event is set to True in module options - type: bool - sample: True -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import dict_diff - -try: - import cps - import cps_object - import cps_utils - HAS_CPS = True -except ImportError: - HAS_CPS = False - - -def convert_cps_raw_list(raw_list): - resp_list = [] - if raw_list: - for raw_elem in raw_list: - processed_element = convert_cps_raw_data(raw_elem) - if processed_element: - raw_key = raw_elem['key'] - individual_element = {} - individual_element['data'] = processed_element - individual_element['key'] = (cps.qual_from_key(raw_key) + "/" + - cps.name_from_key(raw_key, 1)) - resp_list.append(individual_element) - return resp_list - - -def convert_cps_raw_data(raw_elem): - d = {} - obj = cps_object.CPSObject(obj=raw_elem) - for attr in raw_elem['data']: - d[attr] = obj.get_attr_data(attr) - return d - - -def parse_cps_parameters(module_name, qualifier, attr_type, - attr_data, operation=None, db=None, - commit_event=None): - - obj = cps_object.CPSObject(module=module_name, qual=qualifier) - - if operation: - obj.set_property('oper', operation) - - if attr_type: - for key, val in iteritems(attr_type): - cps_utils.cps_attr_types_map.add_type(key, val) - - for key, val in iteritems(attr_data): - - embed_attrs = key.split(',') - embed_attrs_len = len(embed_attrs) - if embed_attrs_len >= 3: - obj.add_embed_attr(embed_attrs, val, embed_attrs_len - 2) - else: - if isinstance(val, str): - val_list = val.split(',') - # Treat as list if value contains ',' but is not - # enclosed within {} - if len(val_list) == 1 or val.startswith('{'): - obj.add_attr(key, val) - else: - obj.add_attr(key, val_list) - else: - obj.add_attr(key, val) - - if db: - cps.set_ownership_type(obj.get_key(), 'db') - obj.set_property('db', True) - else: - obj.set_property('db', False) - - if commit_event: - cps.set_auto_commit_event(obj.get_key(), True) - obj.set_property('commit-event', True) - return obj - - -def cps_get(obj): - - RESULT = dict() - key = obj.get() - l = [] - cps.get([key], l) - - resp_list = convert_cps_raw_list(l) - - RESULT["response"] = resp_list - return RESULT - - -def cps_transaction(obj): - - RESULT = dict() - ch = {'operation': obj.get_property('oper'), 'change': obj.get()} - if cps.transaction([ch]): - RESULT["response"] = convert_cps_raw_list([ch['change']]) - RESULT["changed"] = True - else: - error_msg = "Transaction error while " + obj.get_property('oper') - raise RuntimeError(error_msg) - return RESULT - - -def parse_key_data(attrs): - - res = dict() - for key, val in iteritems(attrs): - if key == 'cps/key_data': - res.update(val) - else: - res[key] = val - return res - - -def main(): - """ - main entry point for module execution - """ - argument_spec = dict( - qualifier=dict(required=False, - default="target", - type='str', - choices=['target', 'observed', 'proposed', 'realtime', - 'registration', 'running', 'startup']), - module_name=dict(required=True, type='str'), - attr_type=dict(required=False, type='dict'), - attr_data=dict(required=True, type='dict'), - operation=dict(required=False, - default="create", - type='str', - choices=['delete', 'create', 'set', 'action', 'get']), - db=dict(required=False, default=False, type='bool'), - commit_event=dict(required=False, default=False, type='bool') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=False) - - if not HAS_CPS: - module.fail_json(msg='CPS library required for this module') - - qualifier = module.params['qualifier'] - module_name = module.params['module_name'] - attr_type = module.params["attr_type"] - attr_data = module.params["attr_data"] - operation = module.params['operation'] - db = module.params["db"] - commit_event = module.params["commit_event"] - RESULT = dict(changed=False, db=False, commit_event=False) - - if db: - RESULT['db'] = True - if commit_event: - RESULT['commit_event'] = True - - try: - # First do a CPS get operation - get_obj = parse_cps_parameters(module_name, qualifier, attr_type, - attr_data, 'get', db, commit_event) - curr_config = cps_get(get_obj) - - if operation == 'get': - RESULT.update(curr_config) - else: - diff = attr_data - - # Evaluate the changes in the attributes - cfg = dict() - if curr_config and curr_config['response']: - cfg = curr_config['response'][0]['data'] - key_d = 'cps/key_data' - - # diff computation is not needed for delete - if operation != 'delete': - configs = parse_key_data(cfg) - attributes = parse_key_data(attr_data) - diff = dict_diff(configs, attributes) - # Append diff with any 'cps/key_data' from attr_data - if diff and key_d in attr_data: - diff[key_d] = attr_data[key_d] - - # Append diff with any 'cps/key_data' from curr_config - # Needed for all operations including delete - if diff and key_d in cfg: - if key_d in diff: - diff[key_d].update(cfg[key_d]) - else: - diff[key_d] = cfg[key_d] - - RESULT.update({"diff": diff}) - - # Create object for cps operation - obj = parse_cps_parameters(module_name, qualifier, attr_type, - diff, operation, db, commit_event) - - res = dict() - if operation == "delete": - if cfg: - res = cps_transaction(obj) - else: - if diff: - res = cps_transaction(obj) - - if not res and cfg: - res.update({"response": curr_config['response']}) - else: - res.update({"cps_curr_config": curr_config['response']}) - RESULT.update(res) - - except Exception as e: - module.fail_json(msg=str(type(e).__name__) + ": " + str(e)) - - module.exit_json(**RESULT) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ordnance/ordnance_config.py b/plugins/modules/network/ordnance/ordnance_config.py deleted file mode 100644 index 62817ac905..0000000000 --- a/plugins/modules/network/ordnance/ordnance_config.py +++ /dev/null @@ -1,361 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ordnance_config -author: "Alexander Turner (@alexanderturner) " -short_description: Manage Ordnance configuration sections -description: - - Ordnance router configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with these configuration sections in - a deterministic way. -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. - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - 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. - 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. - 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. - 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. - default: line - choices: ['line', 'block'] - multiline_delimiter: - description: - - This argument is used when pushing a multiline configuration - element to the Ordnance router. It specifies the character to use - as the delimiting character. This only applies to the - configuration action - default: "@" - backup: - description: - - This argument will cause the module to create a full backup of - the current C(running-config) from the remote device before any - changes are made. The backup file is written to the C(backup) - folder in the playbook root directory. If the directory does not - exist, it is created. - type: bool - default: 'no' - config: - description: - - The C(config) argument allows the playbook designer to supply - the base configuration to be used to validate configuration - changes necessary. If this argument is provided, the module - will not download the running-config from the remote node. - defaults: - description: - - This argument specifies whether or not to collect all defaults - when getting the remote device running config. When enabled, - the module will get the current config by issuing the command - C(show running-config all). - type: bool - default: 'no' - 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' -''' - -EXAMPLES = """ ---- -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. -vars: - cli: - host: "{{ inventory_hostname }}" - username: RouterName - password: password - transport: cli - ---- -- name: configure top level configuration - ordnance_config: - lines: hostname {{ inventory_hostname }} - provider: "{{ cli }}" - -- name: configure interface settings - ordnance_config: - lines: - - description test interface - - ip address 172.31.1.1 255.255.255.0 - parents: interface Ethernet1 - provider: "{{ cli }}" - -- name: configure bgp router - ordnance_config: - lines: - - neighbor 1.1.1.1 remote-as 1234 - - network 10.0.0.0/24 - parents: router bgp 65001 - provider: "{{ cli }}" - -""" - -RETURN = """ -updates: - description: The set of commands that will be pushed to the remote device - returned: Only when commands is specified. - type: list - sample: ['...', '...'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/ordnance_config.2016-07-16@22:28:34 -""" -import re -import time -import traceback - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network import NetworkModule, NetworkError -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Command -from ansible_collections.community.general.plugins.module_utils.network.ordnance.ordnance import get_config -from ansible.module_utils.six import iteritems -from ansible.module_utils._text import to_native - - -def check_args(module, warnings): - if module.params['multiline_delimiter']: - if len(module.params['multiline_delimiter']) != 1: - module.fail_json(msg='multiline_delimiter value can only be a ' - 'single character') - if module.params['force']: - warnings.append('The force argument is deprecated, please use ' - 'match=none instead. This argument will be ' - 'removed in the future') - - -def extract_banners(config): - banners = {} - banner_cmds = re.findall(r'^banner (\w+)', config, re.M) - for cmd in banner_cmds: - regex = r'banner %s \^C(.+?)(?=\^C)' % cmd - match = re.search(regex, config, re.S) - if match: - key = 'banner %s' % cmd - banners[key] = match.group(1).strip() - - for cmd in banner_cmds: - regex = r'banner %s \^C(.+?)(?=\^C)' % cmd - match = re.search(regex, config, re.S) - if match: - config = config.replace(str(match.group(1)), '') - - config = re.sub(r'banner \w+ \^C\^C', '!! banner removed', config) - return (config, banners) - - -def diff_banners(want, have): - candidate = {} - for key, value in iteritems(want): - if value != have.get(key): - candidate[key] = value - return candidate - - -def load_banners(module, banners): - delimiter = module.params['multiline_delimiter'] - for key, value in iteritems(banners): - key += ' %s' % delimiter - for cmd in ['config terminal', key, value, delimiter, 'end']: - cmd += '\r' - module.connection.shell.shell.sendall(cmd) - time.sleep(1) - module.connection.shell.receive() - - -def get_config(module, result): - contents = module.params['config'] - if not contents: - defaults = module.params['defaults'] - contents = module.config.get_config(include_defaults=defaults) - - contents, banners = extract_banners(contents) - return NetworkConfig(indent=1, contents=contents), banners - - -def get_candidate(module): - candidate = NetworkConfig(indent=1) - banners = {} - - if module.params['src']: - src, banners = extract_banners(module.params['src']) - candidate.load(src) - - elif module.params['lines']: - parents = module.params['parents'] or list() - candidate.add(module.params['lines'], parents=parents) - - return candidate, banners - - -def run(module, result): - match = module.params['match'] - replace = module.params['replace'] - path = module.params['parents'] - - candidate, want_banners = get_candidate(module) - - if match != 'none': - config, have_banners = get_config(module, result) - path = module.params['parents'] - configobjs = candidate.difference(config, path=path, match=match, - replace=replace) - else: - configobjs = candidate.items - have_banners = {} - - banners = diff_banners(want_banners, have_banners) - - if configobjs or banners: - commands = dumps(configobjs, 'commands').split('\n') - - if module.params['lines']: - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['updates'] = commands - result['banners'] = banners - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - if commands: - module.config(commands) - if banners: - load_banners(module, banners) - - result['changed'] = True - - if module.params['save']: - if not module.check_mode: - module.config.save_config() - result['changed'] = True - - -def main(): - """ main entry point for module execution - """ - - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - multiline_delimiter=dict(default='@'), - - config=dict(), - defaults=dict(type='bool', default=False), - - backup=dict(type='bool', default=False), - save=dict(default=False, type='bool'), - ) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines'])] - - module = NetworkModule(argument_spec=argument_spec, - connect_on_load=False, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - if module.params['force'] is True: - module.params['match'] = 'none' - - warnings = list() - check_args(module, warnings) - - result = dict(changed=False, warnings=warnings) - - if module.params['backup']: - result['__backup__'] = module.config.get_config() - - try: - run(module, result) - except NetworkError as e: - module.disconnect() - module.fail_json(msg=to_native(e), exception=traceback.format_exc()) - - module.disconnect() - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/ordnance/ordnance_facts.py b/plugins/modules/network/ordnance/ordnance_facts.py deleted file mode 100644 index d3463b9e48..0000000000 --- a/plugins/modules/network/ordnance/ordnance_facts.py +++ /dev/null @@ -1,293 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: ordnance_facts -author: "Alexander Turner (@alexanderturner) " -short_description: Collect facts from Ordnance Virtual Routers over SSH -description: - - Collects a base set of device facts from an Ordnance Virtual - router over SSH. This module prepends all of the - base network fact keys with C(ansible_net_). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: '!config' -''' - -EXAMPLES = """ ---- -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. -vars: - cli: - host: "{{ inventory_hostname }}" - username: RouterName - password: ordnance - transport: cli - ---- -# Collect all facts from the device -- ordnance_facts: - gather_subset: all - provider: "{{ cli }}" - -# Collect only the config and default facts -- ordnance_facts: - gather_subset: - - config - provider: "{{ cli }}" - -# Do not collect hardware facts -- ordnance_facts: - gather_subset: - - "!hardware" - provider: "{{ cli }}" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the virtual router - returned: always - type: list - -# config -ansible_net_config: - description: The current active config from the virtual router - returned: when config is configured - type: str - -# interfaces -ansible_net_all_ipv4_addresses: - description: All IPv4 addresses configured on the virtual router - returned: when interfaces is configured - type: list -ansible_net_all_ipv6_addresses: - description: All IPv6 addresses configured on the virtual router - returned: when interfaces is configured - type: list -ansible_net_interfaces: - description: A hash of all interfaces running on the virtual router - returned: when interfaces is configured - type: dict -""" -import re -import traceback - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network import NetworkModule -from ansible.module_utils.six import iteritems -from ansible.module_utils.six.moves import zip -from ansible.module_utils._text import to_native - - -class FactsBase(object): - - def __init__(self, module): - self.module = module - self.facts = dict() - self.failed_commands = list() - - def run(self, cmd): - try: - return self.module.cli(cmd)[0] - except Exception: - self.failed_commands.append(cmd) - - -class Config(FactsBase): - - def populate(self): - data = self.run('show running-config') - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - def populate(self): - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - data = self.run('show interfaces') - if data: - interfaces = self.parse_interfaces(data) - self.facts['interfaces'] = self.populate_interfaces(interfaces) - - data = self.run('show ipv6 interface') - if data: - data = self.parse_interfaces(data) - self.populate_ipv6_interfaces(data) - - def populate_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - intf['description'] = self.parse_description(value) - intf['macaddress'] = self.parse_macaddress(value) - - ipv4 = self.parse_ipv4(value) - intf['ipv4'] = self.parse_ipv4(value) - if ipv4: - self.add_ip_address(ipv4['address'], 'ipv4') - - intf['duplex'] = self.parse_duplex(value) - intf['operstatus'] = self.parse_operstatus(value) - intf['type'] = self.parse_type(value) - - facts[key] = intf - return facts - - def populate_ipv6_interfaces(self, data): - for key, value in iteritems(data): - self.facts['interfaces'][key]['ipv6'] = list() - addresses = re.findall(r'\s+(.+), subnet', value, re.M) - subnets = re.findall(r', subnet is (.+)$', value, re.M) - for addr, subnet in zip(addresses, subnets): - ipv6 = dict(address=addr.strip(), subnet=subnet.strip()) - self.add_ip_address(addr.strip(), 'ipv6') - self.facts['interfaces'][key]['ipv6'].append(ipv6) - - 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_interfaces(self, data): - parsed = dict() - key = '' - for line in data.split('\n'): - if len(line) == 0: - continue - elif line[0] == ' ': - parsed[key] += '\n%s' % line - else: - match = re.match(r'^(\S+)', line) - if match: - key = match.group(1) - parsed[key] = line - return parsed - - def parse_description(self, data): - match = re.search(r'Description: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_macaddress(self, data): - match = re.search(r'address is (\S+)', data) - if match: - return match.group(1) - - def parse_ipv4(self, data): - match = re.search(r'Internet address is (\S+)', data) - if match: - addr, masklen = match.group(1).split('/') - return dict(address=addr, masklen=int(masklen)) - - def parse_duplex(self, data): - match = re.search(r'(\w+) Duplex', data, re.M) - if match: - return match.group(1) - - def parse_operstatus(self, data): - match = re.search(r'^(?:.+) is (.+),', data, re.M) - if match: - return match.group(1) - - -FACT_SUBSETS = dict( - interfaces=Interfaces, - config=Config, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - module = NetworkModule(argument_spec=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)) - - failed_commands = list() - - try: - for inst in instances: - inst.populate() - failed_commands.extend(inst.failed_commands) - facts.update(inst.facts) - except Exception as exc: - module.fail_json(msg=to_native(exc), exception=traceback.format_exc()) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - module.exit_json(ansible_facts=ansible_facts, failed_commands=failed_commands) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_admin.py b/plugins/modules/network/panos/panos_admin.py deleted file mode 100644 index 8198dc3bb8..0000000000 --- a/plugins/modules/network/panos/panos_admin.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: panos_admin -short_description: Add or modify PAN-OS user accounts password. -description: - - PanOS module that allows changes to the user account passwords by doing - API calls to the Firewall using pan-api as the protocol. -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - admin_username: - description: - - username for admin user - default: "admin" - admin_password: - description: - - password for admin user - required: true - role: - description: - - role for admin user - commit: - description: - - commit if changed - type: bool - default: 'yes' -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -# Set the password of user admin to "badpassword" -# Doesn't commit the candidate config - - name: set admin password - panos_admin: - ip_address: "192.168.1.1" - password: "admin" - admin_username: admin - admin_password: "badpassword" - commit: False -''' - -RETURN = ''' -status: - description: success status - returned: success - type: str - sample: "okey dokey" -''' -from ansible.module_utils.basic import AnsibleModule - -try: - import pan.xapi - HAS_LIB = True -except ImportError: - HAS_LIB = False - -_ADMIN_XPATH = "/config/mgt-config/users/entry[@name='%s']" - - -def admin_exists(xapi, admin_username): - xapi.get(_ADMIN_XPATH % admin_username) - e = xapi.element_root.find('.//entry') - return e - - -def admin_set(xapi, module, admin_username, admin_password, role): - if admin_password is not None: - xapi.op(cmd='request password-hash password "%s"' % admin_password, - cmd_xml=True) - r = xapi.element_root - phash = r.find('.//phash').text - if role is not None: - rbval = "yes" - if role != "superuser" and role != 'superreader': - rbval = "" - - ea = admin_exists(xapi, admin_username) - if ea is not None: - # user exists - changed = False - - if role is not None: - rb = ea.find('.//role-based') - if rb is not None: - if rb[0].tag != role: - changed = True - xpath = _ADMIN_XPATH % admin_username - xpath += '/permissions/role-based/%s' % rb[0].tag - xapi.delete(xpath=xpath) - - xpath = _ADMIN_XPATH % admin_username - xpath += '/permissions/role-based' - xapi.set(xpath=xpath, - element='<%s>%s' % (role, rbval, role)) - - if admin_password is not None: - xapi.edit(xpath=_ADMIN_XPATH % admin_username + '/phash', - element='%s' % phash) - changed = True - - return changed - - # setup the non encrypted part of the monitor - exml = [] - - exml.append('%s' % phash) - exml.append('<%s>%s' - '' % (role, rbval, role)) - - exml = ''.join(exml) - # module.fail_json(msg=exml) - - xapi.set(xpath=_ADMIN_XPATH % admin_username, element=exml) - - return True - - -def main(): - argument_spec = dict( - ip_address=dict(), - password=dict(no_log=True), - username=dict(default='admin'), - admin_username=dict(default='admin'), - admin_password=dict(no_log=True), - role=dict(), - commit=dict(type='bool', default=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - - if not HAS_LIB: - module.fail_json(msg='pan-python required for this module') - - ip_address = module.params["ip_address"] - if not ip_address: - module.fail_json(msg="ip_address should be specified") - password = module.params["password"] - if not password: - module.fail_json(msg="password is required") - username = module.params['username'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - admin_username = module.params['admin_username'] - if admin_username is None: - module.fail_json(msg="admin_username is required") - admin_password = module.params['admin_password'] - role = module.params['role'] - commit = module.params['commit'] - - changed = admin_set(xapi, module, admin_username, admin_password, role) - - if changed and commit: - xapi.commit(cmd="", sync=True, interval=1) - - module.exit_json(changed=changed, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_admpwd.py b/plugins/modules/network/panos/panos_admpwd.py deleted file mode 100644 index 3671bba3bd..0000000000 --- a/plugins/modules/network/panos/panos_admpwd.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_admpwd -short_description: change admin password of PAN-OS device using SSH with SSH key -description: - - Change the admin password of PAN-OS via SSH using a SSH key for authentication. - - Useful for AWS instances where the first login should be done via SSH. -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - paramiko -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device - required: true - username: - description: - - username for initial authentication - required: false - default: "admin" - key_filename: - description: - - filename of the SSH Key to use for authentication - required: true - newpassword: - description: - - password to configure for admin on the PAN-OS device - required: true -''' - -EXAMPLES = ''' -# Tries for 10 times to set the admin password of 192.168.1.1 to "badpassword" -# via SSH, authenticating using key /tmp/ssh.key -- name: set admin password - panos_admpwd: - ip_address: "192.168.1.1" - username: "admin" - key_filename: "/tmp/ssh.key" - newpassword: "badpassword" - register: result - until: result is not failed - retries: 10 - delay: 30 -''' - -RETURN = ''' -status: - description: success status - returned: success - type: str - sample: "Last login: Fri Sep 16 11:09:20 2016 from 10.35.34.56.....Configuration committed successfully" -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.compat.paramiko import paramiko -import time -import sys - -_PROMPTBUFF = 4096 - - -def wait_with_timeout(module, shell, prompt, timeout=60): - now = time.time() - result = "" - while True: - if shell.recv_ready(): - result += shell.recv(_PROMPTBUFF) - endresult = result.strip() - if len(endresult) != 0 and endresult[-1] == prompt: - break - - if time.time() - now > timeout: - module.fail_json(msg="Timeout waiting for prompt") - - return result - - -def set_panwfw_password(module, ip_address, key_filename, newpassword, username): - stdout = "" - - ssh = paramiko.SSHClient() - - # add policy to accept all host keys, I haven't found - # a way to retrieve the instance SSH key fingerprint from AWS - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - ssh.connect(ip_address, username=username, key_filename=key_filename) - shell = ssh.invoke_shell() - - # wait for the shell to start - buff = wait_with_timeout(module, shell, ">") - stdout += buff - - # step into config mode - shell.send('configure\n') - # wait for the config prompt - buff = wait_with_timeout(module, shell, "#") - stdout += buff - - if module.check_mode: - # exit and close connection - shell.send('exit\n') - ssh.close() - return False, 'Connection test successful. Password left intact.' - - # set admin password - shell.send('set mgt-config users ' + username + ' password\n') - - # wait for the password prompt - buff = wait_with_timeout(module, shell, ":") - stdout += buff - - # enter password for the first time - shell.send(newpassword + '\n') - - # wait for the password prompt - buff = wait_with_timeout(module, shell, ":") - stdout += buff - - # enter password for the second time - shell.send(newpassword + '\n') - - # wait for the config mode prompt - buff = wait_with_timeout(module, shell, "#") - stdout += buff - - # commit ! - shell.send('commit\n') - - # wait for the prompt - buff = wait_with_timeout(module, shell, "#", 120) - stdout += buff - - if 'success' not in buff: - module.fail_json(msg="Error setting " + username + " password: " + stdout) - - # exit - shell.send('exit\n') - - ssh.close() - - return True, stdout - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - username=dict(default='admin'), - key_filename=dict(required=True), - newpassword=dict(no_log=True, required=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - if paramiko is None: - module.fail_json(msg='paramiko is required for this module') - - ip_address = module.params["ip_address"] - if not ip_address: - module.fail_json(msg="ip_address should be specified") - key_filename = module.params["key_filename"] - if not key_filename: - module.fail_json(msg="key_filename should be specified") - newpassword = module.params["newpassword"] - if not newpassword: - module.fail_json(msg="newpassword is required") - username = module.params['username'] - - try: - changed, stdout = set_panwfw_password(module, ip_address, key_filename, newpassword, username) - module.exit_json(changed=changed, stdout=stdout) - except Exception: - x = sys.exc_info()[1] - module.fail_json(msg=x) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_cert_gen_ssh.py b/plugins/modules/network/panos/panos_cert_gen_ssh.py deleted file mode 100644 index 9c204d1a7b..0000000000 --- a/plugins/modules/network/panos/panos_cert_gen_ssh.py +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_cert_gen_ssh -short_description: generates a self-signed certificate using SSH protocol with SSH key -description: - - This module generates a self-signed certificate that can be used by GlobalProtect client, SSL connector, or - - otherwise. Root certificate must be preset on the system first. This module depends on paramiko for ssh. -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - paramiko -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is not supported. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device being configured. - required: true - key_filename: - description: - - Location of the filename that is used for the auth. Either I(key_filename) or I(password) is required. - required: true - password: - description: - - Password credentials to use for auth. Either I(key_filename) or I(password) is required. - required: true - cert_friendly_name: - description: - - Human friendly certificate name (not CN but just a friendly name). - required: true - cert_cn: - description: - - Certificate CN (common name) embedded in the certificate signature. - required: true - signed_by: - description: - - Undersigning authority (CA) that MUST already be presents on the device. - required: true - rsa_nbits: - description: - - Number of bits used by the RSA algorithm for the certificate generation. - default: "2048" -''' - -EXAMPLES = ''' -# Generates a new self-signed certificate using ssh -- name: generate self signed certificate - panos_cert_gen_ssh: - ip_address: "192.168.1.1" - password: "paloalto" - cert_cn: "1.1.1.1" - cert_friendly_name: "test123" - signed_by: "root-ca" -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils._text import to_native -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.compat.paramiko import paramiko -import time - -_PROMPTBUFF = 4096 - - -def wait_with_timeout(module, shell, prompt, timeout=60): - now = time.time() - result = "" - while True: - if shell.recv_ready(): - result += shell.recv(_PROMPTBUFF) - endresult = result.strip() - if len(endresult) != 0 and endresult[-1] == prompt: - break - - if time.time() - now > timeout: - module.fail_json(msg="Timeout waiting for prompt") - - return result - - -def generate_cert(module, ip_address, key_filename, password, - cert_cn, cert_friendly_name, signed_by, rsa_nbits): - stdout = "" - - client = paramiko.SSHClient() - - # add policy to accept all host keys, I haven't found - # a way to retrieve the instance SSH key fingerprint from AWS - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - if not key_filename: - client.connect(ip_address, username="admin", password=password) - else: - client.connect(ip_address, username="admin", key_filename=key_filename) - - shell = client.invoke_shell() - # wait for the shell to start - buff = wait_with_timeout(module, shell, ">") - stdout += buff - - # generate self-signed certificate - if isinstance(cert_cn, list): - cert_cn = cert_cn[0] - cmd = 'request certificate generate signed-by {0} certificate-name {1} name {2} algorithm RSA rsa-nbits {3}\n'.format( - signed_by, cert_friendly_name, cert_cn, rsa_nbits) - shell.send(cmd) - - # wait for the shell to complete - buff = wait_with_timeout(module, shell, ">") - stdout += buff - - # exit - shell.send('exit\n') - - if 'Success' not in buff: - module.fail_json(msg="Error generating self signed certificate: " + stdout) - - client.close() - return stdout - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - key_filename=dict(), - password=dict(no_log=True), - cert_cn=dict(required=True), - cert_friendly_name=dict(required=True), - rsa_nbits=dict(default='2048'), - signed_by=dict(required=True) - - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - required_one_of=[['key_filename', 'password']]) - if paramiko is None: - module.fail_json(msg='paramiko is required for this module') - - ip_address = module.params["ip_address"] - key_filename = module.params["key_filename"] - password = module.params["password"] - cert_cn = module.params["cert_cn"] - cert_friendly_name = module.params["cert_friendly_name"] - signed_by = module.params["signed_by"] - rsa_nbits = module.params["rsa_nbits"] - - try: - stdout = generate_cert(module, - ip_address, - key_filename, - password, - cert_cn, - cert_friendly_name, - signed_by, - rsa_nbits) - except Exception as exc: - module.fail_json(msg=to_native(exc)) - - module.exit_json(changed=True, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_check.py b/plugins/modules/network/panos/panos_check.py deleted file mode 100644 index c7a8fa6e79..0000000000 --- a/plugins/modules/network/panos/panos_check.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_check -short_description: check if PAN-OS device is ready for configuration -description: - - Check if PAN-OS device is ready for being configured (no pending jobs). - - The check could be done once or multiple times until the device is ready. -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - timeout: - description: - - timeout of API calls - required: false - default: 0 - interval: - description: - - time waited between checks - required: false - default: 0 -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -# single check on 192.168.1.1 with credentials admin/admin -- name: check if ready - panos_check: - ip_address: "192.168.1.1" - password: "admin" - -# check for 10 times, every 30 seconds, if device 192.168.1.1 -# is ready, using credentials admin/admin -- name: wait for reboot - panos_check: - ip_address: "192.168.1.1" - password: "admin" - register: result - until: result is not failed - retries: 10 - delay: 30 -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils.basic import AnsibleModule -import time - -try: - import pan.xapi - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def check_jobs(jobs, module): - job_check = False - for j in jobs: - status = j.find('.//status') - if status is None: - return False - if status.text != 'FIN': - return False - job_check = True - return job_check - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - timeout=dict(default=0, type='int'), - interval=dict(default=0, type='int') - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - timeout = module.params['timeout'] - interval = module.params['interval'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password, - timeout=60 - ) - - checkpnt = time.time() + timeout - while True: - try: - xapi.op(cmd="show jobs all", cmd_xml=True) - except Exception: - pass - else: - jobs = xapi.element_root.findall('.//job') - if check_jobs(jobs, module): - module.exit_json(changed=True, msg="okey dokey") - - if time.time() > checkpnt: - break - - time.sleep(interval) - - module.fail_json(msg="Timeout") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_commit.py b/plugins/modules/network/panos/panos_commit.py deleted file mode 100644 index 3d94c045a8..0000000000 --- a/plugins/modules/network/panos/panos_commit.py +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2019, Tomi Raittinen -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_commit -short_description: commit firewall's candidate configuration -description: - - PanOS module that will commit firewall's candidate configuration on - - the device. The new configuration will become active immediately. -author: - - Luigi Mori (@jtschichold) - - Ivan Bojer (@ivanbojer) - - Tomi Raittinen (@traittinen) -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device. - required: true - password: - description: - - Password for authentication. If the value is not specified in the - task, the value of environment variable C(ANSIBLE_NET_PASSWORD) - will be used instead. - required: true - username: - description: - - Username for authentication. If the value is not specified in the - task, the value of environment variable C(ANSIBLE_NET_USERNAME) - will be used instead if defined. C(admin) will be used if nothing - above is defined. - default: admin - interval: - description: - - interval for checking commit job - default: 0.5 - timeout: - description: - - timeout for commit job - sync: - description: - - if commit should be synchronous - type: bool - default: 'yes' - description: - description: - - Commit description/comment - type: str - commit_changes_by: - description: - - Commit changes made by specified admin - type: list - commit_vsys: - description: - - Commit changes for specified VSYS - type: list -''' - -EXAMPLES = ''' -# Commit candidate config on 192.168.1.1 in sync mode -- panos_commit: - ip_address: "192.168.1.1" - username: "admin" - password: "admin" -''' - -RETURN = ''' -panos_commit: - description: Information about commit job. - returned: always - type: complex - version_added: 2.8 - contains: - job_id: - description: Palo Alto job ID for the commit operation. Only returned if commit job is launched on device. - returned: always - type: str - sample: "139" - status_code: - description: Palo Alto API status code. Null if commit is successful. - returned: always - type: str - sample: 19 - status_detail: - description: Palo Alto API detailed status message. - returned: always - type: str - sample: Configuration committed successfully - status_text: - description: Palo Alto API status text. - returned: always - type: str - sample: success -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils.basic import AnsibleModule, env_fallback -import xml.etree.ElementTree as etree - -try: - import pan.xapi - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def main(): - argument_spec = dict( - ip_address=dict(required=True, type='str'), - password=dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True), - username=dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME']), default="admin"), - interval=dict(default=0.5), - timeout=dict(), - sync=dict(type='bool', default=True), - description=dict(type='str'), - commit_changes_by=dict(type='list'), - commit_vsys=dict(type='list') - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - if not ip_address: - module.fail_json(msg="ip_address should be specified") - - password = module.params["password"] - if not password: - module.fail_json(msg="password is required") - - username = module.params['username'] - if not username: - module.fail_json(msg="username is required") - - interval = module.params['interval'] - timeout = module.params['timeout'] - sync = module.params['sync'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - cmd = "" - - description = module.params["description"] - if description: - cmd += "" + description + "" - - commit_changes_by = module.params["commit_changes_by"] - commit_vsys = module.params["commit_vsys"] - - if commit_changes_by or commit_vsys: - - cmd += "" - - if commit_changes_by: - cmd += "" - for admin in commit_changes_by: - cmd += "" + admin + "" - cmd += "" - - if commit_vsys: - cmd += "" - for vsys in commit_vsys: - cmd += "" + vsys + "" - cmd += "" - - cmd += "" - - cmd += "" - - xapi.commit( - cmd=cmd, - sync=sync, - interval=interval, - timeout=timeout - ) - - try: - result = xapi.xml_root().encode('utf-8') - root = etree.fromstring(result) - job_id = root.find('./result/job/id').text - except AttributeError: - job_id = None - - panos_commit_details = dict( - status_text=xapi.status, - status_code=xapi.status_code, - status_detail=xapi.status_detail, - job_id=job_id - ) - - if "Commit failed" in xapi.status_detail: - module.fail_json(msg=xapi.status_detail, panos_commit=panos_commit_details) - - if job_id: - module.exit_json(changed=True, msg="Commit successful.", panos_commit=panos_commit_details) - else: - module.exit_json(changed=False, msg="No changes to commit.", panos_commit=panos_commit_details) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_dag.py b/plugins/modules/network/panos/panos_dag.py deleted file mode 100644 index 6e9e57d53b..0000000000 --- a/plugins/modules/network/panos/panos_dag.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_dag -short_description: create a dynamic address group -description: - - Create a dynamic address group object in the firewall used for policy rules -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - dag_name: - description: - - name of the dynamic address group - required: true - dag_filter: - description: - - dynamic filter user by the dynamic address group - required: true - commit: - description: - - commit if changed - type: bool - default: 'yes' -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -- name: dag - panos_dag: - ip_address: "192.168.1.1" - password: "admin" - dag_name: "dag-1" - dag_filter: "'aws-tag.aws:cloudformation:logical-id.ServerInstance' and 'instanceState.running'" -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils.basic import AnsibleModule - -try: - import pan.xapi - HAS_LIB = True -except ImportError: - HAS_LIB = False - -_ADDRGROUP_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\ - "/vsys/entry[@name='vsys1']/address-group/entry[@name='%s']" - - -def addressgroup_exists(xapi, group_name): - xapi.get(_ADDRGROUP_XPATH % group_name) - e = xapi.element_root.find('.//entry') - if e is None: - return False - return True - - -def add_dag(xapi, dag_name, dag_filter): - if addressgroup_exists(xapi, dag_name): - return False - - # setup the non encrypted part of the monitor - exml = [] - - exml.append('') - exml.append('%s' % dag_filter) - exml.append('') - - exml = ''.join(exml) - xapi.set(xpath=_ADDRGROUP_XPATH % dag_name, element=exml) - - return True - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - dag_name=dict(required=True), - dag_filter=dict(required=True), - commit=dict(type='bool', default=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - dag_name = module.params['dag_name'] - dag_filter = module.params['dag_filter'] - commit = module.params['commit'] - - changed = add_dag(xapi, dag_name, dag_filter) - - if changed and commit: - xapi.commit(cmd="", sync=True, interval=1) - - module.exit_json(changed=changed, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_dag_tags.py b/plugins/modules/network/panos/panos_dag_tags.py deleted file mode 100644 index e706def9f4..0000000000 --- a/plugins/modules/network/panos/panos_dag_tags.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . -# limitations under the License. - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: panos_dag_tags -short_description: Create tags for DAG's on PAN-OS devices. -description: - - Create the ip address to tag associations. Tags will in turn be used to create DAG's -author: "Vinay Venkataraghavan (@vinayvenkat)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) - - pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is not supported. - - Panorama is not supported. -options: - api_key: - description: - - API key that can be used instead of I(username)/I(password) credentials. - description: - description: - - The purpose / objective of the static Address Group - commit: - description: - - commit if changed - default: true - type: bool - devicegroup: - description: > - - Device groups are used for the Panorama interaction with Firewall(s). The group must exists on Panorama. - If device group is not define we assume that we are contacting Firewall. - operation: - description: - - The action to be taken. Supported values are I(add)/I(update)/I(find)/I(delete). - tag_names: - description: - - The list of the tags that will be added or removed from the IP address. - ip_to_register: - description: - - IP that will be registered with the given tag names. -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -- name: Create the tags to map IP addresses - panos_dag_tags: - ip_address: "{{ ip_address }}" - password: "{{ password }}" - ip_to_register: "{{ ip_to_register }}" - tag_names: "{{ tag_names }}" - description: "Tags to allow certain IP's to access various SaaS Applications" - operation: 'add' - tags: "adddagip" - -- name: List the IP address to tag mapping - panos_dag_tags: - ip_address: "{{ ip_address }}" - password: "{{ password }}" - tag_names: "{{ tag_names }}" - description: "List the IP address to tag mapping" - operation: 'list' - tags: "listdagip" - -- name: Unregister an IP address from a tag mapping - panos_dag_tags: - ip_address: "{{ ip_address }}" - password: "{{ password }}" - ip_to_register: "{{ ip_to_register }}" - tag_names: "{{ tag_names }}" - description: "Unregister IP address from tag mappings" - operation: 'delete' - tags: "deletedagip" -''' - -RETURN = ''' -# Default return values -''' - -try: - from pandevice import base - from pandevice import firewall - from pandevice import panorama - from pandevice import objects - - from pan.xapi import PanXapiError - - HAS_LIB = True -except ImportError: - HAS_LIB = False - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - - -def get_devicegroup(device, devicegroup): - dg_list = device.refresh_devices() - for group in dg_list: - if isinstance(group, panorama.DeviceGroup): - if group.name == devicegroup: - return group - return False - - -def register_ip_to_tag_map(device, ip_addresses, tag): - exc = None - try: - device.userid.register(ip_addresses, tag) - except PanXapiError as exc: - return False, exc - - return True, exc - - -def get_all_address_group_mapping(device): - exc = None - ret = None - try: - ret = device.userid.get_registered_ip() - except PanXapiError as exc: - return False, exc - - return ret, exc - - -def delete_address_from_mapping(device, ip_address, tags): - exc = None - try: - ret = device.userid.unregister(ip_address, tags) - except PanXapiError as exc: - return False, exc - - return True, exc - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - api_key=dict(no_log=True), - devicegroup=dict(default=None), - description=dict(default=None), - ip_to_register=dict(type='str', required=False), - tag_names=dict(type='list', required=True), - commit=dict(type='bool', default=True), - operation=dict(type='str', required=True) - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - api_key = module.params['api_key'] - commit = module.params['commit'] - devicegroup = module.params['devicegroup'] - operation = module.params['operation'] - - # Create the device with the appropriate pandevice type - device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key) - - # If Panorama, validate the devicegroup - dev_group = None - if devicegroup and isinstance(device, panorama.Panorama): - dev_group = get_devicegroup(device, devicegroup) - if dev_group: - device.add(dev_group) - else: - module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup) - - result = None - if operation == 'add': - result, exc = register_ip_to_tag_map(device, - ip_addresses=module.params.get('ip_to_register', None), - tag=module.params.get('tag_names', None) - ) - elif operation == 'list': - result, exc = get_all_address_group_mapping(device) - elif operation == 'delete': - result, exc = delete_address_from_mapping(device, - ip_address=module.params.get('ip_to_register', None), - tags=module.params.get('tag_names', []) - ) - else: - module.fail_json(msg="Unsupported option") - - if not result: - module.fail_json(msg=exc.message) - - if commit: - try: - device.commit(sync=True) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - - module.exit_json(changed=True, msg=result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/network/panos/panos_import.py b/plugins/modules/network/panos/panos_import.py deleted file mode 100644 index 19d3ee9b20..0000000000 --- a/plugins/modules/network/panos/panos_import.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_import -short_description: import file on PAN-OS devices -description: - - Import file on PAN-OS device -notes: - - API reference documentation can be read from the C(/api/) directory of your appliance - - Certificate validation is enabled by default as of Ansible 2.6. This may break existing playbooks but should be disabled with caution. -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python - - requests - - requests_toolbelt -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - category: - description: - - Category of file uploaded. The default is software. - - See API > Import section of the API reference for category options. - default: software - file: - description: - - Location of the file to import into device. - url: - description: - - URL of the file that will be imported to device. - validate_certs: - description: - - If C(no), SSL certificates will not be validated. Disabling certificate validation is not recommended. - default: yes - type: bool -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -# import software image PanOS_vm-6.1.1 on 192.168.1.1 -- name: import software image into PAN-OS - panos_import: - ip_address: 192.168.1.1 - username: admin - password: admin - file: /tmp/PanOS_vm-6.1.1 - category: software -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - -import os.path -import xml.etree -import tempfile -import shutil -import os - -try: - import pan.xapi - import requests - import requests_toolbelt - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def import_file(xapi, module, ip_address, file_, category): - xapi.keygen() - - params = { - 'type': 'import', - 'category': category, - 'key': xapi.api_key - } - - filename = os.path.basename(file_) - - mef = requests_toolbelt.MultipartEncoder( - fields={ - 'file': (filename, open(file_, 'rb'), 'application/octet-stream') - } - ) - - r = requests.post( - 'https://' + ip_address + '/api/', - verify=module.params['validate_certs'], - params=params, - headers={'Content-Type': mef.content_type}, - data=mef - ) - - # if something goes wrong just raise an exception - r.raise_for_status() - - resp = xml.etree.ElementTree.fromstring(r.content) - - if resp.attrib['status'] == 'error': - module.fail_json(msg=r.content) - - return True, filename - - -def download_file(url): - r = requests.get(url, stream=True) - fo = tempfile.NamedTemporaryFile(prefix='ai', delete=False) - shutil.copyfileobj(r.raw, fo) - fo.close() - - return fo.name - - -def delete_file(path): - os.remove(path) - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - category=dict(default='software'), - file=dict(), - url=dict(), - validate_certs=dict(type='bool', default=True), - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, required_one_of=[['file', 'url']]) - if not HAS_LIB: - module.fail_json(msg='pan-python, requests, and requests_toolbelt are required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - file_ = module.params['file'] - url = module.params['url'] - - category = module.params['category'] - - # we can get file from URL or local storage - if url is not None: - file_ = download_file(url) - - try: - changed, filename = import_file(xapi, module, ip_address, file_, category) - except Exception as exc: - module.fail_json(msg=to_native(exc)) - - # cleanup and delete file if local - if url is not None: - delete_file(file_) - - module.exit_json(changed=changed, filename=filename, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_interface.py b/plugins/modules/network/panos/panos_interface.py deleted file mode 100644 index 3460bcb8f4..0000000000 --- a/plugins/modules/network/panos/panos_interface.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: panos_interface -short_description: configure data-port network interface for DHCP -description: - - Configure data-port (DP) network interface for DHCP. By default DP interfaces are static. -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is not supported. -options: - if_name: - description: - - Name of the interface to configure. - required: true - zone_name: - description: > - Name of the zone for the interface. If the zone does not exist it is created but if the zone exists and - it is not of the layer3 type the operation will fail. - required: true - create_default_route: - description: - - Whether or not to add default route with router learned via DHCP. - default: "false" - type: bool - commit: - description: - - Commit if changed - default: true - type: bool -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -- name: enable DHCP client on ethernet1/1 in zone public - interface: - password: "admin" - ip_address: "192.168.1.1" - if_name: "ethernet1/1" - zone_name: "public" - create_default_route: "yes" -''' - -RETURN = ''' -# Default return values -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - -try: - import pan.xapi - from pan.xapi import PanXapiError - HAS_LIB = True -except ImportError: - HAS_LIB = False - -_IF_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\ - "/network/interface/ethernet/entry[@name='%s']" - -_ZONE_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\ - "/vsys/entry/zone/entry" -_ZONE_XPATH_QUERY = _ZONE_XPATH + "[network/layer3/member/text()='%s']" -_ZONE_XPATH_IF = _ZONE_XPATH + "[@name='%s']/network/layer3/member[text()='%s']" -_VR_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\ - "/network/virtual-router/entry" - - -def add_dhcp_if(xapi, if_name, zone_name, create_default_route): - if_xml = [ - '', - '', - '', - '%s', - '' - '' - '' - ] - cdr = 'yes' - if not create_default_route: - cdr = 'no' - if_xml = (''.join(if_xml)) % (if_name, cdr) - xapi.edit(xpath=_IF_XPATH % if_name, element=if_xml) - - xapi.set(xpath=_ZONE_XPATH + "[@name='%s']/network/layer3" % zone_name, - element='%s' % if_name) - xapi.set(xpath=_VR_XPATH + "[@name='default']/interface", - element='%s' % if_name) - - return True - - -def if_exists(xapi, if_name): - xpath = _IF_XPATH % if_name - xapi.get(xpath=xpath) - network = xapi.element_root.find('.//layer3') - return (network is not None) - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - if_name=dict(required=True), - zone_name=dict(required=True), - create_default_route=dict(type='bool', default=False), - commit=dict(type='bool', default=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - if_name = module.params['if_name'] - zone_name = module.params['zone_name'] - create_default_route = module.params['create_default_route'] - commit = module.params['commit'] - - ifexists = if_exists(xapi, if_name) - - if ifexists: - module.exit_json(changed=False, msg="interface exists, not changed") - - try: - changed = add_dhcp_if(xapi, if_name, zone_name, create_default_route) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - - if changed and commit: - xapi.commit(cmd="", sync=True, interval=1) - - module.exit_json(changed=changed, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_lic.py b/plugins/modules/network/panos/panos_lic.py deleted file mode 100644 index 9e37fd8cd3..0000000000 --- a/plugins/modules/network/panos/panos_lic.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_lic -short_description: apply authcode to a device/instance -description: - - Apply an authcode to a device. - - The authcode should have been previously registered on the Palo Alto Networks support portal. - - The device should have Internet access. -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - auth_code: - description: - - authcode to be applied - required: true - force: - description: - - whether to apply authcode even if device is already licensed - required: false - default: "false" - type: bool -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' - - hosts: localhost - connection: local - tasks: - - name: fetch license - panos_lic: - ip_address: "192.168.1.1" - password: "paloalto" - auth_code: "IBADCODE" - register: result - - name: Display serialnumber (if already registered) - debug: - var: "{{result.serialnumber}}" -''' - -RETURN = ''' -serialnumber: - description: serialnumber of the device in case that it has been already registered - returned: success - type: str - sample: 007200004214 -''' - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils.basic import AnsibleModule - -try: - import pan.xapi - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def get_serial(xapi, module): - xapi.op(cmd="show system info", cmd_xml=True) - r = xapi.element_root - serial = r.find('.//serial') - if serial is None: - module.fail_json(msg="No tag in show system info") - - serial = serial.text - - return serial - - -def apply_authcode(xapi, module, auth_code): - try: - xapi.op(cmd='request license fetch auth-code "%s"' % auth_code, - cmd_xml=True) - except pan.xapi.PanXapiError: - if hasattr(xapi, 'xml_document'): - if 'Successfully' in xapi.xml_document: - return - - if 'Invalid Auth Code' in xapi.xml_document: - module.fail_json(msg="Invalid Auth Code") - - raise - - return - - -def fetch_authcode(xapi, module): - try: - xapi.op(cmd='request license fetch', cmd_xml=True) - except pan.xapi.PanXapiError: - if hasattr(xapi, 'xml_document'): - if 'Successfully' in xapi.xml_document: - return - - if 'Invalid Auth Code' in xapi.xml_document: - module.fail_json(msg="Invalid Auth Code") - - raise - - return - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - auth_code=dict(), - username=dict(default='admin'), - force=dict(type='bool', default=False) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - auth_code = module.params["auth_code"] - force = module.params['force'] - username = module.params['username'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - if not force: - serialnumber = get_serial(xapi, module) - if serialnumber != 'unknown': - return module.exit_json(changed=False, serialnumber=serialnumber) - if auth_code: - apply_authcode(xapi, module, auth_code) - else: - fetch_authcode(xapi, module) - - module.exit_json(changed=True, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_loadcfg.py b/plugins/modules/network/panos/panos_loadcfg.py deleted file mode 100644 index b9ab48e680..0000000000 --- a/plugins/modules/network/panos/panos_loadcfg.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_loadcfg -short_description: load configuration on PAN-OS device -description: - - Load configuration on PAN-OS device -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - file: - description: - - configuration file to load - commit: - description: - - commit if changed - type: bool - default: 'yes' -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -# Import and load config file from URL - - name: import configuration - panos_import: - ip_address: "192.168.1.1" - password: "admin" - url: "{{ConfigURL}}" - category: "configuration" - register: result - - name: load configuration - panos_loadcfg: - ip_address: "192.168.1.1" - password: "admin" - file: "{{result.filename}}" -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils.basic import AnsibleModule - -try: - import pan.xapi - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def load_cfgfile(xapi, module, ip_address, file_): - # load configuration file - cmd = '%s' %\ - file_ - - xapi.op(cmd=cmd) - - return True - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - file=dict(), - commit=dict(type='bool', default=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - file_ = module.params['file'] - commit = module.params['commit'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - changed = load_cfgfile(xapi, module, ip_address, file_) - if changed and commit: - xapi.commit(cmd="", sync=True, interval=1) - - module.exit_json(changed=changed, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_match_rule.py b/plugins/modules/network/panos/panos_match_rule.py deleted file mode 100644 index 782794259e..0000000000 --- a/plugins/modules/network/panos/panos_match_rule.py +++ /dev/null @@ -1,389 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . -# limitations under the License. - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: panos_match_rule -short_description: Test for match against a security rule on PAN-OS devices or Panorama management console. -description: - - Security policies allow you to enforce rules and take action, and can be as general or specific as needed. -author: "Robert Hagen (@rnh556)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) - - pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is not supported. - - Panorama NOT is supported. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device being configured. - required: true - username: - description: - - Username credentials to use for auth unless I(api_key) is set. - default: "admin" - password: - description: - - Password credentials to use for auth unless I(api_key) is set. - required: true - api_key: - description: - - API key that can be used instead of I(username)/I(password) credentials. - rule_type: - description: - - Type of rule. Valid types are I(security) or I(nat). - required: true - choices: - - security - - nat - source_zone: - description: - - The source zone. - source_ip: - description: - - The source IP address. - required: true - source_port: - description: - - The source port. - source_user: - description: - - The source user or group. - to_interface: - description: - - The inbound interface in a NAT rule. - destination_zone: - description: - - The destination zone. - destination_ip: - description: - - The destination IP address. - destination_port: - description: - - The destination port. - application: - description: - - The application. - protocol: - description: - - The IP protocol number from 1 to 255. - category: - description: - - URL category - vsys_id: - description: - - ID of the VSYS object. - default: "vsys1" - required: true -''' - -EXAMPLES = ''' -- name: check security rules for Google DNS - panos_match_rule: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - rule_type: 'security' - source_ip: '10.0.0.0' - destination_ip: '8.8.8.8' - application: 'dns' - destination_port: '53' - protocol: '17' - register: result -- debug: msg='{{result.stdout_lines}}' - -- name: check security rules inbound SSH with user match - panos_match_rule: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - rule_type: 'security' - source_ip: '0.0.0.0' - source_user: 'mydomain\\jsmith' - destination_ip: '192.168.100.115' - destination_port: '22' - protocol: '6' - register: result -- debug: msg='{{result.stdout_lines}}' - -- name: check NAT rules for source NAT - panos_match_rule: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - rule_type: 'nat' - source_zone: 'Prod-DMZ' - source_ip: '10.10.118.50' - to_interface: 'ethernet1/2' - destination_zone: 'Internet' - destination_ip: '0.0.0.0' - protocol: '6' - register: result -- debug: msg='{{result.stdout_lines}}' - -- name: check NAT rules for inbound web - panos_match_rule: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - rule_type: 'nat' - source_zone: 'Internet' - source_ip: '0.0.0.0' - to_interface: 'ethernet1/1' - destination_zone: 'Prod DMZ' - destination_ip: '192.168.118.50' - destination_port: '80' - protocol: '6' - register: result -- debug: msg='{{result.stdout_lines}}' - -- name: check security rules for outbound POP3 in vsys4 - panos_match_rule: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - vsys_id: 'vsys4' - rule_type: 'security' - source_ip: '10.0.0.0' - destination_ip: '4.3.2.1' - application: 'pop3' - destination_port: '110' - protocol: '6' - register: result -- debug: msg='{{result.stdout_lines}}' - -''' - -RETURN = ''' -# Default return values -''' - -from ansible.module_utils.basic import AnsibleModule - -try: - from pan.xapi import PanXapiError - from pan.xapi import PanXapiError - from pandevice import base - from pandevice import policies - from pandevice import panorama - import xmltodict - import json - - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def create_security_test(**kwargs): - security_test = 'test security-policy-match' - - # Add the source IP (required) - if kwargs['source_ip']: - security_test += ' source \"%s\"' % kwargs['source_ip'] - - # Add the source user (optional) - if kwargs['source_user']: - security_test += ' source-user \"%s\"' % kwargs['source_user'] - - # Add the destination IP (required) - if kwargs['destination_ip']: - security_test += ' destination \"%s\"' % kwargs['destination_ip'] - - # Add the application (optional) - if kwargs['application']: - security_test += ' application \"%s\"' % kwargs['application'] - - # Add the destination port (required) - if kwargs['destination_port']: - security_test += ' destination-port \"%s\"' % kwargs['destination_port'] - - # Add the IP protocol number (required) - if kwargs['protocol']: - security_test += ' protocol \"%s\"' % kwargs['protocol'] - - # Add the URL category (optional) - if kwargs['category']: - security_test += ' category \"%s\"' % kwargs['category'] - - # Return the resulting string - return security_test - - -def create_nat_test(**kwargs): - nat_test = 'test nat-policy-match' - - # Add the source zone (optional) - if kwargs['source_zone']: - nat_test += ' from \"%s\"' % kwargs['source_zone'] - - # Add the source IP (required) - if kwargs['source_ip']: - nat_test += ' source \"%s\"' % kwargs['source_ip'] - - # Add the source user (optional) - if kwargs['source_port']: - nat_test += ' source-port \"%s\"' % kwargs['source_port'] - - # Add inbound interface (optional) - if kwargs['to_interface']: - nat_test += ' to-interface \"%s\"' % kwargs['to_interface'] - - # Add the destination zone (optional) - if kwargs['destination_zone']: - nat_test += ' to \"%s\"' % kwargs['destination_zone'] - - # Add the destination IP (required) - if kwargs['destination_ip']: - nat_test += ' destination \"%s\"' % kwargs['destination_ip'] - - # Add the destination port (optional) - if kwargs['destination_port']: - nat_test += ' destination-port \"%s\"' % kwargs['destination_port'] - - # Add the IP protocol number (required) - if kwargs['protocol']: - nat_test += ' protocol \"%s\"' % kwargs['protocol'] - - # Return the resulting string - return nat_test - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(no_log=True), - username=dict(default='admin'), - api_key=dict(no_log=True), - vsys_id=dict(default='vsys1'), - rule_type=dict(required=True, choices=['security', 'nat']), - source_zone=dict(default=None), - source_ip=dict(default=None), - source_user=dict(default=None), - source_port=dict(default=None, type=int), - to_interface=dict(default=None), - destination_zone=dict(default=None), - category=dict(default=None), - application=dict(default=None), - protocol=dict(default=None, type=int), - destination_ip=dict(default=None), - destination_port=dict(default=None, type=int) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - required_one_of=[['api_key', 'password']]) - if not HAS_LIB: - module.fail_json(msg='Missing required libraries.') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - api_key = module.params['api_key'] - vsys_id = module.params['vsys_id'] - rule_type = module.params['rule_type'] - source_zone = module.params['source_zone'] - source_ip = module.params['source_ip'] - source_user = module.params['source_user'] - source_port = module.params['source_port'] - to_interface = module.params['to_interface'] - destination_zone = module.params['destination_zone'] - destination_ip = module.params['destination_ip'] - destination_port = module.params['destination_port'] - category = module.params['category'] - application = module.params['application'] - protocol = module.params['protocol'] - - # Create the device with the appropriate pandevice type - device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key) - - # Fail the module if this is a Panorama instance - if isinstance(device, panorama.Panorama): - module.fail_json( - failed=1, - msg='Panorama is not supported.' - ) - - # Create and attach security and NAT rulebases. Then populate them. - sec_rule_base = nat_rule_base = policies.Rulebase() - device.add(sec_rule_base) - device.add(nat_rule_base) - policies.SecurityRule.refreshall(sec_rule_base) - policies.NatRule.refreshall(nat_rule_base) - - # Which action shall we take on the object? - if rule_type == 'security': - # Search for the object - test_string = create_security_test( - source_ip=source_ip, - source_user=source_user, - destination_ip=destination_ip, - destination_port=destination_port, - application=application, - protocol=protocol, - category=category - ) - elif rule_type == 'nat': - test_string = create_nat_test( - source_zone=source_zone, - source_ip=source_ip, - source_port=source_port, - to_interface=to_interface, - destination_zone=destination_zone, - destination_ip=destination_ip, - destination_port=destination_port, - protocol=protocol - ) - - # Submit the op command with the appropriate test string - try: - response = device.op(cmd=test_string, vsys=vsys_id) - except PanXapiError as exc: - module.fail_json(msg=exc.message) - - if response.find('result/rules').__len__() == 1: - rule_name = response.find('result/rules/entry').text.split(';')[0] - elif rule_type == 'nat': - module.exit_json(msg='No matching NAT rule.') - else: - module.fail_json(msg='Rule match failed. Please check playbook syntax.') - - if rule_type == 'security': - rule_match = sec_rule_base.find(rule_name, policies.SecurityRule) - elif rule_type == 'nat': - rule_match = nat_rule_base.find(rule_name, policies.NatRule) - - # Print out the rule - module.exit_json( - stdout_lines=json.dumps(xmltodict.parse(rule_match.element_str()), indent=2), - msg='Rule matched' - ) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_mgtconfig.py b/plugins/modules/network/panos/panos_mgtconfig.py deleted file mode 100644 index ac3f300765..0000000000 --- a/plugins/modules/network/panos/panos_mgtconfig.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_mgtconfig -short_description: configure management settings of device -description: - - Configure management settings of device -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - dns_server_primary: - description: - - address of primary DNS server - dns_server_secondary: - description: - - address of secondary DNS server - panorama_primary: - description: - - address of primary Panorama server - panorama_secondary: - description: - - address of secondary Panorama server - commit: - description: - - commit if changed - type: bool - default: 'yes' -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -- name: set dns and panorama - panos_mgtconfig: - ip_address: "192.168.1.1" - password: "admin" - dns_server_primary: "1.1.1.1" - dns_server_secondary: "1.1.1.2" - panorama_primary: "1.1.1.3" - panorama_secondary: "1.1.1.4" -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - -try: - import pan.xapi - from pan.xapi import PanXapiError - HAS_LIB = True -except ImportError: - HAS_LIB = False - -_XPATH_DNS_SERVERS = "/config/devices/entry[@name='localhost.localdomain']" +\ - "/deviceconfig/system/dns-setting/servers" -_XPATH_PANORAMA_SERVERS = "/config" +\ - "/devices/entry[@name='localhost.localdomain']" +\ - "/deviceconfig/system" - - -def set_dns_server(xapi, new_dns_server, primary=True): - if primary: - tag = "primary" - else: - tag = "secondary" - xpath = _XPATH_DNS_SERVERS + "/" + tag - - # check the current element value - xapi.get(xpath) - val = xapi.element_root.find(".//" + tag) - if val is not None: - # element exists - val = val.text - if val == new_dns_server: - return False - - element = "<%(tag)s>%(value)s" %\ - dict(tag=tag, value=new_dns_server) - xapi.edit(xpath, element) - - return True - - -def set_panorama_server(xapi, new_panorama_server, primary=True): - if primary: - tag = "panorama-server" - else: - tag = "panorama-server-2" - xpath = _XPATH_PANORAMA_SERVERS + "/" + tag - - # check the current element value - xapi.get(xpath) - val = xapi.element_root.find(".//" + tag) - if val is not None: - # element exists - val = val.text - if val == new_panorama_server: - return False - - element = "<%(tag)s>%(value)s" %\ - dict(tag=tag, value=new_panorama_server) - xapi.edit(xpath, element) - - return True - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - dns_server_primary=dict(), - dns_server_secondary=dict(), - panorama_primary=dict(), - panorama_secondary=dict(), - commit=dict(type='bool', default=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - dns_server_primary = module.params['dns_server_primary'] - dns_server_secondary = module.params['dns_server_secondary'] - panorama_primary = module.params['panorama_primary'] - panorama_secondary = module.params['panorama_secondary'] - commit = module.params['commit'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - changed = False - try: - if dns_server_primary is not None: - changed |= set_dns_server(xapi, dns_server_primary, primary=True) - if dns_server_secondary is not None: - changed |= set_dns_server(xapi, dns_server_secondary, primary=False) - if panorama_primary is not None: - changed |= set_panorama_server(xapi, panorama_primary, primary=True) - if panorama_secondary is not None: - changed |= set_panorama_server(xapi, panorama_secondary, primary=False) - - if changed and commit: - xapi.commit(cmd="", sync=True, interval=1) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - - module.exit_json(changed=changed, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_nat_rule.py b/plugins/modules/network/panos/panos_nat_rule.py deleted file mode 100644 index d67b1fccff..0000000000 --- a/plugins/modules/network/panos/panos_nat_rule.py +++ /dev/null @@ -1,471 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# (c) 2016, techbizdev -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -DOCUMENTATION = ''' ---- -module: panos_nat_rule -short_description: create a policy NAT rule -description: > - - Create a policy nat rule. Keep in mind that we can either end up configuring source NAT, destination NAT, or - both. Instead of splitting it into two we will make a fair attempt to determine which one the user wants. -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer), Robert Hagen (@rnh556)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) - - pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is not supported. - - Panorama is supported. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device being configured. - required: true - username: - description: - - Username credentials to use for auth unless I(api_key) is set. - default: "admin" - password: - description: - - Password credentials to use for auth unless I(api_key) is set. - required: true - api_key: - description: - - API key that can be used instead of I(username)/I(password) credentials. - operation: - description: - - The action to be taken. Supported values are I(add)/I(update)/I(find)/I(delete). - required: true - choices: - - add - - update - - delete - - find - devicegroup: - description: - - If Panorama, the device group to put this rule in. - rule_name: - description: - - name of the SNAT rule - required: true - description: - description: - - The description - source_zone: - description: - - list of source zones - required: true - destination_zone: - description: - - destination zone - required: true - source_ip: - description: - - list of source addresses - default: ["any"] - destination_ip: - description: - - list of destination addresses - default: ["any"] - service: - description: - - service - default: "any" - snat_type: - description: - - type of source translation - choices: - - static-ip - - dynamic-ip-and-port - - dynamic-ip - snat_address_type: - description: - - type of source translation. Supported values are I(translated-address)/I(translated-address). - default: 'interface-address' - choices: - - interface-address - - translated-address - snat_static_address: - description: - - Source NAT translated address. Used with Static-IP translation. - snat_dynamic_address: - description: - - Source NAT translated address. Used with Dynamic-IP and Dynamic-IP-and-Port. - snat_interface: - description: - - snat interface - snat_interface_address: - description: - - snat interface address - snat_bidirectional: - description: - - bidirectional flag - type: bool - default: 'no' - dnat_address: - description: - - dnat translated address - dnat_port: - description: - - dnat translated port - tag_name: - description: - - Tag for the NAT rule. - to_interface: - description: - - Destination interface. - default: 'any' - commit: - description: - - Commit configuration if changed. - type: bool - default: 'yes' -''' - -EXAMPLES = ''' -# Create a source and destination nat rule - - name: Create NAT SSH rule for 10.0.1.101 - panos_nat_rule: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - rule_name: "Web SSH" - source_zone: ["external"] - destination_zone: "external" - source: ["any"] - destination: ["10.0.0.100"] - service: "service-tcp-221" - snat_type: "dynamic-ip-and-port" - snat_interface: "ethernet1/2" - dnat_address: "10.0.1.101" - dnat_port: "22" -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -# import pydevd -# pydevd.settrace('localhost', port=60374, stdoutToServer=True, stderrToServer=True) -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - -try: - import pan.xapi - from pan.xapi import PanXapiError - import pandevice - from pandevice import base - from pandevice import firewall - from pandevice import panorama - from pandevice import objects - from pandevice import policies - import xmltodict - import json - - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def get_devicegroup(device, devicegroup): - dg_list = device.refresh_devices() - for group in dg_list: - if isinstance(group, pandevice.panorama.DeviceGroup): - if group.name == devicegroup: - return group - return False - - -def get_rulebase(device, devicegroup): - # Build the rulebase - if isinstance(device, pandevice.firewall.Firewall): - rulebase = pandevice.policies.Rulebase() - device.add(rulebase) - elif isinstance(device, pandevice.panorama.Panorama): - dg = panorama.DeviceGroup(devicegroup) - device.add(dg) - rulebase = policies.PreRulebase() - dg.add(rulebase) - else: - return False - policies.NatRule.refreshall(rulebase) - return rulebase - - -def find_rule(rulebase, rule_name): - # Search for the rule name - rule = rulebase.find(rule_name) - if rule: - return rule - else: - return False - - -def create_nat_rule(**kwargs): - nat_rule = policies.NatRule( - name=kwargs['rule_name'], - description=kwargs['description'], - fromzone=kwargs['source_zone'], - source=kwargs['source_ip'], - tozone=kwargs['destination_zone'], - destination=kwargs['destination_ip'], - service=kwargs['service'], - to_interface=kwargs['to_interface'], - nat_type=kwargs['nat_type'] - ) - - # Source translation: Static IP - if kwargs['snat_type'] in ['static-ip'] and kwargs['snat_static_address']: - nat_rule.source_translation_type = kwargs['snat_type'] - nat_rule.source_translation_static_translated_address = kwargs['snat_static_address'] - # Bi-directional flag set? - if kwargs['snat_bidirectional']: - nat_rule.source_translation_static_bi_directional = kwargs['snat_bidirectional'] - - # Source translation: Dynamic IP and port - elif kwargs['snat_type'] in ['dynamic-ip-and-port']: - nat_rule.source_translation_type = kwargs['snat_type'] - nat_rule.source_translation_address_type = kwargs['snat_address_type'] - # Interface address? - if kwargs['snat_interface']: - nat_rule.source_translation_interface = kwargs['snat_interface'] - # Interface IP? - if kwargs['snat_interface_address']: - nat_rule.source_translation_ip_address = kwargs['snat_interface_address'] - else: - nat_rule.source_translation_translated_addresses = kwargs['snat_dynamic_address'] - - # Source translation: Dynamic IP - elif kwargs['snat_type'] in ['dynamic-ip']: - if kwargs['snat_dynamic_address']: - nat_rule.source_translation_type = kwargs['snat_type'] - nat_rule.source_translation_translated_addresses = kwargs['snat_dynamic_address'] - else: - return False - - # Destination translation - if kwargs['dnat_address']: - nat_rule.destination_translated_address = kwargs['dnat_address'] - if kwargs['dnat_port']: - nat_rule.destination_translated_port = kwargs['dnat_port'] - - # Any tags? - if 'tag_name' in kwargs: - nat_rule.tag = kwargs['tag_name'] - - return nat_rule - - -def add_rule(rulebase, nat_rule): - if rulebase: - rulebase.add(nat_rule) - nat_rule.create() - return True - else: - return False - - -def update_rule(rulebase, nat_rule): - if rulebase: - rulebase.add(nat_rule) - nat_rule.apply() - return True - else: - return False - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - username=dict(default='admin'), - password=dict(required=True, no_log=True), - api_key=dict(no_log=True), - operation=dict(required=True, choices=['add', 'update', 'delete', 'find']), - rule_name=dict(required=True), - description=dict(), - tag_name=dict(), - source_zone=dict(type='list'), - source_ip=dict(type='list', default=['any']), - destination_zone=dict(), - destination_ip=dict(type='list', default=['any']), - service=dict(default='any'), - to_interface=dict(default='any'), - snat_type=dict(choices=['static-ip', 'dynamic-ip-and-port', 'dynamic-ip']), - snat_address_type=dict(choices=['interface-address', 'translated-address'], default='interface-address'), - snat_static_address=dict(), - snat_dynamic_address=dict(type='list'), - snat_interface=dict(), - snat_interface_address=dict(), - snat_bidirectional=dict(type='bool', default=False), - dnat_address=dict(), - dnat_port=dict(), - devicegroup=dict(), - commit=dict(type='bool', default=True) - ) - - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - required_one_of=[['api_key', 'password']]) - if not HAS_LIB: - module.fail_json(msg='Missing required libraries.') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - api_key = module.params['api_key'] - operation = module.params['operation'] - rule_name = module.params['rule_name'] - description = module.params['description'] - tag_name = module.params['tag_name'] - source_zone = module.params['source_zone'] - source_ip = module.params['source_ip'] - destination_zone = module.params['destination_zone'] - destination_ip = module.params['destination_ip'] - service = module.params['service'] - to_interface = module.params['to_interface'] - nat_type = 'ipv4' - snat_type = module.params['snat_type'] - snat_address_type = module.params['snat_address_type'] - snat_static_address = module.params['snat_static_address'] - snat_dynamic_address = module.params['snat_dynamic_address'] - snat_interface = module.params['snat_interface'] - snat_interface_address = module.params['snat_interface_address'] - snat_bidirectional = module.params['snat_bidirectional'] - dnat_address = module.params['dnat_address'] - dnat_port = module.params['dnat_port'] - devicegroup = module.params['devicegroup'] - - commit = module.params['commit'] - - # Create the device with the appropriate pandevice type - device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key) - - # If Panorama, validate the devicegroup - dev_group = None - if devicegroup and isinstance(device, panorama.Panorama): - dev_group = get_devicegroup(device, devicegroup) - if dev_group: - device.add(dev_group) - else: - module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup) - - # Get the rulebase - rulebase = get_rulebase(device, dev_group) - - # Which action shall we take on the object? - if operation == "find": - # Search for the rule - match = find_rule(rulebase, rule_name) - # If found, format and return the result - if match: - match_dict = xmltodict.parse(match.element_str()) - module.exit_json( - stdout_lines=json.dumps(match_dict, indent=2), - msg='Rule matched' - ) - else: - module.fail_json(msg='Rule \'%s\' not found. Is the name correct?' % rule_name) - elif operation == "delete": - # Search for the object - match = find_rule(rulebase, rule_name) - # If found, delete it - if match: - try: - match.delete() - if commit: - device.commit(sync=True) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - - module.exit_json(changed=True, msg='Rule \'%s\' successfully deleted.' % rule_name) - else: - module.fail_json(msg='Rule \'%s\' not found. Is the name correct?' % rule_name) - elif operation == "add": - # Look for required parameters - if source_zone and destination_zone and nat_type: - pass - else: - module.fail_json(msg='Missing parameter. Required: source_zone, destination_zone, nat_type') - # Search for the rule. Fail if found. - match = find_rule(rulebase, rule_name) - if match: - module.fail_json(msg='Rule \'%s\' already exists. Use operation: \'update\' to change it.' % rule_name) - else: - try: - new_rule = create_nat_rule( - rule_name=rule_name, - description=description, - tag_name=tag_name, - source_zone=source_zone, - destination_zone=destination_zone, - source_ip=source_ip, - destination_ip=destination_ip, - service=service, - to_interface=to_interface, - nat_type=nat_type, - snat_type=snat_type, - snat_address_type=snat_address_type, - snat_static_address=snat_static_address, - snat_dynamic_address=snat_dynamic_address, - snat_interface=snat_interface, - snat_interface_address=snat_interface_address, - snat_bidirectional=snat_bidirectional, - dnat_address=dnat_address, - dnat_port=dnat_port - ) - changed = add_rule(rulebase, new_rule) - if changed and commit: - device.commit(sync=True) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - module.exit_json(changed=changed, msg='Rule \'%s\' successfully added.' % rule_name) - elif operation == 'update': - # Search for the rule. Update if found. - match = find_rule(rulebase, rule_name) - if match: - try: - new_rule = create_nat_rule( - rule_name=rule_name, - description=description, - tag_name=tag_name, - source_zone=source_zone, - destination_zone=destination_zone, - source_ip=source_ip, - destination_ip=destination_ip, - service=service, - to_interface=to_interface, - nat_type=nat_type, - snat_type=snat_type, - snat_address_type=snat_address_type, - snat_static_address=snat_static_address, - snat_dynamic_address=snat_dynamic_address, - snat_interface=snat_interface, - snat_interface_address=snat_interface_address, - snat_bidirectional=snat_bidirectional, - dnat_address=dnat_address, - dnat_port=dnat_port - ) - changed = update_rule(rulebase, new_rule) - if changed and commit: - device.commit(sync=True) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - module.exit_json(changed=changed, msg='Rule \'%s\' successfully updated.' % rule_name) - else: - module.fail_json(msg='Rule \'%s\' does not exist. Use operation: \'add\' to add it.' % rule_name) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_object.py b/plugins/modules/network/panos/panos_object.py deleted file mode 100644 index f654641dfa..0000000000 --- a/plugins/modules/network/panos/panos_object.py +++ /dev/null @@ -1,490 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . -# limitations under the License. - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: panos_object -short_description: create/read/update/delete object in PAN-OS or Panorama -description: - - Policy objects form the match criteria for policy rules and many other functions in PAN-OS. These may include - address object, address groups, service objects, service groups, and tag. -author: "Bob Hagen (@rnh556)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) - - pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is not supported. - - Panorama is supported. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device or Panorama management console being configured. - required: true - username: - description: - - Username credentials to use for authentication. - default: "admin" - password: - description: - - Password credentials to use for authentication. - required: true - api_key: - description: - - API key that can be used instead of I(username)/I(password) credentials. - operation: - description: - - The operation to be performed. Supported values are I(add)/I(delete)/I(find). - required: true - choices: - - add - - update - - delete - - find - addressobject: - description: - - The name of the address object. - address: - description: - - The IP address of the host or network in CIDR notation. - address_type: - description: - - The type of address object definition. Valid types are I(ip-netmask) and I(ip-range). - default: 'ip-netmask' - choices: - - ip-netmask - - ip-range - - fqdn - addressgroup: - description: - - A static group of address objects or dynamic address group. - static_value: - description: - - A group of address objects to be used in an addressgroup definition. - dynamic_value: - description: - - The filter match criteria to be used in a dynamic addressgroup definition. - serviceobject: - description: - - The name of the service object. - source_port: - description: - - The source port to be used in a service object definition. - destination_port: - description: - - The destination port to be used in a service object definition. - protocol: - description: - - The IP protocol to be used in a service object definition. Valid values are I(tcp) or I(udp). - choices: - - tcp - - udp - servicegroup: - description: - - A group of service objects. - services: - description: - - The group of service objects used in a servicegroup definition. - description: - description: - - The description of the object. - tag_name: - description: - - The name of an object or rule tag. - color: - description: > - - The color of the tag object. Valid values are I(red, green, blue, yellow, copper, orange, purple, gray, - light green, cyan, light gray, blue gray, lime, black, gold, and brown). - choices: - - red - - green - - blue - - yellow - - copper - - orange - - purple - - gray - - light green - - cyan - - light gray - - blue gray - - lime - - black - - gold - - brown - devicegroup: - description: > - - The name of the Panorama device group. The group must exist on Panorama. If device group is not defined it - is assumed that we are contacting a firewall. -''' - -EXAMPLES = ''' -- name: search for shared address object - panos_object: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - operation: 'find' - address: 'DevNet' - -- name: create an address group in devicegroup using API key - panos_object: - ip_address: '{{ ip_address }}' - api_key: '{{ api_key }}' - operation: 'add' - addressgroup: 'Prod_DB_Svrs' - static_value: ['prod-db1', 'prod-db2', 'prod-db3'] - description: 'Production DMZ database servers' - tag_name: 'DMZ' - devicegroup: 'DMZ Firewalls' - -- name: create a global service for TCP 3306 - panos_object: - ip_address: '{{ ip_address }}' - api_key: '{{ api_key }}' - operation: 'add' - serviceobject: 'mysql-3306' - destination_port: '3306' - protocol: 'tcp' - description: 'MySQL on tcp/3306' - -- name: create a global tag - panos_object: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - operation: 'add' - tag_name: 'ProjectX' - color: 'yellow' - description: 'Associated with Project X' - -- name: delete an address object from a devicegroup using API key - panos_object: - ip_address: '{{ ip_address }}' - api_key: '{{ api_key }}' - operation: 'delete' - addressobject: 'Win2K test' -''' - -RETURN = ''' -# Default return values -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - -try: - import pan.xapi - from pan.xapi import PanXapiError - import pandevice - from pandevice import base - from pandevice import firewall - from pandevice import panorama - from pandevice import objects - import xmltodict - import json - - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def get_devicegroup(device, devicegroup): - dg_list = device.refresh_devices() - for group in dg_list: - if isinstance(group, pandevice.panorama.DeviceGroup): - if group.name == devicegroup: - return group - return False - - -def find_object(device, dev_group, obj_name, obj_type): - # Get the firewall objects - obj_type.refreshall(device) - if isinstance(device, pandevice.firewall.Firewall): - addr = device.find(obj_name, obj_type) - return addr - elif isinstance(device, pandevice.panorama.Panorama): - addr = device.find(obj_name, obj_type) - if addr is None: - if dev_group: - device.add(dev_group) - obj_type.refreshall(dev_group) - addr = dev_group.find(obj_name, obj_type) - return addr - else: - return False - - -def create_object(**kwargs): - if kwargs['addressobject']: - newobject = objects.AddressObject( - name=kwargs['addressobject'], - value=kwargs['address'], - type=kwargs['address_type'], - description=kwargs['description'], - tag=kwargs['tag_name'] - ) - if newobject.type and newobject.value: - return newobject - else: - return False - elif kwargs['addressgroup']: - newobject = objects.AddressGroup( - name=kwargs['addressgroup'], - static_value=kwargs['static_value'], - dynamic_value=kwargs['dynamic_value'], - description=kwargs['description'], - tag=kwargs['tag_name'] - ) - if newobject.static_value or newobject.dynamic_value: - return newobject - else: - return False - elif kwargs['serviceobject']: - newobject = objects.ServiceObject( - name=kwargs['serviceobject'], - protocol=kwargs['protocol'], - source_port=kwargs['source_port'], - destination_port=kwargs['destination_port'], - tag=kwargs['tag_name'] - ) - if newobject.protocol and newobject.destination_port: - return newobject - else: - return False - elif kwargs['servicegroup']: - newobject = objects.ServiceGroup( - name=kwargs['servicegroup'], - value=kwargs['services'], - tag=kwargs['tag_name'] - ) - if newobject.value: - return newobject - else: - return False - elif kwargs['tag_name']: - newobject = objects.Tag( - name=kwargs['tag_name'], - color=kwargs['color'], - comments=kwargs['description'] - ) - if newobject.name: - return newobject - else: - return False - else: - return False - - -def add_object(device, dev_group, new_object): - if dev_group: - dev_group.add(new_object) - else: - device.add(new_object) - new_object.create() - return True - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(no_log=True), - username=dict(default='admin'), - api_key=dict(no_log=True), - operation=dict(required=True, choices=['add', 'update', 'delete', 'find']), - addressobject=dict(default=None), - addressgroup=dict(default=None), - serviceobject=dict(default=None), - servicegroup=dict(default=None), - address=dict(default=None), - address_type=dict(default='ip-netmask', choices=['ip-netmask', 'ip-range', 'fqdn']), - static_value=dict(type='list', default=None), - dynamic_value=dict(default=None), - protocol=dict(default=None, choices=['tcp', 'udp']), - source_port=dict(default=None), - destination_port=dict(default=None), - services=dict(type='list', default=None), - description=dict(default=None), - tag_name=dict(default=None), - color=dict(default=None, choices=['red', 'green', 'blue', 'yellow', 'copper', 'orange', 'purple', - 'gray', 'light green', 'cyan', 'light gray', 'blue gray', - 'lime', 'black', 'gold', 'brown']), - devicegroup=dict(default=None) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - required_one_of=[['api_key', 'password']], - mutually_exclusive=[['addressobject', 'addressgroup', - 'serviceobject', 'servicegroup', - 'tag_name']] - ) - if not HAS_LIB: - module.fail_json(msg='Missing required libraries.') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - api_key = module.params['api_key'] - operation = module.params['operation'] - addressobject = module.params['addressobject'] - addressgroup = module.params['addressgroup'] - serviceobject = module.params['serviceobject'] - servicegroup = module.params['servicegroup'] - address = module.params['address'] - address_type = module.params['address_type'] - static_value = module.params['static_value'] - dynamic_value = module.params['dynamic_value'] - protocol = module.params['protocol'] - source_port = module.params['source_port'] - destination_port = module.params['destination_port'] - services = module.params['services'] - description = module.params['description'] - tag_name = module.params['tag_name'] - color = module.params['color'] - devicegroup = module.params['devicegroup'] - - # Create the device with the appropriate pandevice type - device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key) - - # If Panorama, validate the devicegroup - dev_group = None - if devicegroup and isinstance(device, panorama.Panorama): - dev_group = get_devicegroup(device, devicegroup) - if dev_group: - device.add(dev_group) - else: - module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup) - - # What type of object are we talking about? - if addressobject: - obj_name = addressobject - obj_type = objects.AddressObject - elif addressgroup: - obj_name = addressgroup - obj_type = objects.AddressGroup - elif serviceobject: - obj_name = serviceobject - obj_type = objects.ServiceObject - elif servicegroup: - obj_name = servicegroup - obj_type = objects.ServiceGroup - elif tag_name: - obj_name = tag_name - obj_type = objects.Tag - else: - module.fail_json(msg='No object type defined!') - - # Which operation shall we perform on the object? - if operation == "find": - # Search for the object - match = find_object(device, dev_group, obj_name, obj_type) - - # If found, format and return the result - if match: - match_dict = xmltodict.parse(match.element_str()) - module.exit_json( - stdout_lines=json.dumps(match_dict, indent=2), - msg='Object matched' - ) - else: - module.fail_json(msg='Object \'%s\' not found. Is the name correct?' % obj_name) - elif operation == "delete": - # Search for the object - match = find_object(device, dev_group, obj_name, obj_type) - - # If found, delete it - if match: - try: - match.delete() - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - - module.exit_json(changed=True, msg='Object \'%s\' successfully deleted' % obj_name) - else: - module.fail_json(msg='Object \'%s\' not found. Is the name correct?' % obj_name) - elif operation == "add": - # Search for the object. Fail if found. - match = find_object(device, dev_group, obj_name, obj_type) - if match: - module.fail_json(msg='Object \'%s\' already exists. Use operation: \'update\' to change it.' % obj_name) - else: - try: - new_object = create_object( - addressobject=addressobject, - addressgroup=addressgroup, - serviceobject=serviceobject, - servicegroup=servicegroup, - address=address, - address_type=address_type, - static_value=static_value, - dynamic_value=dynamic_value, - protocol=protocol, - source_port=source_port, - destination_port=destination_port, - services=services, - description=description, - tag_name=tag_name, - color=color - ) - changed = add_object(device, dev_group, new_object) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - module.exit_json(changed=changed, msg='Object \'%s\' successfully added' % obj_name) - elif operation == "update": - # Search for the object. Update if found. - match = find_object(device, dev_group, obj_name, obj_type) - if match: - try: - new_object = create_object( - addressobject=addressobject, - addressgroup=addressgroup, - serviceobject=serviceobject, - servicegroup=servicegroup, - address=address, - address_type=address_type, - static_value=static_value, - dynamic_value=dynamic_value, - protocol=protocol, - source_port=source_port, - destination_port=destination_port, - services=services, - description=description, - tag_name=tag_name, - color=color - ) - changed = add_object(device, dev_group, new_object) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - module.exit_json(changed=changed, msg='Object \'%s\' successfully updated.' % obj_name) - else: - module.fail_json(msg='Object \'%s\' does not exist. Use operation: \'add\' to add it.' % obj_name) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_op.py b/plugins/modules/network/panos/panos_op.py deleted file mode 100644 index 50fc1b4de8..0000000000 --- a/plugins/modules/network/panos/panos_op.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: panos_op -short_description: execute arbitrary OP commands on PANW devices (e.g. show interface all) -description: This module will allow user to pass and execute any supported OP command on the PANW device. -author: "Ivan Bojer (@ivanbojer)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) - - pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is NOT supported. - - Panorama is NOT supported. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device or Panorama management console being configured. - required: true - username: - description: - - Username credentials to use for authentication. - required: false - default: "admin" - password: - description: - - Password credentials to use for authentication. - required: true - api_key: - description: - - API key that can be used instead of I(username)/I(password) credentials. - cmd: - description: - - The OP command to be performed. - required: true -''' - -EXAMPLES = ''' -- name: show list of all interfaces - panos_op: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - cmd: 'show interfaces all' - -- name: show system info - panos_op: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - cmd: 'show system info' -''' - -RETURN = ''' -stdout: - description: output of the given OP command as JSON formatted string - returned: success - type: str - sample: "{system: {app-release-date: 2017/05/01 15:09:12}}" - -stdout_xml: - description: output of the given OP command as JSON formatted string - returned: success - type: str - sample: "fw2" -''' - -from ansible.module_utils.basic import AnsibleModule - -try: - import pan.xapi - from pan.xapi import PanXapiError - import pandevice - from pandevice import base - from pandevice import firewall - from pandevice import panorama - import xmltodict - import json - - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(no_log=True), - username=dict(default='admin'), - api_key=dict(no_log=True), - cmd=dict(required=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - required_one_of=[['api_key', 'password']]) - if not HAS_LIB: - module.fail_json(msg='Missing required libraries.') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - api_key = module.params['api_key'] - cmd = module.params['cmd'] - - # Create the device with the appropriate pandevice type - device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key) - - changed = False - try: - xml_output = device.op(cmd, xml=True) - changed = True - except PanXapiError as exc: - if 'non NULL value' in exc.message: - # rewrap and call again - cmd_array = cmd.split() - cmd_array_len = len(cmd_array) - cmd_array[cmd_array_len - 1] = '\"' + cmd_array[cmd_array_len - 1] + '\"' - cmd2 = ' '.join(cmd_array) - try: - xml_output = device.op(cmd2, xml=True) - changed = True - except PanXapiError as exc: - module.fail_json(msg=exc.message) - - obj_dict = xmltodict.parse(xml_output) - json_output = json.dumps(obj_dict) - - module.exit_json(changed=changed, msg="Done", stdout=json_output, stdout_xml=xml_output) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_pg.py b/plugins/modules/network/panos/panos_pg.py deleted file mode 100644 index ddd155fb11..0000000000 --- a/plugins/modules/network/panos/panos_pg.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_pg -short_description: create a security profiles group -description: - - Create a security profile group -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - pg_name: - description: - - name of the security profile group - required: true - data_filtering: - description: - - name of the data filtering profile - file_blocking: - description: - - name of the file blocking profile - spyware: - description: - - name of the spyware profile - url_filtering: - description: - - name of the url filtering profile - virus: - description: - - name of the anti-virus profile - vulnerability: - description: - - name of the vulnerability profile - wildfire: - description: - - name of the wildfire analysis profile - commit: - description: - - commit if changed - type: bool - default: 'yes' -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -- name: setup security profile group - panos_pg: - ip_address: "192.168.1.1" - password: "admin" - username: "admin" - pg_name: "pg-default" - virus: "default" - spyware: "default" - vulnerability: "default" -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - - -try: - import pan.xapi - from pan.xapi import PanXapiError - HAS_LIB = True -except ImportError: - HAS_LIB = False - -_PG_XPATH = "/config/devices/entry[@name='localhost.localdomain']" +\ - "/vsys/entry[@name='vsys1']" +\ - "/profile-group/entry[@name='%s']" - - -def pg_exists(xapi, pg_name): - xapi.get(_PG_XPATH % pg_name) - e = xapi.element_root.find('.//entry') - if e is None: - return False - return True - - -def add_pg(xapi, pg_name, data_filtering, file_blocking, spyware, - url_filtering, virus, vulnerability, wildfire): - if pg_exists(xapi, pg_name): - return False - - exml = [] - - if data_filtering is not None: - exml.append('%s' % - data_filtering) - if file_blocking is not None: - exml.append('%s' % - file_blocking) - if spyware is not None: - exml.append('%s' % - spyware) - if url_filtering is not None: - exml.append('%s' % - url_filtering) - if virus is not None: - exml.append('%s' % - virus) - if vulnerability is not None: - exml.append('%s' % - vulnerability) - if wildfire is not None: - exml.append('%s' % - wildfire) - - exml = ''.join(exml) - xapi.set(xpath=_PG_XPATH % pg_name, element=exml) - - return True - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - pg_name=dict(required=True), - data_filtering=dict(), - file_blocking=dict(), - spyware=dict(), - url_filtering=dict(), - virus=dict(), - vulnerability=dict(), - wildfire=dict(), - commit=dict(type='bool', default=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - pg_name = module.params['pg_name'] - data_filtering = module.params['data_filtering'] - file_blocking = module.params['file_blocking'] - spyware = module.params['spyware'] - url_filtering = module.params['url_filtering'] - virus = module.params['virus'] - vulnerability = module.params['vulnerability'] - wildfire = module.params['wildfire'] - commit = module.params['commit'] - - try: - changed = add_pg(xapi, pg_name, data_filtering, file_blocking, - spyware, url_filtering, virus, vulnerability, wildfire) - - if changed and commit: - xapi.commit(cmd="", sync=True, interval=1) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - - module.exit_json(changed=changed, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_query_rules.py b/plugins/modules/network/panos/panos_query_rules.py deleted file mode 100644 index 6fe27767d2..0000000000 --- a/plugins/modules/network/panos/panos_query_rules.py +++ /dev/null @@ -1,495 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . -# limitations under the License. - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: panos_query_rules -short_description: PANOS module that allows search for security rules in PANW NGFW devices. -description: > - - Security policies allow you to enforce rules and take action, and can be as general or specific as needed. The - policy rules are compared against the incoming traffic in sequence, and because the first rule that matches the - traffic is applied, the more specific rules must precede the more general ones. -author: "Bob Hagen (@rnh556)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) - - pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/) - - xmltodict can be obtains from PyPI U(https://pypi.org/project/xmltodict/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is not supported. - - Panorama is supported. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS firewall or Panorama management console being queried. - required: true - username: - description: - - Username credentials to use for authentication. - default: "admin" - password: - description: - - Password credentials to use for authentication. - required: true - api_key: - description: - - API key that can be used instead of I(username)/I(password) credentials. - application: - description: - - Name of the application or application group to be queried. - source_zone: - description: - - Name of the source security zone to be queried. - source_ip: - description: - - The source IP address to be queried. - source_port: - description: - - The source port to be queried. - destination_zone: - description: - - Name of the destination security zone to be queried. - destination_ip: - description: - - The destination IP address to be queried. - destination_port: - description: - - The destination port to be queried. - protocol: - description: - - The protocol used to be queried. Must be either I(tcp) or I(udp). - choices: - - tcp - - udp - tag_name: - description: - - Name of the rule tag to be queried. - devicegroup: - description: - - The Panorama device group in which to conduct the query. -''' - -EXAMPLES = ''' -- name: search for rules with tcp/3306 - panos_query_rules: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - source_zone: 'DevNet' - destination_zone: 'DevVPC' - destination_port: '3306' - protocol: 'tcp' - -- name: search devicegroup for inbound rules to dmz host - panos_query_rules: - ip_address: '{{ ip_address }}' - api_key: '{{ api_key }}' - destination_zone: 'DMZ' - destination_ip: '10.100.42.18' - address: 'DeviceGroupA' - -- name: search for rules containing a specified rule tag - panos_query_rules: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - tag_name: 'ProjectX' -''' - -RETURN = ''' -# Default return values -''' - -from ansible.module_utils.basic import AnsibleModule - -try: - import pan.xapi - from pan.xapi import PanXapiError - import pandevice - from pandevice import base - from pandevice import firewall - from pandevice import panorama - from pandevice import objects - from pandevice import policies - import ipaddress - import xmltodict - import json - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def get_devicegroup(device, devicegroup): - dg_list = device.refresh_devices() - for group in dg_list: - if isinstance(group, pandevice.panorama.DeviceGroup): - if group.name == devicegroup: - return group - return False - - -def get_rulebase(device, devicegroup): - # Build the rulebase - if isinstance(device, firewall.Firewall): - rulebase = policies.Rulebase() - device.add(rulebase) - elif isinstance(device, panorama.Panorama): - dg = panorama.DeviceGroup(devicegroup) - device.add(dg) - rulebase = policies.PreRulebase() - dg.add(rulebase) - else: - return False - policies.SecurityRule.refreshall(rulebase) - return rulebase - - -def get_object(device, dev_group, obj_name): - # Search global address objects - match = device.find(obj_name, objects.AddressObject) - if match: - return match - - # Search global address groups - match = device.find(obj_name, objects.AddressGroup) - if match: - return match - - # Search Panorama device group - if isinstance(device, pandevice.panorama.Panorama): - # Search device group address objects - match = dev_group.find(obj_name, objects.AddressObject) - if match: - return match - - # Search device group address groups - match = dev_group.find(obj_name, objects.AddressGroup) - if match: - return match - return False - - -def addr_in_obj(addr, obj): - ip = ipaddress.ip_address(addr) - # Process address objects - if isinstance(obj, objects.AddressObject): - if obj.type == 'ip-netmask': - net = ipaddress.ip_network(obj.value) - if ip in net: - return True - if obj.type == 'ip-range': - ip_range = obj.value.split('-') - lower = ipaddress.ip_address(ip_range[0]) - upper = ipaddress.ip_address(ip_range[1]) - if lower < ip < upper: - return True - return False - - -def get_services(device, dev_group, svc_list, obj_list): - for svc in svc_list: - - # Search global address objects - global_obj_match = device.find(svc, objects.ServiceObject) - if global_obj_match: - obj_list.append(global_obj_match) - - # Search global address groups - global_grp_match = device.find(svc, objects.ServiceGroup) - if global_grp_match: - get_services(device, dev_group, global_grp_match.value, obj_list) - - # Search Panorama device group - if isinstance(device, pandevice.panorama.Panorama): - - # Search device group address objects - dg_obj_match = dev_group.find(svc, objects.ServiceObject) - if dg_obj_match: - obj_list.append(dg_obj_match) - - # Search device group address groups - dg_grp_match = dev_group.find(svc, objects.ServiceGroup) - if dg_grp_match: - get_services(device, dev_group, dg_grp_match.value, obj_list) - - return obj_list - - -def port_in_svc(orientation, port, protocol, obj): - # Process address objects - if orientation == 'source': - for x in obj.source_port.split(','): - if '-' in x: - port_range = x.split('-') - lower = int(port_range[0]) - upper = int(port_range[1]) - if (lower <= int(port) <= upper) and (obj.protocol == protocol): - return True - else: - if port == x and obj.protocol == protocol: - return True - elif orientation == 'destination': - for x in obj.destination_port.split(','): - if '-' in x: - port_range = x.split('-') - lower = int(port_range[0]) - upper = int(port_range[1]) - if (lower <= int(port) <= upper) and (obj.protocol == protocol): - return True - else: - if port == x and obj.protocol == protocol: - return True - return False - - -def get_tag(device, dev_group, tag_name): - # Search global address objects - match = device.find(tag_name, objects.Tag) - if match: - return match - # Search Panorama device group - if isinstance(device, panorama.Panorama): - # Search device group address objects - match = dev_group.find(tag_name, objects.Tag) - if match: - return match - return False - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(no_log=True), - username=dict(default='admin'), - api_key=dict(no_log=True), - application=dict(default=None), - source_zone=dict(default=None), - destination_zone=dict(default=None), - source_ip=dict(default=None), - destination_ip=dict(default=None), - source_port=dict(default=None), - destination_port=dict(default=None), - protocol=dict(default=None, choices=['tcp', 'udp']), - tag_name=dict(default=None), - devicegroup=dict(default=None) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - required_one_of=[['api_key', 'password']] - ) - if not HAS_LIB: - module.fail_json(msg='Missing required libraries.') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - api_key = module.params['api_key'] - application = module.params['application'] - source_zone = module.params['source_zone'] - source_ip = module.params['source_ip'] - source_port = module.params['source_port'] - destination_zone = module.params['destination_zone'] - destination_ip = module.params['destination_ip'] - destination_port = module.params['destination_port'] - protocol = module.params['protocol'] - tag_name = module.params['tag_name'] - devicegroup = module.params['devicegroup'] - - # Create the device with the appropriate pandevice type - device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key) - - # Grab the global objects - objects.AddressObject.refreshall(device) - objects.AddressGroup.refreshall(device) - objects.ServiceObject.refreshall(device) - objects.ServiceGroup.refreshall(device) - objects.Tag.refreshall(device) - - # If Panorama, validate the devicegroup and grab the devicegroup objects - dev_group = None - if devicegroup and isinstance(device, panorama.Panorama): - dev_group = get_devicegroup(device, devicegroup) - if dev_group: - device.add(dev_group) - objects.AddressObject.refreshall(dev_group) - objects.AddressGroup.refreshall(dev_group) - objects.ServiceObject.refreshall(dev_group) - objects.ServiceGroup.refreshall(dev_group) - objects.Tag.refreshall(dev_group) - else: - module.fail_json( - failed=1, - msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup - ) - - # Build the rulebase and produce list - rulebase = get_rulebase(device, dev_group) - rulelist = rulebase.children - hitbase = policies.Rulebase() - loose_match = True - - # Process each rule - for rule in rulelist: - hitlist = [] - - if source_zone: - source_zone_match = False - if loose_match and 'any' in rule.fromzone: - source_zone_match = True - else: - for object_string in rule.fromzone: - if object_string == source_zone: - source_zone_match = True - hitlist.append(source_zone_match) - - if destination_zone: - destination_zone_match = False - if loose_match and 'any' in rule.tozone: - destination_zone_match = True - else: - for object_string in rule.tozone: - if object_string == destination_zone: - destination_zone_match = True - hitlist.append(destination_zone_match) - - if source_ip: - source_ip_match = False - if loose_match and 'any' in rule.source: - source_ip_match = True - else: - for object_string in rule.source: - # Get a valid AddressObject or AddressGroup - obj = get_object(device, dev_group, object_string) - # Otherwise the object_string is not an object and should be handled differently - if obj is False: - if '-' in object_string: - obj = ipaddress.ip_address(source_ip) - source_range = object_string.split('-') - source_lower = ipaddress.ip_address(source_range[0]) - source_upper = ipaddress.ip_address(source_range[1]) - if source_lower <= obj <= source_upper: - source_ip_match = True - else: - if source_ip == object_string: - source_ip_match = True - if isinstance(obj, objects.AddressObject) and addr_in_obj(source_ip, obj): - source_ip_match = True - elif isinstance(obj, objects.AddressGroup) and obj.static_value: - for member_string in obj.static_value: - member = get_object(device, dev_group, member_string) - if addr_in_obj(source_ip, member): - source_ip_match = True - hitlist.append(source_ip_match) - - if destination_ip: - destination_ip_match = False - if loose_match and 'any' in rule.destination: - destination_ip_match = True - else: - for object_string in rule.destination: - # Get a valid AddressObject or AddressGroup - obj = get_object(device, dev_group, object_string) - # Otherwise the object_string is not an object and should be handled differently - if obj is False: - if '-' in object_string: - obj = ipaddress.ip_address(destination_ip) - destination_range = object_string.split('-') - destination_lower = ipaddress.ip_address(destination_range[0]) - destination_upper = ipaddress.ip_address(destination_range[1]) - if destination_lower <= obj <= destination_upper: - destination_ip_match = True - else: - if destination_ip == object_string: - destination_ip_match = True - if isinstance(obj, objects.AddressObject) and addr_in_obj(destination_ip, obj): - destination_ip_match = True - elif isinstance(obj, objects.AddressGroup) and obj.static_value: - for member_string in obj.static_value: - member = get_object(device, dev_group, member_string) - if addr_in_obj(destination_ip, member): - destination_ip_match = True - hitlist.append(destination_ip_match) - - if source_port: - source_port_match = False - orientation = 'source' - if loose_match and (rule.service[0] == 'any'): - source_port_match = True - elif rule.service[0] == 'application-default': - source_port_match = False # Fix this once apps are supported - else: - service_list = [] - service_list = get_services(device, dev_group, rule.service, service_list) - for obj in service_list: - if port_in_svc(orientation, source_port, protocol, obj): - source_port_match = True - break - hitlist.append(source_port_match) - - if destination_port: - destination_port_match = False - orientation = 'destination' - if loose_match and (rule.service[0] == 'any'): - destination_port_match = True - elif rule.service[0] == 'application-default': - destination_port_match = False # Fix this once apps are supported - else: - service_list = [] - service_list = get_services(device, dev_group, rule.service, service_list) - for obj in service_list: - if port_in_svc(orientation, destination_port, protocol, obj): - destination_port_match = True - break - hitlist.append(destination_port_match) - - if tag_name: - tag_match = False - if rule.tag: - for object_string in rule.tag: - obj = get_tag(device, dev_group, object_string) - if obj and (obj.name == tag_name): - tag_match = True - hitlist.append(tag_match) - - # Add to hit rulebase - if False not in hitlist: - hitbase.add(rule) - - # Dump the hit rulebase - if hitbase.children: - output_string = xmltodict.parse(hitbase.element_str()) - module.exit_json( - stdout_lines=json.dumps(output_string, indent=2), - msg='%s of %s rules matched' % (hitbase.children.__len__(), rulebase.children.__len__()) - ) - else: - module.fail_json(msg='No matching rules found.') - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_restart.py b/plugins/modules/network/panos/panos_restart.py deleted file mode 100644 index 1991cfac05..0000000000 --- a/plugins/modules/network/panos/panos_restart.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_restart -short_description: restart a device -description: - - Restart a device -author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" -requirements: - - pan-python -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -- panos_restart: - ip_address: "192.168.1.1" - username: "admin" - password: "admin" -''' - -RETURN = ''' -status: - description: success status - returned: success - type: str - sample: "okey dokey" -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - - -import sys -import traceback - -try: - import pan.xapi - HAS_LIB = True -except ImportError: - HAS_LIB = False - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - - -def main(): - argument_spec = dict( - ip_address=dict(), - password=dict(no_log=True), - username=dict(default='admin') - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - - if not HAS_LIB: - module.fail_json(msg='pan-python required for this module') - - ip_address = module.params["ip_address"] - if not ip_address: - module.fail_json(msg="ip_address should be specified") - password = module.params["password"] - if not password: - module.fail_json(msg="password is required") - username = module.params['username'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password - ) - - try: - xapi.op(cmd="") - except Exception as e: - if 'succeeded' in to_native(e): - module.exit_json(changed=True, msg=to_native(e)) - else: - module.fail_json(msg=to_native(e), exception=traceback.format_exc()) - - module.exit_json(changed=True, msg="okey dokey") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_sag.py b/plugins/modules/network/panos/panos_sag.py deleted file mode 100644 index 2e171db258..0000000000 --- a/plugins/modules/network/panos/panos_sag.py +++ /dev/null @@ -1,268 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2016, techbizdev -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_sag -short_description: Create a static address group. -description: - - Create a static address group object in the firewall used for policy rules. -author: "Vinay Venkataraghavan (@vinayvenkat)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) - - pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/) - - xmltodict can be obtained from PyPI U(https://pypi.org/project/xmltodict/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -options: - api_key: - description: - - API key that can be used instead of I(username)/I(password) credentials. - sag_name: - description: - - name of the dynamic address group - required: true - sag_match_filter: - description: - - Static filter user by the address group - type: list - devicegroup: - description: > - - The name of the Panorama device group. The group must exist on Panorama. If device group is not defined - it is assumed that we are contacting a firewall. - description: - description: - - The purpose / objective of the static Address Group - tags: - description: - - Tags to be associated with the address group - commit: - description: - - commit if changed - type: bool - default: 'yes' - operation: - description: - - The operation to perform Supported values are I(add)/I(list)/I(delete). - required: true - choices: - - add - - list - - delete -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' -- name: sag - panos_sag: - ip_address: "192.168.1.1" - password: "admin" - sag_name: "sag-1" - static_value: ['test-addresses', ] - description: "A description for the static address group" - tags: ["tags to be associated with the group", ] -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - -try: - from pandevice import base - from pandevice import firewall - from pandevice import panorama - from pandevice import objects - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def get_devicegroup(device, devicegroup): - dg_list = device.refresh_devices() - for group in dg_list: - if isinstance(group, panorama.DeviceGroup): - if group.name == devicegroup: - return group - return False - - -def find_object(device, dev_group, obj_name, obj_type): - # Get the firewall objects - obj_type.refreshall(device) - if isinstance(device, firewall.Firewall): - addr = device.find(obj_name, obj_type) - return addr - elif isinstance(device, panorama.Panorama): - addr = device.find(obj_name, obj_type) - if addr is None: - if dev_group: - device.add(dev_group) - obj_type.refreshall(dev_group) - addr = dev_group.find(obj_name, obj_type) - return addr - else: - return False - - -def create_address_group_object(**kwargs): - """ - Create an Address object - - :param kwargs: key word arguments to instantiate AddressGroup object - @return False or ```objects.AddressObject``` - """ - ad_object = objects.AddressGroup( - name=kwargs['address_gp_name'], - static_value=kwargs['sag_match_filter'], - description=kwargs['description'], - tag=kwargs['tag_name'] - ) - if ad_object.static_value or ad_object.dynamic_value: - return ad_object - else: - return None - - -def add_address_group(device, dev_group, ag_object): - """ - Create a new dynamic address group object on the - PAN FW. - - :param device: Firewall Handle - :param dev_group: Panorama device group - :param ag_object: Address group object - """ - - if dev_group: - dev_group.add(ag_object) - else: - device.add(ag_object) - - exc = None - try: - ag_object.create() - except Exception as exc: - return False, exc - - return True, exc - - -def delete_address_group(device, dev_group, obj_name): - """ - - :param device: - :param dev_group: - :param obj_name: - :return: - """ - static_obj = find_object(device, dev_group, obj_name, objects.AddressGroup) - # If found, delete it - - if static_obj: - try: - static_obj.delete() - except Exception as exc: - return False, exc - return True, None - else: - return False, None - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True), - username=dict(default='admin'), - api_key=dict(no_log=True), - sag_match_filter=dict(type='list', required=False), - sag_name=dict(required=True), - commit=dict(type='bool', default=True), - devicegroup=dict(default=None), - description=dict(default=None), - tags=dict(type='list', default=[]), - operation=dict(type='str', required=True, choices=['add', 'list', 'delete']) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - required_one_of=[['api_key', 'password']]) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - api_key = module.params['api_key'] - operation = module.params['operation'] - - ag_object = create_address_group_object(address_gp_name=module.params.get('sag_name', None), - sag_match_filter=module.params.get('sag_match_filter', None), - description=module.params.get('description', None), - tag_name=module.params.get('tags', None) - ) - commit = module.params['commit'] - - devicegroup = module.params['devicegroup'] - # Create the device with the appropriate pandevice type - device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key) - - # If Panorama, validate the devicegroup - dev_group = None - if devicegroup and isinstance(device, panorama.Panorama): - dev_group = get_devicegroup(device, devicegroup) - if dev_group: - device.add(dev_group) - else: - module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup) - - if operation == 'add': - result, exc = add_address_group(device, dev_group, ag_object) - - if result and commit: - try: - device.commit(sync=True) - except Exception as exc: - module.fail_json(msg=to_native(exc)) - - elif operation == 'delete': - obj_name = module.params.get('sag_name', None) - result, exc = delete_address_group(device, dev_group, obj_name) - if not result and exc: - module.fail_json(msg=exc.message) - elif not result: - module.fail_json(msg="Specified object not found.") - else: - module.fail_json(changed=False, msg="Unsupported option.") - - module.exit_json(changed=True, msg="Address Group Operation Completed.") - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_security_rule.py b/plugins/modules/network/panos/panos_security_rule.py deleted file mode 100644 index 8cbacb1a61..0000000000 --- a/plugins/modules/network/panos/panos_security_rule.py +++ /dev/null @@ -1,576 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# (c) 2016, techbizdev -# 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': ['deprecated'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: panos_security_rule -short_description: Create security rule policy on PAN-OS devices or Panorama management console. -description: - - Security policies allow you to enforce rules and take action, and can be as general or specific as needed. - The policy rules are compared against the incoming traffic in sequence, and because the first rule that matches the traffic is applied, - the more specific rules must precede the more general ones. -author: "Ivan Bojer (@ivanbojer), Robert Hagen (@rnh556)" -requirements: - - pan-python can be obtained from PyPI U(https://pypi.org/project/pan-python/) - - pandevice can be obtained from PyPI U(https://pypi.org/project/pandevice/) - - xmltodict can be obtained from PyPI U(https://pypi.org/project/xmltodict/) -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -notes: - - Checkmode is not supported. - - Panorama is supported. -options: - ip_address: - description: - - IP address (or hostname) of PAN-OS device being configured. - required: true - username: - description: - - Username credentials to use for auth unless I(api_key) is set. - default: "admin" - password: - description: - - Password credentials to use for auth unless I(api_key) is set. - required: true - api_key: - description: - - API key that can be used instead of I(username)/I(password) credentials. - operation: - description: - - The action to be taken. Supported values are I(add)/I(update)/I(find)/I(delete). - default: 'add' - choices: - - add - - update - - delete - - find - category: - description: - - The category. - type: list - default: ['any'] - rule_name: - description: - - Name of the security rule. - required: true - rule_type: - description: - - Type of security rule (version 6.1 of PanOS and above). - default: "universal" - description: - description: - - Description for the security rule. - tag_name: - description: - - Administrative tags that can be added to the rule. Note, tags must be already defined. - source_zone: - description: - - List of source zones. - default: "any" - destination_zone: - description: - - List of destination zones. - default: "any" - source_ip: - description: - - List of source addresses. - default: "any" - source_user: - description: - - Use users to enforce policy for individual users or a group of users. - default: "any" - hip_profiles: - description: > - - If you are using GlobalProtect with host information profile (HIP) enabled, you can also base the policy - on information collected by GlobalProtect. For example, the user access level can be determined HIP that - notifies the firewall about the user's local configuration. - default: "any" - destination_ip: - description: - - List of destination addresses. - default: "any" - application: - description: - - List of applications. - default: "any" - service: - description: - - List of services. - default: "application-default" - log_start: - description: - - Whether to log at session start. - type: bool - log_end: - description: - - Whether to log at session end. - default: true - type: bool - action: - description: - - Action to apply once rules maches. - default: "allow" - group_profile: - description: > - - Security profile group that is already defined in the system. This property supersedes antivirus, - vulnerability, spyware, url_filtering, file_blocking, data_filtering, and wildfire_analysis properties. - antivirus: - description: - - Name of the already defined antivirus profile. - vulnerability: - description: - - Name of the already defined vulnerability profile. - spyware: - description: - - Name of the already defined spyware profile. - url_filtering: - description: - - Name of the already defined url_filtering profile. - file_blocking: - description: - - Name of the already defined file_blocking profile. - data_filtering: - description: - - Name of the already defined data_filtering profile. - wildfire_analysis: - description: - - Name of the already defined wildfire_analysis profile. - devicegroup: - description: > - - Device groups are used for the Panorama interaction with Firewall(s). The group must exists on Panorama. - If device group is not define we assume that we are contacting Firewall. - commit: - description: - - Commit configuration if changed. - type: bool - default: 'yes' -''' - -EXAMPLES = ''' -- name: add an SSH inbound rule to devicegroup - panos_security_rule: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - operation: 'add' - rule_name: 'SSH permit' - description: 'SSH rule test' - tag_name: ['ProjectX'] - source_zone: ['public'] - destination_zone: ['private'] - source_ip: ['any'] - source_user: ['any'] - destination_ip: ['1.1.1.1'] - category: ['any'] - application: ['ssh'] - service: ['application-default'] - hip_profiles: ['any'] - action: 'allow' - devicegroup: 'Cloud Edge' - -- name: add a rule to allow HTTP multimedia only from CDNs - panos_security_rule: - ip_address: '10.5.172.91' - username: 'admin' - password: 'paloalto' - operation: 'add' - rule_name: 'HTTP Multimedia' - description: 'Allow HTTP multimedia only to host at 1.1.1.1' - source_zone: ['public'] - destination_zone: ['private'] - source_ip: ['any'] - source_user: ['any'] - destination_ip: ['1.1.1.1'] - category: ['content-delivery-networks'] - application: ['http-video', 'http-audio'] - service: ['service-http', 'service-https'] - hip_profiles: ['any'] - action: 'allow' - -- name: add a more complex rule that uses security profiles - panos_security_rule: - ip_address: '{{ ip_address }}' - username: '{{ username }}' - password: '{{ password }}' - operation: 'add' - rule_name: 'Allow HTTP w profile' - log_start: false - log_end: true - action: 'allow' - antivirus: 'default' - vulnerability: 'default' - spyware: 'default' - url_filtering: 'default' - wildfire_analysis: 'default' - -- name: delete a devicegroup security rule - panos_security_rule: - ip_address: '{{ ip_address }}' - api_key: '{{ api_key }}' - operation: 'delete' - rule_name: 'Allow telnet' - devicegroup: 'DC Firewalls' - -- name: find a specific security rule - panos_security_rule: - ip_address: '{{ ip_address }}' - password: '{{ password }}' - operation: 'find' - rule_name: 'Allow RDP to DCs' - register: result -- debug: msg='{{result.stdout_lines}}' - -''' - -RETURN = ''' -# Default return values -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_native - -try: - import pan.xapi - from pan.xapi import PanXapiError - import pandevice - from pandevice import base - from pandevice import firewall - from pandevice import panorama - from pandevice import objects - from pandevice import policies - import xmltodict - import json - - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def get_devicegroup(device, devicegroup): - dg_list = device.refresh_devices() - for group in dg_list: - if isinstance(group, pandevice.panorama.DeviceGroup): - if group.name == devicegroup: - return group - return False - - -def get_rulebase(device, devicegroup): - # Build the rulebase - if isinstance(device, pandevice.firewall.Firewall): - rulebase = pandevice.policies.Rulebase() - device.add(rulebase) - elif isinstance(device, pandevice.panorama.Panorama): - dg = panorama.DeviceGroup(devicegroup) - device.add(dg) - rulebase = policies.PreRulebase() - dg.add(rulebase) - else: - return False - policies.SecurityRule.refreshall(rulebase) - return rulebase - - -def find_rule(rulebase, rule_name): - # Search for the rule name - rule = rulebase.find(rule_name) - if rule: - return rule - else: - return False - - -def rule_is_match(propose_rule, current_rule): - - match_check = ['name', 'description', 'group_profile', 'antivirus', 'vulnerability', - 'spyware', 'url_filtering', 'file_blocking', 'data_filtering', - 'wildfire_analysis', 'type', 'action', 'tag', 'log_start', 'log_end'] - list_check = ['tozone', 'fromzone', 'source', 'source_user', 'destination', 'category', - 'application', 'service', 'hip_profiles'] - - for check in match_check: - propose_check = getattr(propose_rule, check, None) - current_check = getattr(current_rule, check, None) - if propose_check != current_check: - return False - for check in list_check: - propose_check = getattr(propose_rule, check, []) - current_check = getattr(current_rule, check, []) - if set(propose_check) != set(current_check): - return False - return True - - -def create_security_rule(**kwargs): - security_rule = policies.SecurityRule( - name=kwargs['rule_name'], - description=kwargs['description'], - fromzone=kwargs['source_zone'], - source=kwargs['source_ip'], - source_user=kwargs['source_user'], - hip_profiles=kwargs['hip_profiles'], - tozone=kwargs['destination_zone'], - destination=kwargs['destination_ip'], - application=kwargs['application'], - service=kwargs['service'], - category=kwargs['category'], - log_start=kwargs['log_start'], - log_end=kwargs['log_end'], - action=kwargs['action'], - type=kwargs['rule_type'] - ) - - if 'tag_name' in kwargs: - security_rule.tag = kwargs['tag_name'] - - # profile settings - if 'group_profile' in kwargs: - security_rule.group = kwargs['group_profile'] - else: - if 'antivirus' in kwargs: - security_rule.virus = kwargs['antivirus'] - if 'vulnerability' in kwargs: - security_rule.vulnerability = kwargs['vulnerability'] - if 'spyware' in kwargs: - security_rule.spyware = kwargs['spyware'] - if 'url_filtering' in kwargs: - security_rule.url_filtering = kwargs['url_filtering'] - if 'file_blocking' in kwargs: - security_rule.file_blocking = kwargs['file_blocking'] - if 'data_filtering' in kwargs: - security_rule.data_filtering = kwargs['data_filtering'] - if 'wildfire_analysis' in kwargs: - security_rule.wildfire_analysis = kwargs['wildfire_analysis'] - return security_rule - - -def add_rule(rulebase, sec_rule): - if rulebase: - rulebase.add(sec_rule) - sec_rule.create() - return True - else: - return False - - -def update_rule(rulebase, nat_rule): - if rulebase: - rulebase.add(nat_rule) - nat_rule.apply() - return True - else: - return False - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(no_log=True), - username=dict(default='admin'), - api_key=dict(no_log=True), - operation=dict(default='add', choices=['add', 'update', 'delete', 'find']), - rule_name=dict(required=True), - description=dict(default=''), - tag_name=dict(type='list'), - destination_zone=dict(type='list', default=['any']), - source_zone=dict(type='list', default=['any']), - source_ip=dict(type='list', default=["any"]), - source_user=dict(type='list', default=['any']), - destination_ip=dict(type='list', default=["any"]), - category=dict(type='list', default=['any']), - application=dict(type='list', default=['any']), - service=dict(type='list', default=['application-default']), - hip_profiles=dict(type='list', default=['any']), - group_profile=dict(), - antivirus=dict(), - vulnerability=dict(), - spyware=dict(), - url_filtering=dict(), - file_blocking=dict(), - data_filtering=dict(), - wildfire_analysis=dict(), - log_start=dict(type='bool', default=False), - log_end=dict(type='bool', default=True), - rule_type=dict(default='universal'), - action=dict(default='allow'), - devicegroup=dict(), - commit=dict(type='bool', default=True) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, - required_one_of=[['api_key', 'password']]) - if not HAS_LIB: - module.fail_json(msg='Missing required libraries.') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - api_key = module.params['api_key'] - operation = module.params['operation'] - rule_name = module.params['rule_name'] - description = module.params['description'] - tag_name = module.params['tag_name'] - source_zone = module.params['source_zone'] - source_ip = module.params['source_ip'] - source_user = module.params['source_user'] - hip_profiles = module.params['hip_profiles'] - destination_zone = module.params['destination_zone'] - destination_ip = module.params['destination_ip'] - application = module.params['application'] - service = module.params['service'] - category = module.params['category'] - log_start = module.params['log_start'] - log_end = module.params['log_end'] - action = module.params['action'] - group_profile = module.params['group_profile'] - antivirus = module.params['antivirus'] - vulnerability = module.params['vulnerability'] - spyware = module.params['spyware'] - url_filtering = module.params['url_filtering'] - file_blocking = module.params['file_blocking'] - data_filtering = module.params['data_filtering'] - wildfire_analysis = module.params['wildfire_analysis'] - rule_type = module.params['rule_type'] - devicegroup = module.params['devicegroup'] - - commit = module.params['commit'] - - # Create the device with the appropriate pandevice type - device = base.PanDevice.create_from_device(ip_address, username, password, api_key=api_key) - - # If Panorama, validate the devicegroup - dev_group = None - if devicegroup and isinstance(device, panorama.Panorama): - dev_group = get_devicegroup(device, devicegroup) - if dev_group: - device.add(dev_group) - else: - module.fail_json(msg='\'%s\' device group not found in Panorama. Is the name correct?' % devicegroup) - - # Get the rulebase - rulebase = get_rulebase(device, dev_group) - - # Which action shall we take on the object? - if operation == "find": - # Search for the object - match = find_rule(rulebase, rule_name) - # If found, format and return the result - if match: - match_dict = xmltodict.parse(match.element_str()) - module.exit_json( - stdout_lines=json.dumps(match_dict, indent=2), - msg='Rule matched' - ) - else: - module.fail_json(msg='Rule \'%s\' not found. Is the name correct?' % rule_name) - elif operation == "delete": - # Search for the object - match = find_rule(rulebase, rule_name) - # If found, delete it - if match: - try: - if commit: - match.delete() - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - - module.exit_json(changed=True, msg='Rule \'%s\' successfully deleted' % rule_name) - else: - module.fail_json(msg='Rule \'%s\' not found. Is the name correct?' % rule_name) - elif operation == "add": - new_rule = create_security_rule( - rule_name=rule_name, - description=description, - tag_name=tag_name, - source_zone=source_zone, - destination_zone=destination_zone, - source_ip=source_ip, - source_user=source_user, - destination_ip=destination_ip, - category=category, - application=application, - service=service, - hip_profiles=hip_profiles, - group_profile=group_profile, - antivirus=antivirus, - vulnerability=vulnerability, - spyware=spyware, - url_filtering=url_filtering, - file_blocking=file_blocking, - data_filtering=data_filtering, - wildfire_analysis=wildfire_analysis, - log_start=log_start, - log_end=log_end, - rule_type=rule_type, - action=action - ) - # Search for the rule. Fail if found. - match = find_rule(rulebase, rule_name) - if match: - if rule_is_match(match, new_rule): - module.exit_json(changed=False, msg='Rule \'%s\' is already in place' % rule_name) - else: - module.fail_json(msg='Rule \'%s\' already exists. Use operation: \'update\' to change it.' % rule_name) - else: - try: - changed = add_rule(rulebase, new_rule) - if changed and commit: - device.commit(sync=True) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - module.exit_json(changed=changed, msg='Rule \'%s\' successfully added' % rule_name) - elif operation == 'update': - # Search for the rule. Update if found. - match = find_rule(rulebase, rule_name) - if match: - try: - new_rule = create_security_rule( - rule_name=rule_name, - description=description, - tag_name=tag_name, - source_zone=source_zone, - destination_zone=destination_zone, - source_ip=source_ip, - source_user=source_user, - destination_ip=destination_ip, - category=category, - application=application, - service=service, - hip_profiles=hip_profiles, - group_profile=group_profile, - antivirus=antivirus, - vulnerability=vulnerability, - spyware=spyware, - url_filtering=url_filtering, - file_blocking=file_blocking, - data_filtering=data_filtering, - wildfire_analysis=wildfire_analysis, - log_start=log_start, - log_end=log_end, - rule_type=rule_type, - action=action - ) - changed = update_rule(rulebase, new_rule) - if changed and commit: - device.commit(sync=True) - except PanXapiError as exc: - module.fail_json(msg=to_native(exc)) - module.exit_json(changed=changed, msg='Rule \'%s\' successfully updated' % rule_name) - else: - module.fail_json(msg='Rule \'%s\' does not exist. Use operation: \'add\' to add it.' % rule_name) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/panos/panos_set.py b/plugins/modules/network/panos/panos_set.py deleted file mode 100644 index 4f83f91561..0000000000 --- a/plugins/modules/network/panos/panos_set.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Ansible module to manage PaloAltoNetworks Firewall -# (c) 2018, Jasper Mackenzie -# -# 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 . - -DOCUMENTATION = ''' ---- -module: panos_set -short_description: Execute arbitrary commands on a PAN-OS device using XPath and element -description: - - Run an arbitrary 'xapi' command taking an XPath (i.e get) or XPath and element (i.e set). - - See https://github.com/kevinsteves/pan-python/blob/master/doc/pan.xapi.rst for details - - Runs a 'set' command by default - - This should support _all_ commands that your PAN-OS device accepts vi it's cli - - cli commands are found as - - Once logged in issue 'debug cli on' - - Enter configuration mode by issuing 'configure' - - Enter your set (or other) command, for example 'set deviceconfig system timezone Australia/Melbourne' - - returns - - > - "Australia/Melbourne - - The 'xpath' is "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system" - - The 'element' is "Australia/Melbourne" -author: "Jasper Mackenzie (@spmp)" -deprecated: - alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. - removed_in: "2.12" - why: Consolidating code base. -requirements: - - pan-python -options: - ip_address: - description: - - IP address or host FQDN of the target PAN-OS NVA - required: true - username: - description: - - User name for a user with admin rights on the PAN-OS NVA - default: admin - password: - description: - - Password for the given 'username' - required: true - command: - description: - - Xapi method name which supports 'xpath' or 'xpath' and 'element' - choices: - - set - - edit - - delete - - get - - show - - override - default: set - xpath: - description: - - The 'xpath' for the commands configurable - required: true - element: - description: - - The 'element' for the 'xpath' if required -extends_documentation_fragment: -- community.general.panos - -''' - -EXAMPLES = ''' - -- name: Set timezone on PA NVA - panos_set: - ip_address: "192.168.1.1" - username: "my-random-admin" - password: "admin1234" - xpath: "/config/devices/entry/deviceconfig/system" - element: "Australia/Melbourne" - -- name: Commit configuration - panos_commit: - ip_address: "192.168.1.1" - username: "my-random-admin" - password: "admin1234" -''' - -RETURN = ''' -# Default return values -''' - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['deprecated'], - 'supported_by': 'community'} - -from ansible.module_utils.basic import AnsibleModule - -try: - import pan.xapi - HAS_LIB = True -except ImportError: - HAS_LIB = False - - -def main(): - argument_spec = dict( - ip_address=dict(required=True), - password=dict(required=True, no_log=True), - username=dict(default='admin'), - command=dict(default='set', choices=['set', 'edit', 'delete', 'get', 'show', 'override']), - xpath=dict(required=True), - element=dict(default=None) - ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - if not HAS_LIB: - module.fail_json(msg='pan-python is required for this module') - - ip_address = module.params["ip_address"] - password = module.params["password"] - username = module.params['username'] - xpath = module.params['xpath'] - element = module.params['element'] - xcommand = module.params['command'] - - xapi = pan.xapi.PanXapi( - hostname=ip_address, - api_username=username, - api_password=password, - timeout=60 - ) - - if element is None: - # Issue command with no `element` - try: - getattr(xapi, xcommand)(xpath=xpath) - except Exception as e: - raise Exception("Failed to run '%s' with xpath: '%s' with the following error: %s" % - (xcommand, xpath, e)) - else: - # Issue command with `element` - try: - getattr(xapi, xcommand)(xpath=xpath, element=element) - except Exception as e: - raise Exception("Failed to run '%s' with xpath: '%s' and element '%s' with the following error: %s" % - (xcommand, xpath, element, e)) - - module.exit_json( - status="success" - ) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/radware/vdirect_commit.py b/plugins/modules/network/radware/vdirect_commit.py deleted file mode 100644 index 900dab0a0a..0000000000 --- a/plugins/modules/network/radware/vdirect_commit.py +++ /dev/null @@ -1,342 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright 2017 Radware LTD. -# -# 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 . - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' -module: vdirect_commit -author: Evgeny Fedoruk @ Radware LTD (@evgenyfedoruk) -short_description: Commits pending configuration changes on Radware devices -description: - - Commits pending configuration changes on one or more Radware devices via vDirect server. - - For Alteon ADC device, apply, sync and save actions will be performed by default. - Skipping of an action is possible by explicit parameter specifying. - - For Alteon VX Container device, no sync operation will be performed - since sync action is only relevant for Alteon ADC devices. - - For DefensePro and AppWall devices, a bulk commit action will be performed. - Explicit apply, sync and save actions specifying is not relevant. -notes: - - Requires the Radware vdirect-client Python package on the host. This is as easy as - C(pip install vdirect-client) -options: - vdirect_ip: - description: - - Primary vDirect server IP address, may be set as C(VDIRECT_IP) environment variable. - required: true - vdirect_user: - description: - - vDirect server username, may be set as C(VDIRECT_USER) environment variable. - required: true - vdirect_password: - description: - - vDirect server password, may be set as C(VDIRECT_PASSWORD) environment variable. - required: true - vdirect_secondary_ip: - description: - - Secondary vDirect server IP address, may be set as C(VDIRECT_SECONDARY_IP) environment variable. - vdirect_wait: - description: - - Wait for async operation to complete, may be set as C(VDIRECT_WAIT) environment variable. - type: bool - default: 'yes' - vdirect_https_port: - description: - - vDirect server HTTPS port number, may be set as C(VDIRECT_HTTPS_PORT) environment variable. - default: 2189 - vdirect_http_port: - description: - - vDirect server HTTP port number, may be set as C(VDIRECT_HTTP_PORT) environment variable. - default: 2188 - vdirect_timeout: - description: - - Amount of time to wait for async operation completion [seconds], - - may be set as C(VDIRECT_TIMEOUT) environment variable. - default: 60 - vdirect_use_ssl: - description: - - If C(no), an HTTP connection will be used instead of the default HTTPS connection, - - may be set as C(VDIRECT_HTTPS) or C(VDIRECT_USE_SSL) environment variable. - type: bool - default: 'yes' - validate_certs: - description: - - If C(no), SSL certificates will not be validated, - - may be set as C(VDIRECT_VALIDATE_CERTS) or C(VDIRECT_VERIFY) environment variable. - - This should only set to C(no) used on personally controlled sites using self-signed certificates. - type: bool - default: 'yes' - aliases: [ vdirect_validate_certs ] - devices: - description: - - List of Radware Alteon device names for commit operations. - required: true - apply: - description: - - If C(no), apply action will not be performed. Relevant for ADC devices only. - type: bool - default: 'yes' - save: - description: - - If C(no), save action will not be performed. Relevant for ADC devices only. - type: bool - default: 'yes' - sync: - description: - - If C(no), sync action will not be performed. Relevant for ADC devices only. - type: bool - default: 'yes' - -requirements: - - "vdirect-client >= 4.9.0-post4" -''' - -EXAMPLES = ''' -- name: vdirect_commit - vdirect_commit: - vdirect_ip: 10.10.10.10 - vdirect_user: vDirect - vdirect_password: radware - devices: ['dev1', 'dev2'] - sync: no -''' - -RETURN = ''' -result: - description: Message detailing actions result - returned: success - type: str - sample: "Requested actions were successfully performed on all devices." -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.basic import env_fallback - -try: - from vdirect_client import rest_client - HAS_REST_CLIENT = True -except ImportError: - HAS_REST_CLIENT = False - - -SUCCESS = 'Requested actions were successfully performed on all devices.' -FAILURE = 'Failure occurred while performing requested actions on devices. See details' - -ADC_DEVICE_TYPE = 'Adc' -CONTAINER_DEVICE_TYPE = 'Container' -PARTITIONED_CONTAINER_DEVICE_TYPE = 'AlteonPartitioned' -APPWALL_DEVICE_TYPE = 'AppWall' -DP_DEVICE_TYPE = 'DefensePro' - -SUCCEEDED = 'succeeded' -FAILED = 'failed' -NOT_PERFORMED = 'not performed' - -meta_args = dict( - vdirect_ip=dict(required=True, fallback=(env_fallback, ['VDIRECT_IP'])), - vdirect_user=dict(required=True, fallback=(env_fallback, ['VDIRECT_USER'])), - vdirect_password=dict( - required=True, fallback=(env_fallback, ['VDIRECT_PASSWORD']), - no_log=True, type='str'), - vdirect_secondary_ip=dict( - required=False, fallback=(env_fallback, ['VDIRECT_SECONDARY_IP']), - default=None), - vdirect_use_ssl=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTPS', 'VDIRECT_USE_SSL']), - default=True, type='bool'), - vdirect_wait=dict( - required=False, fallback=(env_fallback, ['VDIRECT_WAIT']), - default=True, type='bool'), - vdirect_timeout=dict( - required=False, fallback=(env_fallback, ['VDIRECT_TIMEOUT']), - default=60, type='int'), - validate_certs=dict( - required=False, fallback=(env_fallback, ['VDIRECT_VERIFY', 'VDIRECT_VALIDATE_CERTS']), - default=True, type='bool', aliases=['vdirect_validate_certs']), - vdirect_https_port=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTPS_PORT']), - default=2189, type='int'), - vdirect_http_port=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTP_PORT']), - default=2188, type='int'), - devices=dict( - required=True, type='list'), - apply=dict( - required=False, default=True, type='bool'), - save=dict( - required=False, default=True, type='bool'), - sync=dict( - required=False, default=True, type='bool'), -) - - -class CommitException(Exception): - def __init__(self, reason, details): - self.reason = reason - self.details = details - - def __str__(self): - return 'Reason: {0}. Details:{1}.'.format(self.reason, self.details) - - -class MissingDeviceException(CommitException): - def __init__(self, device_name): - super(MissingDeviceException, self).__init__( - 'Device missing', - 'Device ' + repr(device_name) + ' does not exist') - - -class VdirectCommit(object): - def __init__(self, params): - self.client = rest_client.RestClient(params['vdirect_ip'], - params['vdirect_user'], - params['vdirect_password'], - wait=params['vdirect_wait'], - secondary_vdirect_ip=params['vdirect_secondary_ip'], - https_port=params['vdirect_https_port'], - http_port=params['vdirect_http_port'], - timeout=params['vdirect_timeout'], - https=params['vdirect_use_ssl'], - verify=params['validate_certs']) - self.devices = params['devices'] - self.apply = params['apply'] - self.save = params['save'] - self.sync = params['sync'] - self.devicesMap = {} - - def _validate_devices(self): - for device in self.devices: - try: - res = self.client.adc.get(device) - if res[rest_client.RESP_STATUS] == 200: - self.devicesMap.update({device: ADC_DEVICE_TYPE}) - continue - res = self.client.container.get(device) - if res[rest_client.RESP_STATUS] == 200: - if res[rest_client.RESP_DATA]['type'] == PARTITIONED_CONTAINER_DEVICE_TYPE: - self.devicesMap.update({device: CONTAINER_DEVICE_TYPE}) - continue - res = self.client.appWall.get(device) - if res[rest_client.RESP_STATUS] == 200: - self.devicesMap.update({device: APPWALL_DEVICE_TYPE}) - continue - res = self.client.defensePro.get(device) - if res[rest_client.RESP_STATUS] == 200: - self.devicesMap.update({device: DP_DEVICE_TYPE}) - continue - - except Exception as e: - raise CommitException('Failed to communicate with device ' + device, str(e)) - - raise MissingDeviceException(device) - - def _perform_action_and_update_result(self, device, action, perform, failure_occurred, actions_result): - - if not perform or failure_occurred: - actions_result[action] = NOT_PERFORMED - return True - - try: - if self.devicesMap[device] == ADC_DEVICE_TYPE: - res = self.client.adc.control_device(device, action) - elif self.devicesMap[device] == CONTAINER_DEVICE_TYPE: - res = self.client.container.control(device, action) - elif self.devicesMap[device] == APPWALL_DEVICE_TYPE: - res = self.client.appWall.control_device(device, action) - elif self.devicesMap[device] == DP_DEVICE_TYPE: - res = self.client.defensePro.control_device(device, action) - - if res[rest_client.RESP_STATUS] in [200, 204]: - actions_result[action] = SUCCEEDED - else: - actions_result[action] = FAILED - actions_result['failure_description'] = res[rest_client.RESP_STR] - return False - except Exception as e: - actions_result[action] = FAILED - actions_result['failure_description'] = 'Exception occurred while performing '\ - + action + ' action. Exception: ' + str(e) - return False - - return True - - def commit(self): - self._validate_devices() - - result_to_return = dict() - result_to_return['details'] = list() - - for device in self.devices: - failure_occurred = False - device_type = self.devicesMap[device] - actions_result = dict() - actions_result['device_name'] = device - actions_result['device_type'] = device_type - - if device_type in [DP_DEVICE_TYPE, APPWALL_DEVICE_TYPE]: - failure_occurred = not self._perform_action_and_update_result( - device, 'commit', True, failure_occurred, actions_result)\ - or failure_occurred - else: - failure_occurred = not self._perform_action_and_update_result( - device, 'apply', self.apply, failure_occurred, actions_result)\ - or failure_occurred - if device_type != CONTAINER_DEVICE_TYPE: - failure_occurred = not self._perform_action_and_update_result( - device, 'sync', self.sync, failure_occurred, actions_result)\ - or failure_occurred - failure_occurred = not self._perform_action_and_update_result( - device, 'save', self.save, failure_occurred, actions_result)\ - or failure_occurred - - result_to_return['details'].extend([actions_result]) - - if failure_occurred: - result_to_return['msg'] = FAILURE - - if 'msg' not in result_to_return: - result_to_return['msg'] = SUCCESS - - return result_to_return - - -def main(): - - module = AnsibleModule(argument_spec=meta_args) - - if not HAS_REST_CLIENT: - module.fail_json(msg="The python vdirect-client module is required") - - try: - vdirect_commit = VdirectCommit(module.params) - result = vdirect_commit.commit() - result = dict(result=result) - module.exit_json(**result) - except Exception as e: - module.fail_json(msg=str(e)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/radware/vdirect_file.py b/plugins/modules/network/radware/vdirect_file.py deleted file mode 100644 index 889a96e71f..0000000000 --- a/plugins/modules/network/radware/vdirect_file.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright 2017 Radware LTD. -# -# 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 . - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' -module: vdirect_file -author: Evgeny Fedoruk @ Radware LTD (@evgenyfedoruk) -short_description: Uploads a new or updates an existing runnable file into Radware vDirect server -description: - - Uploads a new or updates an existing configuration template or workflow template into the Radware vDirect server. - All parameters may be set as environment variables. -notes: - - Requires the Radware vdirect-client Python package on the host. This is as easy as - C(pip install vdirect-client) -options: - vdirect_ip: - description: - - Primary vDirect server IP address, may be set as VDIRECT_IP environment variable. - required: true - vdirect_user: - description: - - vDirect server username, may be set as VDIRECT_USER environment variable. - required: true - vdirect_password: - description: - - vDirect server password, may be set as VDIRECT_PASSWORD environment variable. - required: true - vdirect_secondary_ip: - description: - - Secondary vDirect server IP address, may be set as VDIRECT_SECONDARY_IP environment variable. - vdirect_wait: - description: - - Wait for async operation to complete, may be set as VDIRECT_WAIT environment variable. - type: bool - default: 'yes' - vdirect_https_port: - description: - - vDirect server HTTPS port number, may be set as VDIRECT_HTTPS_PORT environment variable. - default: 2189 - vdirect_http_port: - description: - - vDirect server HTTP port number, may be set as VDIRECT_HTTP_PORT environment variable. - default: 2188 - vdirect_timeout: - description: - - Amount of time to wait for async operation completion [seconds], - - may be set as VDIRECT_TIMEOUT environment variable. - default: 60 - vdirect_use_ssl: - description: - - If C(no), an HTTP connection will be used instead of the default HTTPS connection, - - may be set as VDIRECT_HTTPS or VDIRECT_USE_SSL environment variable. - type: bool - default: 'yes' - validate_certs: - description: - - If C(no), SSL certificates will not be validated, - - may be set as VDIRECT_VALIDATE_CERTS or VDIRECT_VERIFY environment variable. - - This should only set to C(no) used on personally controlled sites using self-signed certificates. - type: bool - default: 'yes' - aliases: [ vdirect_validate_certs ] - file_name: - description: - - vDirect runnable file name to be uploaded. - - May be velocity configuration template (.vm) or workflow template zip file (.zip). - required: true - -requirements: - - "vdirect-client >= 4.9.0-post4" -''' - -EXAMPLES = ''' -- name: vdirect_file - vdirect_file: - vdirect_ip: 10.10.10.10 - vdirect_user: vDirect - vdirect_password: radware - file_name: /tmp/get_vlans.vm -''' - -RETURN = ''' -result: - description: Message detailing upload result - returned: success - type: str - sample: "Workflow template created" -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.basic import env_fallback -import os -import os.path - -try: - from vdirect_client import rest_client - HAS_REST_CLIENT = True -except ImportError: - HAS_REST_CLIENT = False - -TEMPLATE_EXTENSION = '.vm' -WORKFLOW_EXTENSION = '.zip' -WRONG_EXTENSION_ERROR = 'The file_name parameter must have ' \ - 'velocity script (.vm) extension or ZIP archive (.zip) extension' -CONFIGURATION_TEMPLATE_CREATED_SUCCESS = 'Configuration template created' -CONFIGURATION_TEMPLATE_UPDATED_SUCCESS = 'Configuration template updated' -WORKFLOW_TEMPLATE_CREATED_SUCCESS = 'Workflow template created' -WORKFLOW_TEMPLATE_UPDATED_SUCCESS = 'Workflow template updated' - -meta_args = dict( - vdirect_ip=dict(required=True, fallback=(env_fallback, ['VDIRECT_IP'])), - vdirect_user=dict(required=True, fallback=(env_fallback, ['VDIRECT_USER'])), - vdirect_password=dict( - required=True, fallback=(env_fallback, ['VDIRECT_PASSWORD']), - no_log=True, type='str'), - vdirect_secondary_ip=dict( - required=False, fallback=(env_fallback, ['VDIRECT_SECONDARY_IP']), - default=None), - vdirect_use_ssl=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTPS', 'VDIRECT_USE_SSL']), - default=True, type='bool'), - vdirect_wait=dict( - required=False, fallback=(env_fallback, ['VDIRECT_WAIT']), - default=True, type='bool'), - vdirect_timeout=dict( - required=False, fallback=(env_fallback, ['VDIRECT_TIMEOUT']), - default=60, type='int'), - validate_certs=dict( - required=False, fallback=(env_fallback, ['VDIRECT_VERIFY', 'VDIRECT_VALIDATE_CERTS']), - default=True, type='bool', aliases=['vdirect_validate_certs']), - vdirect_https_port=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTPS_PORT']), - default=2189, type='int'), - vdirect_http_port=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTP_PORT']), - default=2188, type='int'), - file_name=dict(required=True) -) - - -class FileException(Exception): - def __init__(self, reason, details): - self.reason = reason - self.details = details - - def __str__(self): - return 'Reason: {0}. Details:{1}.'.format(self.reason, self.details) - - -class InvalidSourceException(FileException): - def __init__(self, message): - super(InvalidSourceException, self).__init__( - 'Error parsing file', repr(message)) - - -class VdirectFile(object): - def __init__(self, params): - self.client = rest_client.RestClient(params['vdirect_ip'], - params['vdirect_user'], - params['vdirect_password'], - wait=params['vdirect_wait'], - secondary_vdirect_ip=params['vdirect_secondary_ip'], - https_port=params['vdirect_https_port'], - http_port=params['vdirect_http_port'], - timeout=params['vdirect_timeout'], - https=params['vdirect_use_ssl'], - verify=params['validate_certs']) - - def upload(self, fqn): - if fqn.endswith(TEMPLATE_EXTENSION): - template_name = os.path.basename(fqn) - template = rest_client.Template(self.client) - runnable_file = open(fqn, 'r') - file_content = runnable_file.read() - - result_to_return = CONFIGURATION_TEMPLATE_CREATED_SUCCESS - result = template.create_from_source(file_content, template_name, fail_if_invalid=True) - if result[rest_client.RESP_STATUS] == 409: - result_to_return = CONFIGURATION_TEMPLATE_UPDATED_SUCCESS - result = template.upload_source(file_content, template_name, fail_if_invalid=True) - - if result[rest_client.RESP_STATUS] == 400: - raise InvalidSourceException(str(result[rest_client.RESP_STR])) - elif fqn.endswith(WORKFLOW_EXTENSION): - workflow = rest_client.WorkflowTemplate(self.client) - - runnable_file = open(fqn, 'rb') - file_content = runnable_file.read() - - result_to_return = WORKFLOW_TEMPLATE_CREATED_SUCCESS - result = workflow.create_template_from_archive(file_content, fail_if_invalid=True) - if result[rest_client.RESP_STATUS] == 409: - result_to_return = WORKFLOW_TEMPLATE_UPDATED_SUCCESS - result = workflow.update_archive(file_content, os.path.splitext(os.path.basename(fqn))[0]) - - if result[rest_client.RESP_STATUS] == 400: - raise InvalidSourceException(str(result[rest_client.RESP_STR])) - else: - result_to_return = WRONG_EXTENSION_ERROR - return result_to_return - - -def main(): - - module = AnsibleModule(argument_spec=meta_args) - - if not HAS_REST_CLIENT: - module.fail_json(msg="The python vdirect-client module is required") - - try: - vdirect_file = VdirectFile(module.params) - result = vdirect_file.upload(module.params['file_name']) - result = dict(result=result) - module.exit_json(**result) - except Exception as e: - module.fail_json(msg=str(e)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/radware/vdirect_runnable.py b/plugins/modules/network/radware/vdirect_runnable.py deleted file mode 100644 index df2fce506c..0000000000 --- a/plugins/modules/network/radware/vdirect_runnable.py +++ /dev/null @@ -1,340 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright 2017 Radware LTD. -# -# 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 . - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'metadata_version': '1.1'} - -DOCUMENTATION = ''' -module: vdirect_runnable -author: Evgeny Fedoruk @ Radware LTD (@evgenyfedoruk) -short_description: Runs templates and workflow actions in Radware vDirect server -description: - - Runs configuration templates, creates workflows and runs workflow actions in Radware vDirect server. -notes: - - Requires the Radware vdirect-client Python package on the host. This is as easy as - C(pip install vdirect-client) -options: - vdirect_ip: - description: - - Primary vDirect server IP address, may be set as C(VDIRECT_IP) environment variable. - required: true - vdirect_user: - description: - - vDirect server username, may be set as C(VDIRECT_USER) environment variable. - required: true - vdirect_password: - description: - - vDirect server password, may be set as C(VDIRECT_PASSWORD) environment variable. - required: true - vdirect_secondary_ip: - description: - - Secondary vDirect server IP address, may be set as C(VDIRECT_SECONDARY_IP) environment variable. - vdirect_wait: - description: - - Wait for async operation to complete, may be set as C(VDIRECT_WAIT) environment variable. - type: bool - default: 'yes' - vdirect_https_port: - description: - - vDirect server HTTPS port number, may be set as C(VDIRECT_HTTPS_PORT) environment variable. - default: 2189 - vdirect_http_port: - description: - - vDirect server HTTP port number, may be set as C(VDIRECT_HTTP_PORT) environment variable. - default: 2188 - vdirect_timeout: - description: - - Amount of time to wait for async operation completion [seconds], - - may be set as C(VDIRECT_TIMEOUT) environment variable. - default: 60 - vdirect_use_ssl: - description: - - If C(no), an HTTP connection will be used instead of the default HTTPS connection, - - may be set as C(VDIRECT_HTTPS) or C(VDIRECT_USE_SSL) environment variable. - type: bool - default: 'yes' - validate_certs: - description: - - If C(no), SSL certificates will not be validated, - - may be set as C(VDIRECT_VALIDATE_CERTS) or C(VDIRECT_VERIFY) environment variable. - - This should only set to C(no) used on personally controlled sites using self-signed certificates. - type: bool - default: 'yes' - aliases: [ vdirect_validate_certs ] - runnable_type: - description: - - vDirect runnable type. - required: true - choices: ['ConfigurationTemplate', 'Workflow', 'WorkflowTemplate', 'Plugin'] - runnable_name: - description: - - vDirect runnable name to run. - - May be configuration template name, workflow template name or workflow instance name. - required: true - action_name: - description: - - Workflow action name to run. - - Required if I(runnable_type=Workflow). - parameters: - description: - - Action parameters dictionary. In case of C(ConfigurationTemplate) runnable type, - - the device connection details should always be passed as a parameter. - -requirements: - - "vdirect-client >= 4.9.0-post4" -''' - -EXAMPLES = ''' -- name: vdirect_runnable - vdirect_runnable: - vdirect_ip: 10.10.10.10 - vdirect_user: vDirect - vdirect_password: radware - runnable_type: ConfigurationTemplate - runnable_name: get_vlans - parameters: {'vlans_needed':1,'adc':[{'type':'Adc','name':'adc-1'}]} -''' - -RETURN = ''' -result: - description: Message detailing run result - returned: success - type: str - sample: "Workflow action run completed." -''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.basic import env_fallback - -try: - from vdirect_client import rest_client - HAS_REST_CLIENT = True -except ImportError: - HAS_REST_CLIENT = False - -CONFIGURATION_TEMPLATE_RUNNABLE_TYPE = 'ConfigurationTemplate' -WORKFLOW_TEMPLATE_RUNNABLE_TYPE = 'WorkflowTemplate' -WORKFLOW_RUNNABLE_TYPE = 'Workflow' -PLUGIN_RUNNABLE_TYPE = 'Plugin' - -TEMPLATE_SUCCESS = 'Configuration template run completed.' -WORKFLOW_CREATION_SUCCESS = 'Workflow created.' -WORKFLOW_ACTION_SUCCESS = 'Workflow action run completed.' -PLUGIN_ACTION_SUCCESS = 'Plugin action run completed.' - -meta_args = dict( - vdirect_ip=dict(required=True, fallback=(env_fallback, ['VDIRECT_IP'])), - vdirect_user=dict(required=True, fallback=(env_fallback, ['VDIRECT_USER'])), - vdirect_password=dict( - required=True, fallback=(env_fallback, ['VDIRECT_PASSWORD']), - no_log=True, type='str'), - vdirect_secondary_ip=dict( - required=False, fallback=(env_fallback, ['VDIRECT_SECONDARY_IP']), - default=None), - vdirect_use_ssl=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTPS', 'VDIRECT_USE_SSL']), - default=True, type='bool'), - vdirect_wait=dict( - required=False, fallback=(env_fallback, ['VDIRECT_WAIT']), - default=True, type='bool'), - vdirect_timeout=dict( - required=False, fallback=(env_fallback, ['VDIRECT_TIMEOUT']), - default=60, type='int'), - validate_certs=dict( - required=False, fallback=(env_fallback, ['VDIRECT_VERIFY', 'VDIRECT_VALIDATE_CERTS']), - default=True, type='bool', aliases=['vdirect_validate_certs']), - vdirect_https_port=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTPS_PORT']), - default=2189, type='int'), - vdirect_http_port=dict( - required=False, fallback=(env_fallback, ['VDIRECT_HTTP_PORT']), - default=2188, type='int'), - runnable_type=dict( - required=True, - choices=[CONFIGURATION_TEMPLATE_RUNNABLE_TYPE, WORKFLOW_TEMPLATE_RUNNABLE_TYPE, WORKFLOW_RUNNABLE_TYPE, PLUGIN_RUNNABLE_TYPE]), - runnable_name=dict(required=True), - action_name=dict(required=False, default=None), - parameters=dict(required=False, type='dict', default={}) -) - - -class RunnableException(Exception): - def __init__(self, reason, details): - self.reason = reason - self.details = details - - def __str__(self): - return 'Reason: {0}. Details:{1}.'.format(self.reason, self.details) - - -class WrongActionNameException(RunnableException): - def __init__(self, action, available_actions): - super(WrongActionNameException, self).__init__('Wrong action name ' + repr(action), - 'Available actions are: ' + repr(available_actions)) - - -class MissingActionParametersException(RunnableException): - def __init__(self, required_parameters): - super(MissingActionParametersException, self).__init__( - 'Action parameters missing', - 'Required parameters are: ' + repr(required_parameters)) - - -class MissingRunnableException(RunnableException): - def __init__(self, name): - super(MissingRunnableException, self).__init__( - 'Runnable missing', - 'Runnable ' + name + ' is missing') - - -class VdirectRunnable(object): - - CREATE_WORKFLOW_ACTION = 'createWorkflow' - RUN_ACTION = 'run' - - def __init__(self, params): - self.client = rest_client.RestClient(params['vdirect_ip'], - params['vdirect_user'], - params['vdirect_password'], - wait=params['vdirect_wait'], - secondary_vdirect_ip=params['vdirect_secondary_ip'], - https_port=params['vdirect_https_port'], - http_port=params['vdirect_http_port'], - timeout=params['vdirect_timeout'], - strict_http_results=True, - https=params['vdirect_use_ssl'], - verify=params['validate_certs']) - self.params = params - self.type = self.params['runnable_type'] - self.name = self.params['runnable_name'] - - if self.type == WORKFLOW_TEMPLATE_RUNNABLE_TYPE: - self.action_name = VdirectRunnable.CREATE_WORKFLOW_ACTION - elif self.type == CONFIGURATION_TEMPLATE_RUNNABLE_TYPE: - self.action_name = VdirectRunnable.RUN_ACTION - else: - self.action_name = self.params['action_name'] - - if 'parameters' in self.params and self.params['parameters']: - self.action_params = self.params['parameters'] - else: - self.action_params = {} - - def _validate_runnable_exists(self): - if self.type == WORKFLOW_RUNNABLE_TYPE: - res = self.client.runnable.get_runnable_objects(self.type) - runnable_names = res[rest_client.RESP_DATA]['names'] - if self.name not in runnable_names: - raise MissingRunnableException(self.name) - else: - try: - self.client.catalog.get_catalog_item(self.type, self.name) - except rest_client.RestClientException: - raise MissingRunnableException(self.name) - - def _validate_action_name(self): - if self.type in [WORKFLOW_RUNNABLE_TYPE, PLUGIN_RUNNABLE_TYPE]: - res = self.client.runnable.get_available_actions(self.type, self.name) - available_actions = res[rest_client.RESP_DATA]['names'] - if self.action_name not in available_actions: - raise WrongActionNameException(self.action_name, available_actions) - - def _validate_required_action_params(self): - action_params_names = [n for n in self.action_params] - - res = self.client.runnable.get_action_info(self.type, self.name, self.action_name) - if 'parameters' in res[rest_client.RESP_DATA]: - action_params_spec = res[rest_client.RESP_DATA]['parameters'] - else: - action_params_spec = [] - - required_action_params_dict = [{'name': p['name'], 'type': p['type']} for p in action_params_spec - if p['type'] == 'alteon' or - p['type'] == 'defensePro' or - p['type'] == 'appWall' or - p['type'] == 'alteon[]' or - p['type'] == 'defensePro[]' or - p['type'] == 'appWall[]' or - p['direction'] != 'out'] - required_action_params_names = [n['name'] for n in required_action_params_dict] - - if set(required_action_params_names) & set(action_params_names) != set(required_action_params_names): - raise MissingActionParametersException(required_action_params_dict) - - def run(self): - self._validate_runnable_exists() - self._validate_action_name() - self._validate_required_action_params() - - data = self.action_params - - result = self.client.runnable.run(data, self.type, self.name, self.action_name) - result_to_return = {'msg': ''} - if result[rest_client.RESP_STATUS] == 200: - if result[rest_client.RESP_DATA]['success']: - if self.type == WORKFLOW_TEMPLATE_RUNNABLE_TYPE: - result_to_return['msg'] = WORKFLOW_CREATION_SUCCESS - elif self.type == CONFIGURATION_TEMPLATE_RUNNABLE_TYPE: - result_to_return['msg'] = TEMPLATE_SUCCESS - elif self.type == PLUGIN_RUNNABLE_TYPE: - result_to_return['msg'] = PLUGIN_ACTION_SUCCESS - else: - result_to_return['msg'] = WORKFLOW_ACTION_SUCCESS - result_to_return['output'] = result[rest_client.RESP_DATA] - - else: - if 'exception' in result[rest_client.RESP_DATA]: - raise RunnableException(result[rest_client.RESP_DATA]['exception']['message'], - result[rest_client.RESP_STR]) - else: - raise RunnableException('The status returned ' + str(result[rest_client.RESP_DATA]['status']), - result[rest_client.RESP_STR]) - else: - raise RunnableException(result[rest_client.RESP_REASON], - result[rest_client.RESP_STR]) - - return result_to_return - - -def main(): - - module = AnsibleModule(argument_spec=meta_args, - required_if=[['runnable_type', WORKFLOW_RUNNABLE_TYPE, ['action_name']], - ['runnable_type', PLUGIN_RUNNABLE_TYPE, ['action_name']]]) - - if not HAS_REST_CLIENT: - module.fail_json(msg="The python vdirect-client module is required") - - try: - vdirect_runnable = VdirectRunnable(module.params) - result = vdirect_runnable.run() - result = dict(result=result) - module.exit_json(**result) - except Exception as e: - module.fail_json(msg=str(e)) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/routeros/routeros_command.py b/plugins/modules/network/routeros/routeros_command.py deleted file mode 100644 index 0ac0edeeb0..0000000000 --- a/plugins/modules/network/routeros/routeros_command.py +++ /dev/null @@ -1,186 +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) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: routeros_command -author: "Egor Zaitsev (@heuels)" -short_description: Run commands on remote devices running MikroTik RouterOS -description: - - Sends arbitrary commands to an RouterOS node and returns the results - read from the device. This module includes an - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. -options: - commands: - description: - - List of commands to send to the remote RouterOS 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. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - 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. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -tasks: - - name: run command on remote devices - routeros_command: - commands: /system routerboard print - - - name: run command and check to see if output contains routeros - routeros_command: - commands: /system resource print - wait_for: result[0] contains MikroTik - - - name: run multiple commands on remote nodes - routeros_command: - commands: - - /system routerboard print - - /system identity print - - - name: run multiple commands and evaluate the output - routeros_command: - commands: - - /system routerboard print - - /interface ethernet print - wait_for: - - result[0] contains x86 - - result[1] contains ether1 -""" - -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: ['...', '...'] -""" - -import re -import time - -from ansible_collections.community.general.plugins.module_utils.network.routeros.routeros import run_commands -from ansible_collections.community.general.plugins.module_utils.network.routeros.routeros import routeros_argument_spec -from ansible.module_utils.basic import AnsibleModule -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 main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - argument_spec.update(routeros_argument_spec) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - 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, module.params['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/plugins/modules/network/routeros/routeros_facts.py b/plugins/modules/network/routeros/routeros_facts.py deleted file mode 100644 index b7487629ba..0000000000 --- a/plugins/modules/network/routeros/routeros_facts.py +++ /dev/null @@ -1,436 +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) -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: routeros_facts -author: "Egor Zaitsev (@heuels)" -short_description: Collect facts from remote devices running MikroTik RouterOS -description: - - Collects a base set of device facts from a remote device that - is running RotuerOS. This module prepends all of the - base network fact keys with C(ansible_net_). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - C(all), C(hardware), C(config), and C(interfaces). Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(!) to specify that a specific subset should - not be collected. - required: false - default: '!config' -''' - -EXAMPLES = """ -# Collect all facts from the device -- routeros_facts: - gather_subset: all - -# Collect only the config and default facts -- routeros_facts: - gather_subset: - - config - -# Do not collect hardware facts -- routeros_facts: - gather_subset: - - "!hardware" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - -# hardware -ansible_net_spacefree_mb: - description: The available disk space on the remote device in MiB - returned: when hardware is configured - type: dict -ansible_net_spacetotal_mb: - description: The total disk space on the remote device in MiB - returned: when hardware is configured - type: dict -ansible_net_memfree_mb: - description: The available free memory on the remote device in MiB - returned: when hardware is configured - type: int -ansible_net_memtotal_mb: - description: The total memory on the remote device in MiB - 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 neighbors from the remote device - returned: when interfaces is configured - type: dict -""" -import re - -from ansible_collections.community.general.plugins.module_utils.network.routeros.routeros import run_commands -from ansible_collections.community.general.plugins.module_utils.network.routeros.routeros import routeros_argument_spec -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, commands=self.COMMANDS, check_rc=False) - - def run(self, cmd): - return run_commands(self.module, commands=cmd, check_rc=False) - - -class Default(FactsBase): - - COMMANDS = [ - '/system identity print without-paging', - '/system resource print without-paging', - '/system routerboard print without-paging' - ] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['hostname'] = self.parse_hostname(data) - - data = self.responses[1] - if data: - self.facts['version'] = self.parse_version(data) - - data = self.responses[2] - if data: - self.facts['model'] = self.parse_model(data) - self.facts['serialnum'] = self.parse_serialnum(data) - - def parse_hostname(self, data): - match = re.search(r'name:\s(.*)\s*$', data, re.M) - if match: - return match.group(1) - - def parse_version(self, data): - match = re.search(r'version:\s(.*)\s*$', data, re.M) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'model:\s(.*)\s*$', data, re.M) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'serial-number:\s(.*)\s*$', data, re.M) - if match: - return match.group(1) - - -class Hardware(FactsBase): - - COMMANDS = [ - '/system resource print without-paging' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - if data: - self.parse_filesystem_info(data) - self.parse_memory_info(data) - - def parse_filesystem_info(self, data): - match = re.search(r'free-hdd-space:\s(.*)([KMG]iB)', data, re.M) - if match: - self.facts['spacefree_mb'] = self.to_megabytes(match) - match = re.search(r'total-hdd-space:\s(.*)([KMG]iB)', data, re.M) - if match: - self.facts['spacetotal_mb'] = self.to_megabytes(match) - - def parse_memory_info(self, data): - match = re.search(r'free-memory:\s(\d+\.?\d*)([KMG]iB)', data, re.M) - if match: - self.facts['memfree_mb'] = self.to_megabytes(match) - match = re.search(r'total-memory:\s(\d+\.?\d*)([KMG]iB)', data, re.M) - if match: - self.facts['memtotal_mb'] = self.to_megabytes(match) - - def to_megabytes(self, data): - if data.group(2) == 'KiB': - return float(data.group(1)) / 1024 - elif data.group(2) == 'MiB': - return float(data.group(1)) - elif data.group(2) == 'GiB': - return float(data.group(1)) * 1024 - else: - return None - - -class Config(FactsBase): - - COMMANDS = ['/export'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = [ - '/interface print detail without-paging', - '/ip address print detail without-paging', - '/ipv6 address print detail without-paging', - '/ip neighbor print detail without-paging' - ] - - DETAIL_RE = re.compile(r'([\w\d\-]+)=\"?(\w{3}/\d{2}/\d{4}\s\d{2}:\d{2}:\d{2}|[\w\d\-\.:/]+)') - WRAPPED_LINE_RE = re.compile(r'^\s+(?!\d)') - - def populate(self): - super(Interfaces, self).populate() - - self.facts['interfaces'] = dict() - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - self.facts['neighbors'] = dict() - - data = self.responses[0] - if data: - interfaces = self.parse_interfaces(data) - self.populate_interfaces(interfaces) - - data = self.responses[1] - if data: - data = self.parse_addresses(data) - self.populate_ipv4_interfaces(data) - - data = self.responses[2] - if data: - data = self.parse_addresses(data) - self.populate_ipv6_interfaces(data) - - data = self.responses[3] - if data: - self.facts['neighbors'] = self.parse_neighbors(data) - - def populate_interfaces(self, data): - for key, value in iteritems(data): - self.facts['interfaces'][key] = value - - def populate_ipv4_interfaces(self, data): - for key, value in iteritems(data): - if 'ipv4' not in self.facts['interfaces'][key]: - self.facts['interfaces'][key]['ipv4'] = list() - addr, subnet = value['address'].split("/") - ipv4 = dict(address=addr.strip(), subnet=subnet.strip()) - self.add_ip_address(addr.strip(), 'ipv4') - self.facts['interfaces'][key]['ipv4'].append(ipv4) - - def populate_ipv6_interfaces(self, data): - for key, value in iteritems(data): - if key is None: - break - if 'ipv6' not in self.facts['interfaces'][key]: - self.facts['interfaces'][key]['ipv6'] = list() - addr, subnet = value['address'].split("/") - ipv6 = dict(address=addr.strip(), subnet=subnet.strip()) - self.add_ip_address(addr.strip(), 'ipv6') - self.facts['interfaces'][key]['ipv6'].append(ipv6) - - 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 preprocess(self, data): - preprocessed = list() - for line in data.split('\n'): - if len(line) == 0 or line[:5] == 'Flags': - continue - elif not re.match(self.WRAPPED_LINE_RE, line): - preprocessed.append(line) - else: - preprocessed[-1] += line - return preprocessed - - def parse_interfaces(self, data): - facts = dict() - data = self.preprocess(data) - for line in data: - name = self.parse_name(line) - facts[name] = dict() - for (key, value) in re.findall(self.DETAIL_RE, line): - facts[name][key] = value - return facts - - def parse_addresses(self, data): - facts = dict() - data = self.preprocess(data) - for line in data: - name = self.parse_interface(line) - facts[name] = dict() - for (key, value) in re.findall(self.DETAIL_RE, line): - facts[name][key] = value - return facts - - def parse_neighbors(self, data): - facts = dict() - data = self.preprocess(data) - for line in data: - name = self.parse_interface(line) - facts[name] = dict() - for (key, value) in re.findall(self.DETAIL_RE, line): - facts[name][key] = value - return facts - - def parse_name(self, data): - match = re.search(r'name=\"([\w\d\-]+)\"', data, re.M) - if match: - return match.group(1) - - def parse_interface(self, data): - match = re.search(r'interface=([\w\d\-]+)', data, re.M) - if match: - return match.group(1) - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, -) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - -warnings = list() - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=['!config'], type='list') - ) - - argument_spec.update(routeros_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: %s' % subset) - - if exclude: - exclude_subsets.add(subset) - else: - runable_subsets.add(subset) - - if not runable_subsets: - runable_subsets.update(VALID_SUBSETS) - - runable_subsets.difference_update(exclude_subsets) - runable_subsets.add('default') - - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - instances.append(FACT_SUBSETS[key](module)) - - for inst in instances: - inst.populate() - facts.update(inst.facts) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/slxos/slxos_command.py b/plugins/modules/network/slxos/slxos_command.py deleted file mode 100644 index e8a88ef7e3..0000000000 --- a/plugins/modules/network/slxos/slxos_command.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2018, Extreme Networks 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) -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: slxos_command -author: "Lindsay Hill (@LindsayHill)" -short_description: Run commands on remote devices running Extreme Networks SLX-OS -description: - - Sends arbitrary commands to an SLX node and returns the results - read from the device. This module includes an - argument that will cause the module to wait for a specific condition - before returning or timing out if the condition is not met. - - This module does not support running commands in configuration mode. - Please use M(slxos_config) to configure SLX-OS devices. -notes: - - Tested against SLX-OS 17s.1.02 - - If a command sent to the device requires answering a prompt, it is possible - to pass a dict containing I(command), I(answer) and I(prompt). See examples. -options: - commands: - description: - - List of commands to send to the remote SLX-OS 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. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - 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. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -tasks: - - name: run show version on remote devices - slxos_command: - commands: show version - - - name: run show version and check to see if output contains SLX - slxos_command: - commands: show version - wait_for: result[0] contains SLX - - - name: run multiple commands on remote nodes - slxos_command: - commands: - - show version - - show interfaces - - - name: run multiple commands and evaluate the output - slxos_command: - commands: - - show version - - show interface status - wait_for: - - result[0] contains SLX - - result[1] contains Eth - - name: run command that requires answering a prompt - slxos_command: - commands: - - command: 'clear sessions' - prompt: 'This operation will logout all the user sessions. Do you want to continue (yes/no)?:' - answer: y -""" - -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: ['...', '...'] -""" -import re -import time - -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import run_commands -from ansible.module_utils.basic import AnsibleModule -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 - - -__metaclass__ = type - - -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 item in list(commands): - configure_type = re.match(r'conf(?:\w*)(?:\s+(\w+))?', item['command']) - if module.check_mode: - if configure_type and configure_type.group(1) not in ('confirm', 'replace', 'revert', 'network'): - module.fail_json( - msg='slxos_command does not support running config mode ' - 'commands. Please use slxos_config instead' - ) - if not item['command'].startswith('show'): - warnings.append( - 'only show commands are supported when using check mode, not ' - 'executing `%s`' % item['command'] - ) - commands.remove(item) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - 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/plugins/modules/network/slxos/slxos_config.py b/plugins/modules/network/slxos/slxos_config.py deleted file mode 100644 index acd887e3de..0000000000 --- a/plugins/modules/network/slxos/slxos_config.py +++ /dev/null @@ -1,465 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2018, Extreme Networks 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) -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: slxos_config -author: "Lindsay Hill (@LindsayHill)" -short_description: Manage Extreme Networks SLX-OS configuration sections -description: - - Extreme SLX-OS configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with SLX-OS configuration sections in - a deterministic way. -notes: - - Tested against SLX-OS 17s.1.02 -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. - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - 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. - 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. - 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. - 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. - default: line - choices: ['line', 'block'] - multiline_delimiter: - description: - - This argument is used when pushing a multiline configuration - element to the SLX-OS device. It specifies the character to use - as the delimiting character. This only applies to the - configuration action. - default: "@" - backup: - description: - - This argument will cause the module to create a full backup of - the current C(running-config) from the remote device before any - changes are made. 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' - running_config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(running_config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - aliases: ['config'] - defaults: - description: - - This argument specifies whether or not to collect all defaults - when getting the remote device running config. When enabled, - the module will get the current config by issuing the command - C(show running-config all). - type: bool - default: 'no' - save_when: - description: - - When changes are made to the device running-configuration, the - changes are not copied to non-volatile storage by default. Using - this argument will change that before. If the argument is set to - I(always), then the running-config will always be copied to the - startup-config and the I(modified) flag will always be set to - True. If the argument is set to I(modified), then the running-config - will only be copied to the startup-config if it has changed since - the last save to startup-config. If the argument is set to - I(never), the running-config will never be copied to the - startup-config. If the argument is set to I(changed), then the running-config - will only be copied to the startup-config if the task has made a change. - default: never - choices: ['always', 'never', 'modified', 'changed'] - diff_against: - description: - - When using the C(ansible-playbook --diff) command line argument - the module can generate diffs against different sources. - - When this option is configure as I(startup), the module will return - the diff of the running-config against the startup-config. - - When this option is configured as I(intended), the module will - return the diff of the running-config against the configuration - provided in the C(intended_config) argument. - - When this option is configured as I(running), the module will - return the before and after diff of the running-config with respect - to any changes made to the device configuration. - choices: ['running', 'startup', 'intended'] - diff_ignore_lines: - description: - - Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - intended_config: - description: - - The C(intended_config) provides the master configuration that - the node should conform to and is used to check the final - running-config against. This argument will not modify any settings - on the remote device and is strictly used to check the compliance - of the current device's configuration against. When specifying this - argument, the task should also modify the C(diff_against) value and - set it to I(intended). - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- name: configure top level configuration - slxos_config: - lines: hostname {{ inventory_hostname }} - -- name: configure interface settings - slxos_config: - lines: - - description test interface - - ip address 172.31.1.1/24 - parents: interface Ethernet 0/1 - -- name: configure multiple interfaces - slxos_config: - lines: - - lacp timeout long - parents: "{{ item }}" - with_items: - - interface Ethernet 0/1 - - interface Ethernet 0/2 - -- name: load new acl into device - slxos_config: - lines: - - seq 10 permit ip host 1.1.1.1 any log - - seq 20 permit ip host 2.2.2.2 any log - - seq 30 permit ip host 3.3.3.3 any log - - seq 40 permit ip host 4.4.4.4 any log - - seq 50 permit ip host 5.5.5.5 any log - parents: ip access-list extended test - before: no ip access-list extended test - match: exact - -- name: check the running-config against master config - slxos_config: - diff_against: intended - intended_config: "{{ lookup('file', 'master.cfg') }}" - -- name: check the startup-config against the running-config - slxos_config: - diff_against: startup - diff_ignore_lines: - - ntp clock .* - -- name: save running to startup when modified - slxos_config: - save_when: modified - -- name: configurable backup path - slxos_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: ['switch-attributes hostname foo', 'router ospf', 'area 0'] -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['switch-attributes hostname foo', 'router ospf', 'area 0'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/slxos_config.2018-02-12@18:26:34 -""" - -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import run_commands, get_config, load_config -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps - -__metaclass__ = type - - -def check_args(module, warnings): - if module.params['multiline_delimiter']: - if len(module.params['multiline_delimiter']) != 1: - module.fail_json(msg='multiline_delimiter value can only be a ' - 'single character') - - -def get_running_config(module, current_config=None): - contents = module.params['running_config'] - if not contents: - if current_config: - contents = current_config.config_text - else: - contents = get_config(module) - return NetworkConfig(indent=1, contents=contents) - - -def get_candidate(module): - candidate = NetworkConfig(indent=1) - - if module.params['src']: - src = module.params['src'] - candidate.load(src) - - elif module.params['lines']: - parents = module.params['parents'] or list() - candidate.add(module.params['lines'], parents=parents) - - return candidate - - -def save_config(module, result): - result['changed'] = True - if not module.check_mode: - command = {"command": "copy running-config startup-config", - "prompt": "This operation will modify your startup configuration. Do you want to continue", "answer": "y"} - run_commands(module, command) - else: - module.warn('Skipping command `copy running-config startup-config` ' - 'due to check_mode. Configuration not copied to ' - 'non-volatile storage') - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - multiline_delimiter=dict(default='@'), - - running_config=dict(aliases=['config']), - intended_config=dict(), - - defaults=dict(type='bool', default=False), - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - - save_when=dict(choices=['always', 'never', 'modified', 'changed'], default='never'), - - diff_against=dict(choices=['startup', 'intended', 'running']), - diff_ignore_lines=dict(type='list'), - ) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('diff_against', 'intended', ['intended_config'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - check_args(module, warnings) - result['warnings'] = warnings - - config = None - - if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'): - contents = get_config(module) - config = NetworkConfig(indent=1, contents=contents) - if module.params['backup']: - result['__backup__'] = contents - - if any((module.params['lines'], module.params['src'])): - match = module.params['match'] - replace = module.params['replace'] - path = module.params['parents'] - - candidate = get_candidate(module) - - if match != 'none': - config = get_running_config(module, config) - path = module.params['parents'] - configobjs = candidate.difference(config, path=path, match=match, replace=replace) - else: - configobjs = candidate.items - - if configobjs: - commands = dumps(configobjs, 'commands').split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - result['updates'] = commands - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - if commands: - load_config(module, commands) - - result['changed'] = True - - running_config = None - startup_config = None - - diff_ignore_lines = module.params['diff_ignore_lines'] - - if module.params['save_when'] == 'always': - save_config(module, result) - elif module.params['save_when'] == 'modified': - output = run_commands(module, ['show running-config', 'show startup-config']) - - running_config = NetworkConfig(indent=1, contents=output[0], ignore_lines=diff_ignore_lines) - startup_config = NetworkConfig(indent=1, contents=output[1], ignore_lines=diff_ignore_lines) - - if running_config.sha1 != startup_config.sha1: - save_config(module, result) - elif module.params['save_when'] == 'changed' and result['changed']: - save_config(module, result) - - if module._diff: - if not running_config: - output = run_commands(module, 'show running-config') - contents = output[0] - else: - contents = running_config.config_text - - # recreate the object in order to process diff_ignore_lines - running_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if module.params['diff_against'] == 'running': - if module.check_mode: - module.warn("unable to perform diff against running-config due to check mode") - contents = None - else: - contents = config.config_text - - elif module.params['diff_against'] == 'startup': - if not startup_config: - output = run_commands(module, 'show startup-config') - contents = output[0] - else: - contents = startup_config.config_text - - elif module.params['diff_against'] == 'intended': - contents = module.params['intended_config'] - - if contents is not None: - base_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines) - - if running_config.sha1 != base_config.sha1: - if module.params['diff_against'] == 'intended': - before = running_config - after = base_config - elif module.params['diff_against'] in ('startup', 'running'): - before = base_config - after = running_config - - result.update({ - 'changed': True, - 'diff': {'before': str(before), 'after': str(after)} - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/slxos/slxos_facts.py b/plugins/modules/network/slxos/slxos_facts.py deleted file mode 100644 index 7408965ada..0000000000 --- a/plugins/modules/network/slxos/slxos_facts.py +++ /dev/null @@ -1,456 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: slxos_facts -author: "Lindsay Hill (@LindsayHill)" -short_description: Collect facts from devices running Extreme SLX-OS -description: - - Collects a base set of device facts from a remote device that - is running SLX-OS. This module prepends all of the - base network fact keys with C(ansible_net_). The facts - module will always collect a base set of facts from the device - and can enable or disable collection of additional facts. -notes: - - Tested against SLX-OS 17s.1.02 -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: ['!config'] -''' - -EXAMPLES = """ -# Collect all facts from the device -- slxos_facts: - gather_subset: all - -# Collect only the config and default facts -- slxos_facts: - gather_subset: - - config - -# Do not collect hardware facts -- slxos_facts: - gather_subset: - - "!hardware" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - -# hardware -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 Primary IPv6 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -ansible_net_neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -""" -import re - -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, self.COMMANDS) - - def run(self, cmd): - return run_commands(self.module, cmd) - - -class Default(FactsBase): - - COMMANDS = [ - 'show version', - 'show inventory chassis', - r'show running-config | include host\-name' - ] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = self.parse_version(data) - - data = self.responses[1] - if data: - self.facts['model'] = self.parse_model(data) - self.facts['serialnum'] = self.parse_serialnum(data) - - data = self.responses[2] - if data: - self.facts['hostname'] = self.parse_hostname(data) - - def parse_version(self, data): - match = re.search(r'SLX-OS Operating System Version: (\S+)', data) - if match: - return match.group(1) - - def parse_model(self, data): - match = re.search(r'SID:(\S+)', data, re.M) - if match: - return match.group(1) - - def parse_hostname(self, data): - match = re.search(r'switch-attributes host-name (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_serialnum(self, data): - match = re.search(r'SN:(\S+)', data, re.M) - if match: - return match.group(1) - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show process memory summary' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - if data: - self.facts['memtotal_mb'] = int(round(int(self.parse_memtotal(data)) / 1024, 0)) - self.facts['memfree_mb'] = int(round(int(self.parse_memfree(data)) / 1024, 0)) - - def parse_memtotal(self, data): - match = re.search(r'Total\s*Memory: (\d+)\s', data, re.M) - if match: - return match.group(1) - - def parse_memfree(self, data): - match = re.search(r'Total Free: (\d+)\s', data, re.M) - if match: - return match.group(1) - - -class Config(FactsBase): - - COMMANDS = ['show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'show interface', - 'show ipv6 interface brief', - r'show lldp nei detail | inc ^Local\ Interface|^Remote\ Interface|^System\ Name' - ] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - data = self.responses[0] - if data: - interfaces = self.parse_interfaces(data) - self.facts['interfaces'] = self.populate_interfaces(interfaces) - self.populate_ipv4_interfaces(interfaces) - - data = self.responses[1] - if data: - self.populate_ipv6_interfaces(data) - - data = self.responses[2] - if data: - self.facts['neighbors'] = self.parse_neighbors(data) - - def populate_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - intf['description'] = self.parse_description(value) - intf['macaddress'] = self.parse_macaddress(value) - intf['mtu'] = self.parse_mtu(value) - intf['bandwidth'] = self.parse_bandwidth(value) - intf['duplex'] = self.parse_duplex(value) - intf['lineprotocol'] = self.parse_lineprotocol(value) - intf['operstatus'] = self.parse_operstatus(value) - intf['type'] = self.parse_type(value) - - facts[key] = intf - return facts - - def populate_ipv4_interfaces(self, data): - for key, value in data.items(): - self.facts['interfaces'][key]['ipv4'] = list() - primary_address = addresses = [] - primary_address = re.findall(r'Primary Internet Address is (\S+)', value, re.M) - addresses = re.findall(r'Secondary Internet Address is (\S+)', value, re.M) - if not primary_address: - continue - addresses.append(primary_address[0]) - for address in addresses: - addr, subnet = address.split("/") - ipv4 = dict(address=addr.strip(), subnet=subnet.strip()) - self.add_ip_address(addr.strip(), 'ipv4') - self.facts['interfaces'][key]['ipv4'].append(ipv4) - - # Only gets primary IPv6 addresses - def populate_ipv6_interfaces(self, data): - interfaces = re.split('=+', data)[1].strip() - matches = re.findall(r'(\S+ \S+) +[\w-]+.+\s+([\w:/]+/\d+)', interfaces, re.M) - for match in matches: - interface = match[0] - self.facts['interfaces'][interface]['ipv6'] = list() - address, masklen = match[1].split('/') - ipv6 = dict(address=address, masklen=int(masklen)) - self.add_ip_address(ipv6['address'], 'ipv6') - self.facts['interfaces'][interface]['ipv6'].append(ipv6) - - def add_ip_address(self, address, family): - if family == 'ipv4': - self.facts['all_ipv4_addresses'].append(address) - else: - self.facts['all_ipv6_addresses'].append(address) - - def parse_neighbors(self, neighbors): - facts = dict() - lines = neighbors.split('Local Interface: ') - if not lines: - return facts - for line in lines: - match = re.search(r'(\w+ \S+)\s+\(Local Int.+?\)[\s\S]+Remote Interface: (\S+.+?) \(Remote Int.+?\)[\s\S]+System Name: (\S+)', line, re.M) - if match: - intf = match.group(1) - if intf not in facts: - facts[intf] = list() - fact = dict() - fact['host'] = match.group(3) - fact['port'] = match.group(2) - facts[intf].append(fact) - return facts - - def parse_interfaces(self, data): - parsed = dict() - for interface in data.split('\n\n'): - match = re.match(r'^(\S+ \S+)', interface, re.M) - if not match: - continue - else: - parsed[match.group(1)] = interface - return parsed - - def parse_description(self, data): - match = re.search(r'Description: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_macaddress(self, data): - match = re.search(r'Hardware is Ethernet, address is (\S+)', data) - if match: - return match.group(1) - - def parse_ipv4(self, data): - match = re.search(r'Primary Internet Address is ([^\s,]+)', data) - if match: - addr, masklen = match.group(1).split('/') - return dict(address=addr, masklen=int(masklen)) - - def parse_mtu(self, data): - match = re.search(r'MTU (\d+) bytes', data) - if match: - return int(match.group(1)) - - def parse_bandwidth(self, data): - match = re.search(r'LineSpeed Actual\s+:\s(.+)', data) - if match: - return match.group(1) - - def parse_duplex(self, data): - match = re.search(r'Duplex: (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_type(self, data): - match = re.search(r'Hardware is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lineprotocol(self, data): - match = re.search(r'line protocol is (\S+)', data, re.M) - if match: - return match.group(1) - - def parse_operstatus(self, data): - match = re.match(r'^(?:.+) is (.+),', data, re.M) - if match: - return match.group(1) - - -FACT_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config) - -VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - gather_subset=dict(default=["!config"], type='list') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - gather_subset = module.params['gather_subset'] - - runable_subsets = set() - exclude_subsets = set() - - for subset in gather_subset: - if subset == 'all': - runable_subsets.update(VALID_SUBSETS) - continue - - if subset.startswith('!'): - subset = subset[1:] - if subset == 'all': - exclude_subsets.update(VALID_SUBSETS) - continue - exclude = True - else: - exclude = False - - if subset not in VALID_SUBSETS: - module.fail_json(msg='Bad subset') - - if exclude: - exclude_subsets.add(subset) - else: - runable_subsets.add(subset) - - if not runable_subsets: - runable_subsets.update(VALID_SUBSETS) - - runable_subsets.difference_update(exclude_subsets) - runable_subsets.add('default') - - facts = dict() - facts['gather_subset'] = list(runable_subsets) - - instances = list() - for key in runable_subsets: - instances.append(FACT_SUBSETS[key](module)) - - for inst in instances: - inst.populate() - facts.update(inst.facts) - - ansible_facts = dict() - for key, value in iteritems(facts): - key = 'ansible_net_%s' % key - ansible_facts[key] = value - - warnings = list() - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/slxos/slxos_interface.py b/plugins/modules/network/slxos/slxos_interface.py deleted file mode 100644 index 59cb2b4fb8..0000000000 --- a/plugins/modules/network/slxos/slxos_interface.py +++ /dev/null @@ -1,469 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: slxos_interface -author: "Lindsay Hill (@LindsayHill)" -short_description: Manage Interfaces on Extreme SLX-OS network devices -description: - - This module provides declarative management of Interfaces - on Extreme SLX-OS network devices. -notes: - - Tested against SLX-OS 17s.1.02 -options: - name: - description: - - Name of the Interface. - required: true - description: - description: - - Description of Interface. - enabled: - description: - - Interface link status. - default: True - type: bool - speed: - description: - - Interface link speed. - mtu: - description: - - Maximum size of transmit packet. - tx_rate: - description: - - Transmit rate in bits per second (bps). - rx_rate: - description: - - Receiver rate in bits per second (bps). - neighbors: - description: - - Check the operational state of given interface C(name) for LLDP neighbor. - - The following suboptions are available. - suboptions: - host: - description: - - "LLDP neighbor host for given interface C(name)." - port: - description: - - "LLDP neighbor port to which given interface C(name) is connected." - aggregate: - description: List of Interfaces definitions. - delay: - description: - - Time in seconds to wait before checking for the operational state on remote - device. This wait is applicable for operational state argument which are - I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate). - default: 10 - state: - description: - - State of the Interface configuration, C(up) means present and - operationally up and C(down) means present and operationally C(down) - default: present - choices: ['present', 'absent', 'up', 'down'] -''' - -EXAMPLES = """ -- name: configure interface - slxos_interface: - name: Ethernet 0/2 - description: test-interface - speed: 1000 - mtu: 9216 - -- name: remove interface - slxos_interface: - name: Loopback 9 - state: absent - -- name: make interface up - slxos_interface: - name: Ethernet 0/2 - enabled: True - -- name: make interface down - slxos_interface: - name: Ethernet 0/2 - enabled: False - -- name: Check intent arguments - slxos_interface: - name: Ethernet 0/2 - state: up - tx_rate: ge(0) - rx_rate: le(0) - -- name: Check neighbors intent arguments - slxos_interface: - name: Ethernet 0/41 - neighbors: - - port: Ethernet 0/41 - host: SLX - -- name: Config + intent - slxos_interface: - name: Ethernet 0/2 - enabled: False - state: down - -- name: Add interface using aggregate - slxos_interface: - aggregate: - - { name: Ethernet 0/1, mtu: 1548, description: test-interface-1 } - - { name: Ethernet 0/2, mtu: 1548, description: test-interface-2 } - speed: 10000 - state: present - -- name: Delete interface using aggregate - slxos_interface: - aggregate: - - name: Loopback 9 - - name: Loopback 10 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device. - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - interface Ethernet 0/2 - - description test-interface - - mtu 1548 -""" -import re - -from copy import deepcopy -from time import sleep - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import exec_command -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import get_config, load_config -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import conditional, remove_default_spec - - -def validate_mtu(value, module): - if value and not 1548 <= int(value) <= 9216: - module.fail_json(msg='mtu must be between 1548 and 9216') - - -def validate_param_values(module, obj, param=None): - if param is None: - param = module.params - for key in obj: - # validate the param value (if validator func exists) - validator = globals().get('validate_%s' % key) - if callable(validator): - validator(param.get(key), module) - - -def parse_shutdown(configobj, name): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'^shutdown', cfg, re.M) - if match: - return True - else: - return False - - -def parse_config_argument(configobj, name, arg=None): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'%s (.+)$' % arg, cfg, re.M) - if match: - return match.group(1) - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - return None - - -def add_command_to_interface(interface, cmd, commands): - if interface not in commands: - commands.append(interface) - commands.append(cmd) - - -def map_config_to_obj(module): - config = get_config(module) - configobj = NetworkConfig(indent=1, contents=config) - - match = re.findall(r'^interface (\S+ \S+)', config, re.M) - if not match: - return list() - - instances = list() - - for item in set(match): - obj = { - 'name': item, - 'description': parse_config_argument(configobj, item, 'description'), - 'speed': parse_config_argument(configobj, item, 'speed'), - 'mtu': parse_config_argument(configobj, item, 'mtu'), - 'disable': True if parse_shutdown(configobj, item) else False, - 'state': 'present' - } - instances.append(obj) - return instances - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - validate_param_values(module, item, item) - d = item.copy() - - if d['enabled']: - d['disable'] = False - else: - d['disable'] = True - - obj.append(d) - - else: - params = { - 'name': module.params['name'], - 'description': module.params['description'], - 'speed': module.params['speed'], - 'mtu': module.params['mtu'], - 'state': module.params['state'], - 'delay': module.params['delay'], - 'tx_rate': module.params['tx_rate'], - 'rx_rate': module.params['rx_rate'], - 'neighbors': module.params['neighbors'] - } - - validate_param_values(module, params) - if module.params['enabled']: - params.update({'disable': False}) - else: - params.update({'disable': True}) - - obj.append(params) - return obj - - -def map_obj_to_commands(updates): - commands = list() - want, have = updates - args = ('speed', 'description', 'mtu') - for w in want: - name = w['name'] - disable = w['disable'] - state = w['state'] - - obj_in_have = search_obj_in_list(name, have) - interface = 'interface ' + name - - if state == 'absent' and obj_in_have: - commands.append('no ' + interface) - - elif state in ('present', 'up', 'down'): - if obj_in_have: - for item in args: - candidate = w.get(item) - running = obj_in_have.get(item) - if candidate != running: - if candidate: - cmd = item + ' ' + str(candidate) - add_command_to_interface(interface, cmd, commands) - - if disable and not obj_in_have.get('disable', False): - add_command_to_interface(interface, 'shutdown', commands) - elif not disable and obj_in_have.get('disable', False): - add_command_to_interface(interface, 'no shutdown', commands) - else: - commands.append(interface) - for item in args: - value = w.get(item) - if value: - commands.append(item + ' ' + str(value)) - - if disable: - commands.append('no shutdown') - return commands - - -def check_declarative_intent_params(module, want, result): - failed_conditions = [] - have_neighbors = None - for w in want: - want_state = w.get('state') - want_tx_rate = w.get('tx_rate') - want_rx_rate = w.get('rx_rate') - want_neighbors = w.get('neighbors') - - if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate and not want_neighbors: - continue - - if result['changed']: - sleep(w['delay']) - - command = 'show interface %s' % w['name'] - rc, out, err = exec_command(module, command) - if rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc) - - if want_state in ('up', 'down'): - match = re.search(r'%s (\w+)' % 'line protocol is', out, re.M) - have_state = None - if match: - have_state = match.group(1) - if have_state is None or not conditional(want_state, have_state.strip()): - failed_conditions.append('state ' + 'eq(%s)' % want_state) - - if want_tx_rate: - match = re.search(r'%s (\d+)' % 'Output', out, re.M) - have_tx_rate = None - if match: - have_tx_rate = match.group(1) - - if have_tx_rate is None or not conditional(want_tx_rate, have_tx_rate.strip(), cast=int): - failed_conditions.append('tx_rate ' + want_tx_rate) - - if want_rx_rate: - match = re.search(r'%s (\d+)' % 'Input', out, re.M) - have_rx_rate = None - if match: - have_rx_rate = match.group(1) - - if have_rx_rate is None or not conditional(want_rx_rate, have_rx_rate.strip(), cast=int): - failed_conditions.append('rx_rate ' + want_rx_rate) - - if want_neighbors: - have_host = [] - have_port = [] - if have_neighbors is None: - rc, have_neighbors, err = exec_command(module, 'show lldp neighbors detail') - if rc != 0: - module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc) - - if have_neighbors: - lines = have_neighbors.strip().split('Local Interface: ') - short_name = w['name'].replace('Ethernet', 'Eth') - for line in lines: - field = line.split('\n') - if field[0].split('(')[0].strip() == short_name: - for item in field: - if item.startswith('System Name:'): - have_host.append(item.split(':')[1].strip()) - if item.startswith('Remote Interface:'): - have_port.append(item.split(':')[1].split('(')[0].strip()) - for item in want_neighbors: - host = item.get('host') - port = item.get('port') - if host and host not in have_host: - failed_conditions.append('host ' + host) - if port and port not in have_port: - failed_conditions.append('port ' + port) - return failed_conditions - - -def main(): - """ main entry point for module execution - """ - neighbors_spec = dict( - host=dict(), - port=dict() - ) - - element_spec = dict( - name=dict(), - description=dict(), - speed=dict(), - mtu=dict(), - enabled=dict(default=True, type='bool'), - tx_rate=dict(), - rx_rate=dict(), - neighbors=dict(type='list', elements='dict', options=neighbors_spec), - delay=dict(default=10, type='int'), - state=dict(default='present', - choices=['present', 'absent', 'up', 'down']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - warnings = list() - - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have)) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - failed_conditions = check_declarative_intent_params(module, want, result) - - if failed_conditions: - msg = 'One or more conditional statements have not been satisfied' - module.fail_json(msg=msg, failed_conditions=failed_conditions, changed=result['changed']) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/slxos/slxos_l2_interface.py b/plugins/modules/network/slxos/slxos_l2_interface.py deleted file mode 100644 index 633d5da412..0000000000 --- a/plugins/modules/network/slxos/slxos_l2_interface.py +++ /dev/null @@ -1,505 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: slxos_l2_interface -short_description: Manage Layer-2 interface on Extreme Networks SLX-OS devices. -description: - - This module provides declarative management of Layer-2 interface on - Extreme slxos devices. -author: - - Matthew Stone (@bigmstone) -options: - name: - description: - - Full name of the interface excluding any logical - unit number, i.e. Ethernet 0/1. - required: true - aliases: ['interface'] - mode: - description: - - Mode in which interface needs to be configured. - default: access - choices: ['access', 'trunk'] - access_vlan: - description: - - Configure given VLAN in access port. - If C(mode=access), used as the access VLAN ID. - trunk_vlans: - description: - - List of VLANs to be configured in trunk port. - If C(mode=trunk), used as the VLAN range to ADD or REMOVE - from the trunk. - native_vlan: - description: - - Native VLAN to be configured in trunk port. - If C(mode=trunk), used as the trunk native VLAN ID. - trunk_allowed_vlans: - description: - - List of allowed VLANs in a given trunk port. - If C(mode=trunk), these are the only VLANs that will be - configured on the trunk, i.e. "2-10,15". - aggregate: - description: - - List of Layer-2 interface definitions. - state: - description: - - Manage the state of the Layer-2 Interface configuration. - default: present - choices: ['present','absent', 'unconfigured'] -''' - -EXAMPLES = """ -- name: Ensure Ethernet 0/5 is in its default l2 interface state - slxos_l2_interface: - name: Ethernet 0/5 - state: unconfigured - -- name: Ensure Ethernet 0/5 is configured for access vlan 20 - slxos_l2_interface: - name: Ethernet 0/5 - mode: access - access_vlan: 20 - -- name: Ensure Ethernet 0/5 only has vlans 5-10 as trunk vlans - slxos_l2_interface: - name: Ethernet 0/5 - mode: trunk - native_vlan: 10 - trunk_vlans: 5-10 - -- name: Ensure Ethernet 0/5 is a trunk port and ensure 2-50 are being tagged (doesn't mean others aren't also being tagged) - slxos_l2_interface: - name: Ethernet 0/5 - mode: trunk - native_vlan: 10 - trunk_vlans: 2-50 - -- name: Ensure these VLANs are not being tagged on the trunk - slxos_l2_interface: - name: Ethernet 0/5 - mode: trunk - trunk_vlans: 51-4094 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - interface Ethernet 0/5 - - switchport access vlan 20 -""" - -import re -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import get_config, load_config, run_commands - - -def get_interface_type(interface): - intf_type = 'unknown' - if interface.upper()[:2] in ('ET', 'GI'): - intf_type = 'ethernet' - elif interface.upper().startswith('VL'): - intf_type = 'svi' - elif interface.upper().startswith('LO'): - intf_type = 'loopback' - elif interface.upper()[:2] in ('MG', 'MA'): - intf_type = 'management' - elif interface.upper().startswith('PO'): - intf_type = 'portchannel' - elif interface.upper().startswith('NV'): - intf_type = 'nve' - - return intf_type - - -def is_switchport(name, module): - intf_type = get_interface_type(name) - - if intf_type in ('ethernet', 'portchannel'): - config = run_commands(module, ['show interface {0} switchport'.format(name)])[0] - match = re.search(r'Interface name\s+:\s', config) - return bool(match) - return False - - -def interface_is_portchannel(name, module): - if get_interface_type(name) == 'ethernet': - config = get_config(module) - if 'channel group' in config: - return True - - return False - - -def get_switchport(name, module): - config = run_commands(module, ['show interface {0} switchport'.format(name)])[0] - mode = re.search(r'Switchport mode\s+:\s(?:.* )?(\w+)$', config, re.M) - if mode: - mode = mode.group(1) - access = re.search(r'Default Vlan\s+:\s(\d+)', config) - if access: - access = access.group(1) - native = re.search(r'Native Vlan\s+:\s(\d+)', config) - if native: - native = native.group(1) - trunk = re.search(r'Active Vlans\s+:\s(.+)$', config, re.M) - if trunk: - trunk = trunk.group(1) - if trunk == 'ALL': - trunk = '1-4094' - - switchport_config = { - "interface": name, - "mode": mode, - "access_vlan": access, - "native_vlan": native, - "trunk_vlans": trunk, - } - - return switchport_config - - -def remove_switchport_config_commands(name, existing, proposed, module): - mode = proposed.get('mode') - commands = [] - command = None - - if mode == 'access': - av_check = existing.get('access_vlan') == proposed.get('access_vlan') - if av_check: - command = 'no switchport access vlan {0}'.format(existing.get('access_vlan')) - commands.append(command) - - elif mode == 'trunk': - tv_check = existing.get('trunk_vlans_list') == proposed.get('trunk_vlans_list') - - if not tv_check: - existing_vlans = existing.get('trunk_vlans_list') - proposed_vlans = proposed.get('trunk_vlans_list') - vlans_to_remove = set(proposed_vlans).intersection(existing_vlans) - - if vlans_to_remove: - proposed_allowed_vlans = proposed.get('trunk_allowed_vlans') - remove_trunk_allowed_vlans = proposed.get('trunk_vlans', proposed_allowed_vlans) - command = 'switchport trunk allowed vlan remove {0}'.format(remove_trunk_allowed_vlans) - commands.append(command) - - native_check = existing.get('native_vlan') == proposed.get('native_vlan') - if native_check and proposed.get('native_vlan'): - command = 'no switchport trunk native vlan {0}'.format(existing.get('native_vlan')) - commands.append(command) - - if commands: - commands.insert(0, 'interface ' + name) - return commands - - -def get_switchport_config_commands(name, existing, proposed, module): - """Gets commands required to config a given switchport interface - """ - - proposed_mode = proposed.get('mode') - existing_mode = existing.get('mode') - commands = [] - command = None - - if proposed_mode != existing_mode: - if proposed_mode == 'trunk': - command = 'switchport mode trunk' - elif proposed_mode == 'access': - command = 'switchport mode access' - - if command: - commands.append(command) - - if proposed_mode == 'access': - av_check = str(existing.get('access_vlan')) == str(proposed.get('access_vlan')) - if not av_check: - command = 'switchport access vlan {0}'.format(proposed.get('access_vlan')) - commands.append(command) - - elif proposed_mode == 'trunk': - tv_check = existing.get('trunk_vlans_list') == proposed.get('trunk_vlans_list') - - if not tv_check: - if proposed.get('allowed'): - command = 'switchport trunk allowed vlan add {0}'.format(proposed.get('trunk_allowed_vlans')) - commands.append(command) - - else: - existing_vlans = existing.get('trunk_vlans_list') - proposed_vlans = proposed.get('trunk_vlans_list') - vlans_to_add = set(proposed_vlans).difference(existing_vlans) - if vlans_to_add: - command = 'switchport trunk allowed vlan add {0}'.format(proposed.get('trunk_vlans')) - commands.append(command) - - native_check = str(existing.get('native_vlan')) == str(proposed.get('native_vlan')) - if not native_check and proposed.get('native_vlan'): - command = 'switchport trunk native vlan {0}'.format(proposed.get('native_vlan')) - commands.append(command) - - if commands: - commands.insert(0, 'interface ' + name) - return commands - - -def is_switchport_default(existing): - """Determines if switchport has a default config based on mode - Args: - existing (dict): existing switchport configuration from Ansible mod - Returns: - boolean: True if switchport has OOB Layer 2 config, i.e. - vlan 1 and trunk all and mode is access - """ - - c1 = str(existing['access_vlan']) == '1' - c2 = str(existing['native_vlan']) == '1' - c3 = existing['trunk_vlans'] == '1-4094' - c4 = existing['mode'] == 'access' - - default = c1 and c2 and c3 and c4 - - return default - - -def default_switchport_config(name): - commands = [] - commands.append('interface ' + name) - commands.append('switchport mode access') - commands.append('switch access vlan 1') - commands.append('switchport trunk native vlan 1') - commands.append('switchport trunk allowed vlan all') - return commands - - -def vlan_range_to_list(vlans): - result = [] - if vlans: - for part in vlans.split(','): - if part == 'none': - break - if '-' in part: - start, stop = (int(i) for i in part.split('-')) - result.extend(range(start, stop + 1)) - else: - result.append(int(part)) - return sorted(result) - - -def get_list_of_vlans(module): - config = run_commands(module, ['show vlan brief'])[0] - vlans = set() - - lines = config.strip().splitlines() - for line in lines: - line_parts = line.split() - if line_parts: - try: - int(line_parts[0]) - except ValueError: - continue - vlans.add(line_parts[0]) - - return list(vlans) - - -def flatten_list(commands): - flat_list = [] - for command in commands: - if isinstance(command, list): - flat_list.extend(command) - else: - flat_list.append(command) - return flat_list - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - obj.append(item.copy()) - else: - obj.append({ - 'name': module.params['name'], - 'mode': module.params['mode'], - 'access_vlan': module.params['access_vlan'], - 'native_vlan': module.params['native_vlan'], - 'trunk_vlans': module.params['trunk_vlans'], - 'trunk_allowed_vlans': module.params['trunk_allowed_vlans'], - 'state': module.params['state'] - }) - - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - name=dict(type='str', aliases=['interface']), - mode=dict(choices=['access', 'trunk'], default='access'), - access_vlan=dict(type='str'), - native_vlan=dict(type='str'), - trunk_vlans=dict(type='str'), - trunk_allowed_vlans=dict(type='str'), - state=dict(choices=['absent', 'present', 'unconfigured'], default='present') - ) - - aggregate_spec = deepcopy(element_spec) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=[['access_vlan', 'trunk_vlans'], - ['access_vlan', 'native_vlan'], - ['access_vlan', 'trunk_allowed_vlans']], - supports_check_mode=True) - - warnings = list() - commands = [] - result = {'changed': False, 'warnings': warnings} - - want = map_params_to_obj(module) - for w in want: - name = w['name'] - mode = w['mode'] - access_vlan = w['access_vlan'] - state = w['state'] - trunk_vlans = w['trunk_vlans'] - native_vlan = w['native_vlan'] - trunk_allowed_vlans = w['trunk_allowed_vlans'] - - args = dict(name=name, mode=mode, access_vlan=access_vlan, - native_vlan=native_vlan, trunk_vlans=trunk_vlans, - trunk_allowed_vlans=trunk_allowed_vlans) - - proposed = dict((k, v) for k, v in args.items() if v is not None) - - name = name.lower() - - if mode == 'access' and state == 'present' and not access_vlan: - module.fail_json(msg='access_vlan param is required when mode=access && state=present') - - if mode == 'trunk' and access_vlan: - module.fail_json(msg='access_vlan param not supported when using mode=trunk') - - if not is_switchport(name, module): - module.fail_json(msg='Ensure interface is configured to be a L2' - '\nport first before using this module. You can use' - '\nthe slxos_interface module for this.') - - if interface_is_portchannel(name, module): - module.fail_json(msg='Cannot change L2 config on physical ' - '\nport because it is in a portchannel. ' - '\nYou should update the portchannel config.') - - # existing will never be null for Eth intfs as there is always a default - existing = get_switchport(name, module) - - # Safeguard check - # If there isn't an existing, something is wrong per previous comment - if not existing: - module.fail_json(msg='Make sure you are using the FULL interface name') - - if trunk_vlans or trunk_allowed_vlans: - if trunk_vlans: - trunk_vlans_list = vlan_range_to_list(trunk_vlans) - elif trunk_allowed_vlans: - trunk_vlans_list = vlan_range_to_list(trunk_allowed_vlans) - proposed['allowed'] = True - - existing_trunks_list = vlan_range_to_list((existing['trunk_vlans'])) - - existing['trunk_vlans_list'] = existing_trunks_list - proposed['trunk_vlans_list'] = trunk_vlans_list - - current_vlans = get_list_of_vlans(module) - - if state == 'present': - if access_vlan and access_vlan not in current_vlans: - module.fail_json(msg='You are trying to configure a VLAN' - ' on an interface that\ndoes not exist on the ' - ' switch yet!', vlan=access_vlan) - elif native_vlan and native_vlan not in current_vlans: - module.fail_json(msg='You are trying to configure a VLAN' - ' on an interface that\ndoes not exist on the ' - ' switch yet!', vlan=native_vlan) - else: - command = get_switchport_config_commands(name, existing, proposed, module) - commands.append(command) - elif state == 'unconfigured': - is_default = is_switchport_default(existing) - if not is_default: - command = default_switchport_config(name) - commands.append(command) - elif state == 'absent': - command = remove_switchport_config_commands(name, existing, proposed, module) - commands.append(command) - - if trunk_vlans or trunk_allowed_vlans: - existing.pop('trunk_vlans_list') - proposed.pop('trunk_vlans_list') - - cmds = flatten_list(commands) - if cmds: - if module.check_mode: - module.exit_json(changed=True, commands=cmds) - else: - result['changed'] = True - load_config(module, cmds) - if 'configure' in cmds: - cmds.pop(0) - - result['commands'] = cmds - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/slxos/slxos_l3_interface.py b/plugins/modules/network/slxos/slxos_l3_interface.py deleted file mode 100644 index 2b901ad7af..0000000000 --- a/plugins/modules/network/slxos/slxos_l3_interface.py +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: slxos_l3_interface -author: "Matthew Stone (@bigmstone)" -short_description: Manage L3 interfaces on Extreme Networks SLX-OS network devices. -description: - - This module provides declarative management of L3 interfaces - on slxos network devices. -notes: - - Tested against slxos 15.2 -options: - name: - description: - - Name of the L3 interface to be configured eg. Ethernet 0/2 - ipv4: - description: - - IPv4 address to be set for the L3 interface mentioned in I(name) option. - The address format is /, the mask is number - in range 0-32 eg. 192.168.0.1/24 - ipv6: - description: - - IPv6 address to be set for the L3 interface mentioned in I(name) option. - The address format is /, the mask is number - in range 0-128 eg. fd5d:12c9:2201:1::1/64 - aggregate: - description: - - List of L3 interfaces definitions. Each of the entry in aggregate list should - define name of interface C(name) and a optional C(ipv4) or C(ipv6) address. - state: - description: - - State of the L3 interface configuration. It indicates if the configuration should - be present or absent on remote device. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: Remove Ethernet 0/3 IPv4 and IPv6 address - slxos_l3_interface: - name: Ethernet 0/3 - state: absent - -- name: Set Ethernet 0/3 IPv4 address - slxos_l3_interface: - name: Ethernet 0/3 - ipv4: 192.168.0.1/24 - -- name: Set Ethernet 0/3 IPv6 address - slxos_l3_interface: - name: Ethernet 0/3 - ipv6: "fd5d:12c9:2201:1::1/64" - -- name: Set IP addresses on aggregate - slxos_l3_interface: - aggregate: - - { name: Ethernet 0/3, ipv4: 192.168.2.10/24 } - - { name: Ethernet 0/3, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" } - -- name: Remove IP addresses on aggregate - slxos_l3_interface: - aggregate: - - { name: Ethernet 0/3, ipv4: 192.168.2.10/24 } - - { name: Ethernet 0/3, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" } - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - interface Ethernet 0/2 - - ip address 192.168.0.1/24 - - ipv6 address fd5d:12c9:2201:1::1/64 -""" -import re - -from copy import deepcopy - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import get_config, load_config -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import conditional, remove_default_spec -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import is_netmask, is_masklen, to_netmask, to_masklen - - -def validate_ipv4(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json(msg='address format is /, got invalid format %s' % value) - - if not is_masklen(address[1]): - module.fail_json(msg='invalid value for mask: %s, mask should be in range 0-32' % address[1]) - - -def validate_ipv6(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json(msg='address format is /, got invalid format %s' % value) - else: - if not 0 <= int(address[1]) <= 128: - module.fail_json(msg='invalid value for mask: %s, mask should be in range 0-128' % address[1]) - - -def validate_param_values(module, obj, param=None): - if param is None: - param = module.params - for key in obj: - # validate the param value (if validator func exists) - validator = globals().get('validate_%s' % key) - if callable(validator): - validator(param.get(key), module) - - -def parse_config_argument(configobj, name, arg=None): - cfg = configobj['interface %s' % name] - cfg = '\n'.join(cfg.children) - match = re.search(r'%s (.+)$' % arg, cfg, re.M) - if match: - return match.group(1).strip() - - return None - - -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - - return None - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - for w in want: - name = w['name'] - ipv4 = w['ipv4'] - ipv6 = w['ipv6'] - state = w['state'] - - interface = 'interface ' + name - commands.append(interface) - - obj_in_have = search_obj_in_list(name, have) - if state == 'absent' and obj_in_have: - if obj_in_have['ipv4']: - if ipv4: - commands.append('no ip address %s' % ipv4) - else: - commands.append('no ip address') - if obj_in_have['ipv6']: - if ipv6: - commands.append('no ipv6 address %s' % ipv6) - else: - commands.append('no ipv6 address') - - elif state == 'present': - if ipv4: - if obj_in_have is None or obj_in_have.get('ipv4') is None or ipv4 != obj_in_have['ipv4']: - commands.append('ip address %s' % ipv4) - - if ipv6: - if obj_in_have is None or obj_in_have.get('ipv6') is None or ipv6.lower() != obj_in_have['ipv6'].lower(): - commands.append('ipv6 address %s' % ipv6) - - if commands[-1] == interface: - commands.pop(-1) - - return commands - - -def map_config_to_obj(module): - config = get_config(module) - configobj = NetworkConfig(indent=1, contents=config) - - match = re.findall(r'^interface (\S+\s[0-9]+/[0-9]+)', config, re.M) - if not match: - return list() - - instances = list() - - for item in set(match): - ipv4 = parse_config_argument(configobj, item, 'ip address') - if ipv4: - # eg. 192.168.2.10 255.255.255.0 -> 192.168.2.10/24 - address = ipv4.strip().split(' ') - if len(address) == 2 and is_netmask(address[1]): - ipv4 = '%s/%s' % (address[0], to_text(to_masklen(address[1]))) - - obj = { - 'name': item, - 'ipv4': ipv4, - 'ipv6': parse_config_argument(configobj, item, 'ipv6 address'), - 'state': 'present' - } - instances.append(obj) - - return instances - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - validate_param_values(module, item, item) - obj.append(item.copy()) - else: - obj.append({ - 'name': module.params['name'], - 'ipv4': module.params['ipv4'], - 'ipv6': module.params['ipv6'], - 'state': module.params['state'] - }) - - validate_param_values(module, obj) - - return obj - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - name=dict(), - ipv4=dict(), - ipv6=dict(), - state=dict(default='present', - choices=['present', 'absent']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['name'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - ) - - argument_spec.update(element_spec) - - required_one_of = [['name', 'aggregate']] - mutually_exclusive = [['name', 'aggregate']] - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - - result = {'changed': False} - - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/slxos/slxos_linkagg.py b/plugins/modules/network/slxos/slxos_linkagg.py deleted file mode 100644 index fc0323df9c..0000000000 --- a/plugins/modules/network/slxos/slxos_linkagg.py +++ /dev/null @@ -1,325 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: slxos_linkagg -author: "Matthew Stone (@bigmstone)" -short_description: Manage link aggregation groups on Extreme Networks SLX-OS network devices -description: - - This module provides declarative management of link aggregation groups - on Extreme Networks SLX-OS network devices. -notes: - - Tested against SLX-OS 17s.1.02 -options: - group: - description: - - Channel-group number for the port-channel - Link aggregation group. Range 1-1024. - mode: - description: - - Mode of the link aggregation group. - choices: ['active', 'on', 'passive'] - members: - description: - - List of members of the link aggregation group. - aggregate: - description: List of link aggregation definitions. - state: - description: - - State of the link aggregation group. - default: present - choices: ['present', 'absent'] - purge: - description: - - Purge links not defined in the I(aggregate) parameter. - type: bool -''' - -EXAMPLES = """ -- name: create link aggregation group - slxos_linkagg: - group: 10 - state: present - -- name: delete link aggregation group - slxos_linkagg: - group: 10 - state: absent - -- name: set link aggregation group to members - slxos_linkagg: - group: 200 - mode: active - members: - - Ethernet 0/1 - - Ethernet 0/2 - -- name: remove link aggregation group from Ethernet 0/1 - slxos_linkagg: - group: 200 - mode: active - members: - - Ethernet 0/1 - -- name: Create aggregate of linkagg definitions - slxos_linkagg: - aggregate: - - { group: 3, mode: on, members: [Ethernet 0/1] } - - { group: 100, mode: passive, members: [Ethernet 0/2] } -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - interface port-channel 30 - - interface Ethernet 0/3 - - channel-group 30 mode on - - no interface port-channel 30 -""" - -import re -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import CustomNetworkConfig -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import get_config, load_config - - -def search_obj_in_list(group, lst): - for o in lst: - if o['group'] == group: - return o - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params['purge'] - - for w in want: - group = w['group'] - mode = w['mode'] - members = w.get('members') or [] - state = w['state'] - del w['state'] - - obj_in_have = search_obj_in_list(group, have) - - if state == 'absent': - if obj_in_have: - commands.append('no interface port-channel {0}'.format(group)) - - elif state == 'present': - cmd = ['interface port-channel {0}'.format(group), - 'exit'] - if not obj_in_have: - if not group: - module.fail_json(msg='group is a required option') - commands.extend(cmd) - - if members: - for m in members: - commands.append('interface {0}'.format(m)) - commands.append('channel-group {0} mode {1}'.format(group, mode)) - - else: - if members: - if 'members' not in obj_in_have.keys(): - for m in members: - commands.extend(cmd) - commands.append('interface {0}'.format(m)) - commands.append('channel-group {0} mode {1}'.format(group, mode)) - - elif set(members) != set(obj_in_have['members']): - missing_members = list(set(members) - set(obj_in_have['members'])) - for m in missing_members: - commands.extend(cmd) - commands.append('interface {0}'.format(m)) - commands.append('channel-group {0} mode {1}'.format(group, mode)) - - superfluous_members = list(set(obj_in_have['members']) - set(members)) - for m in superfluous_members: - commands.extend(cmd) - commands.append('interface {0}'.format(m)) - commands.append('no channel-group') - - if purge: - for h in have: - obj_in_want = search_obj_in_list(h['group'], want) - if not obj_in_want: - commands.append('no interface port-channel {0}'.format(h['group'])) - - return commands - - -def map_params_to_obj(module): - obj = [] - - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - d = item.copy() - d['group'] = str(d['group']) - - obj.append(d) - else: - obj.append({ - 'group': str(module.params['group']), - 'mode': module.params['mode'], - 'members': module.params['members'], - 'state': module.params['state'] - }) - - return obj - - -def parse_mode(module, config, group, member): - mode = None - netcfg = CustomNetworkConfig(indent=1, contents=config) - parents = ['interface {0}'.format(member)] - body = netcfg.get_section(parents) - - match_int = re.findall(r'interface {0}\n'.format(member), body, re.M) - if match_int: - match = re.search(r'channel-group {0} mode (\S+)'.format(group), body, re.M) - if match: - mode = match.group(1) - - return mode - - -def parse_members(module, config, group): - members = [] - - for line in config.strip().split('!'): - l = line.strip() - if l.startswith('interface'): - match_group = re.findall(r'channel-group {0} mode'.format(group), l, re.M) - if match_group: - match = re.search(r'^interface (\S+\s\S+)$', l, re.M) - if match: - members.append(match.group(1)) - - return members - - -def get_channel(module, config, group): - match = re.findall(r'^interface (\S+\s\S+)$', config, re.M) - - if not match: - return {} - - channel = {} - for item in set(match): - member = item - channel['mode'] = parse_mode(module, config, group, member) - channel['members'] = parse_members(module, config, group) - - return channel - - -def map_config_to_obj(module): - objs = list() - config = get_config(module) - - for line in config.split('\n'): - l = line.strip() - match = re.search(r'interface Port-channel (\S+)', l, re.M) - if match: - obj = {} - group = match.group(1) - obj['group'] = group - obj.update(get_channel(module, config, group)) - objs.append(obj) - - return objs - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - group=dict(type='int'), - mode=dict(choices=['active', 'on', 'passive']), - members=dict(type='list'), - state=dict(default='present', - choices=['present', 'absent']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['group'] = dict(required=True) - - required_one_of = [['group', 'aggregate']] - required_together = [['members', 'mode']] - mutually_exclusive = [['group', 'aggregate']] - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec, - required_together=required_together), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - required_together=required_together, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - warnings = list() - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/slxos/slxos_lldp.py b/plugins/modules/network/slxos/slxos_lldp.py deleted file mode 100644 index efb8ab3560..0000000000 --- a/plugins/modules/network/slxos/slxos_lldp.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: slxos_lldp -author: "Matthew Stone (@bigmstone)" -short_description: Manage LLDP configuration on Extreme Networks SLX-OS network devices. -description: - - This module provides declarative management of LLDP service - on Extreme SLX-OS network devices. -notes: - - Tested against SLX-OS 17s.1.02 -options: - state: - description: - - State of the LLDP configuration. If value is I(present) lldp will be enabled - else if it is I(absent) it will be disabled. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: Enable LLDP service - slxos_lldp: - state: present - -- name: Disable LLDP service - slxos_lldp: - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always, except for the platforms that use Netconf transport to manage the device. - type: list - sample: - - lldp run -""" -import re -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import CustomNetworkConfig -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import ( - load_config, - get_config -) - -PROTOCOL = "protocol lldp" - - -def has_lldp(module): - config = get_config(module) - netcfg = CustomNetworkConfig(indent=1, contents=config) - parents = [PROTOCOL] - body = netcfg.get_section(parents) - - for line in body.split('\n'): - l = line.strip() - match = re.search(r'disable', l, re.M) - if match: - return False - - return True - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - state=dict(default='present', - choices=['present', 'absent']) - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - HAS_LLDP = has_lldp(module) - - warnings = list() - - result = {'changed': False} - - if warnings: - result['warnings'] = warnings - - commands = [] - - if module.params['state'] == 'absent' and HAS_LLDP: - commands.append('protocol lldp') - commands.append('disable') - elif module.params['state'] == 'present' and not HAS_LLDP: - commands.append('protocol lldp') - commands.append('no disable') - - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/slxos/slxos_vlan.py b/plugins/modules/network/slxos/slxos_vlan.py deleted file mode 100644 index 0adf86ca0d..0000000000 --- a/plugins/modules/network/slxos/slxos_vlan.py +++ /dev/null @@ -1,309 +0,0 @@ -#!/usr/bin/python -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - -DOCUMENTATION = ''' ---- -module: slxos_vlan -author: "Lindsay Hill (@lindsayhill)" -short_description: Manage VLANs on Extreme Networks SLX-OS network devices -description: - - This module provides declarative management of VLANs - on Extreme SLX-OS network devices. -notes: - - Tested against SLX-OS 18r.1.00 -options: - name: - description: - - Name of the VLAN. - vlan_id: - description: - - ID of the VLAN. Range 1-4094. - required: true - interfaces: - description: - - List of interfaces that should be associated to the VLAN. - required: true - delay: - description: - - Delay the play should wait to check for declarative intent params values. - default: 10 - aggregate: - description: List of VLANs definitions. - purge: - description: - - Purge VLANs not defined in the I(aggregate) parameter. - type: bool - default: no - state: - description: - - State of the VLAN configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -- name: Create vlan - slxos_vlan: - vlan_id: 100 - name: test-vlan - state: present -- name: Add interfaces to VLAN - slxos_vlan: - vlan_id: 100 - interfaces: - - Ethernet 0/1 - - Ethernet 0/2 -- name: Delete vlan - slxos_vlan: - vlan_id: 100 - state: absent -""" - -RETURN = """ -commands: - description: The list of configuration mode commands to send to the device - returned: always - type: list - sample: - - vlan 100 - - name test-vlan -""" - -import re -import time - -from copy import deepcopy - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec -from ansible_collections.community.general.plugins.module_utils.network.slxos.slxos import load_config, run_commands - - -def search_obj_in_list(vlan_id, lst): - for o in lst: - if o['vlan_id'] == vlan_id: - return o - return None - - -def map_obj_to_commands(updates, module): - commands = list() - want, have = updates - purge = module.params['purge'] - - for w in want: - vlan_id = w['vlan_id'] - name = w['name'] - interfaces = w['interfaces'] - state = w['state'] - - obj_in_have = search_obj_in_list(vlan_id, have) - - if state == 'absent': - if obj_in_have: - commands.append('no vlan %s' % vlan_id) - - elif state == 'present': - if not obj_in_have: - commands.append('vlan %s' % vlan_id) - if name: - commands.append('name %s' % name) - - if interfaces: - for i in interfaces: - commands.append('interface %s' % i) - commands.append('switchport') - commands.append('switchport mode access') - commands.append('switchport access vlan %s' % vlan_id) - - else: - if name: - if name != obj_in_have['name']: - commands.append('vlan %s' % vlan_id) - commands.append('name %s' % name) - - if interfaces: - if not obj_in_have['interfaces']: - for i in interfaces: - commands.append('vlan %s ' % vlan_id) - commands.append('interface %s' % i) - commands.append('switchport') - commands.append('switchport mode access') - commands.append('switchport access vlan %s' % vlan_id) - - elif set(interfaces) != set(obj_in_have['interfaces']): - missing_interfaces = list(set(interfaces) - set(obj_in_have['interfaces'])) - for i in missing_interfaces: - commands.append('vlan %s' % vlan_id) - commands.append('interface %s' % i) - commands.append('switchport') - commands.append('switchport mode access') - commands.append('switchport access vlan %s' % vlan_id) - - superfluous_interfaces = list(set(obj_in_have['interfaces']) - set(interfaces)) - for i in superfluous_interfaces: - commands.append('vlan %s' % vlan_id) - commands.append('interface %s' % i) - commands.append('switchport mode access') - commands.append('no switchport access vlan %s' % vlan_id) - - if purge: - for h in have: - obj_in_want = search_obj_in_list(h['vlan_id'], want) - if not obj_in_want and h['vlan_id'] != '1': - commands.append('no vlan %s' % h['vlan_id']) - - return commands - - -def map_params_to_obj(module): - obj = [] - aggregate = module.params.get('aggregate') - if aggregate: - for item in aggregate: - for key in item: - if item.get(key) is None: - item[key] = module.params[key] - - d = item.copy() - d['vlan_id'] = str(d['vlan_id']) - - obj.append(d) - else: - obj.append({ - 'vlan_id': str(module.params['vlan_id']), - 'name': module.params['name'], - 'interfaces': module.params['interfaces'], - 'state': module.params['state'] - }) - - return obj - - -def map_config_to_obj(module): - output = run_commands(module, ['show vlan brief']) - lines = output[0].strip().splitlines()[5:] - - if not lines: - return list() - - objs = list() - obj = {} - - for l in lines: - splitted_line = re.split(r'([0-9]+)? +(\S.{14})? +(ACTIVE|INACTIVE\(.+?\))?.*(Eth .+?|Po .+?|Tu .+?)\([ut]\).*$', l.rstrip()) - if len(splitted_line) == 1: - # Handle situation where VLAN is configured, but has no associated ports - inactive = re.match(r'([0-9]+)? +(\S.{14}) +INACTIVE\(no member port\).*$', l.rstrip()) - if inactive: - splitted_line = ['', inactive.groups()[0], inactive.groups()[1], '', ''] - else: - continue - - splitted_line[4] = splitted_line[4].replace('Eth', 'Ethernet').replace('Po', 'Port-channel').replace('Tu', 'Tunnel') - - if splitted_line[1] is None: - obj['interfaces'].append(splitted_line[4]) - continue - - obj = {} - obj['vlan_id'] = splitted_line[1] - obj['name'] = splitted_line[2].strip() - obj['interfaces'] = [splitted_line[4]] - - objs.append(obj) - - return objs - - -def check_declarative_intent_params(want, module): - if module.params['interfaces']: - time.sleep(module.params['delay']) - have = map_config_to_obj(module) - - for w in want: - for i in w['interfaces']: - obj_in_have = search_obj_in_list(w['vlan_id'], have) - if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']: - module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id'])) - - -def main(): - """ main entry point for module execution - """ - element_spec = dict( - vlan_id=dict(type='int'), - name=dict(), - interfaces=dict(type='list'), - delay=dict(default=10, type='int'), - state=dict(default='present', - choices=['present', 'absent']) - ) - - aggregate_spec = deepcopy(element_spec) - aggregate_spec['vlan_id'] = dict(required=True) - - # remove default in aggregate spec, to handle common arguments - remove_default_spec(aggregate_spec) - - argument_spec = dict( - aggregate=dict(type='list', elements='dict', options=aggregate_spec), - purge=dict(default=False, type='bool') - ) - - argument_spec.update(element_spec) - - required_one_of = [['vlan_id', 'aggregate']] - mutually_exclusive = [['vlan_id', 'aggregate']] - - module = AnsibleModule(argument_spec=argument_spec, - required_one_of=required_one_of, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - warnings = list() - result = {'changed': False} - if warnings: - result['warnings'] = warnings - - want = map_params_to_obj(module) - have = map_config_to_obj(module) - commands = map_obj_to_commands((want, have), module) - result['commands'] = commands - - if commands: - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - if result['changed']: - check_declarative_intent_params(want, module) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/sros/sros_command.py b/plugins/modules/network/sros/sros_command.py deleted file mode 100644 index 51d525ebb6..0000000000 --- a/plugins/modules/network/sros/sros_command.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - -DOCUMENTATION = ''' ---- -module: sros_command -author: "Peter Sprygada (@privateip)" -short_description: Run commands on remote devices running Nokia SR OS -description: - - Sends arbitrary commands to an SR OS node and returns the results - read from the device. This module includes an argument that will - cause the module to wait for a specific condition before returning - or timing out if the condition is not met. - - This module does not support running commands in configuration mode. - Please use M(sros_config) to configure SR OS devices. -extends_documentation_fragment: -- community.general.sros - -options: - commands: - description: - - List of commands to send to the remote SR OS 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. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - aliases: ['waitfor'] - match: - description: - - The I(match) argument is used in conjunction with the - I(wait_for) argument to specify the match policy. Valid - values are C(all) or C(any). If the value is set to C(all) - then all conditionals in the wait_for must be satisfied. If - the value is set to C(any) then only one of the values must be - satisfied. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -''' - -EXAMPLES = """ -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. ---- -vars: - cli: - host: "{{ inventory_hostname }}" - username: admin - password: admin - transport: cli - ---- -tasks: - - name: run show version on remote devices - sros_command: - commands: show version - provider: "{{ cli }}" - - - name: run show version and check to see if output contains sros - sros_command: - commands: show version - wait_for: result[0] contains sros - provider: "{{ cli }}" - - - name: run multiple commands on remote nodes - sros_command: - commands: - - show version - - show port detail - provider: "{{ cli }}" - - - name: run multiple commands and evaluate the output - sros_command: - commands: - - show version - - show port detail - wait_for: - - result[0] contains TiMOS-B-14.0.R4 - provider: "{{ cli }}" -""" - -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: ['...', '...'] -""" -import time - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList -from ansible.module_utils.six import string_types -from ansible_collections.community.general.plugins.module_utils.network.sros.sros import run_commands, sros_argument_spec, check_args - - -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='sros_command does not support running config mode ' - 'commands. Please use sros_config instead' - ) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list', aliases=['waitfor']), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - argument_spec.update(sros_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 = { - 'changed': False, - 'stdout': responses, - 'stdout_lines': list(to_lines(responses)) - } - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/sros/sros_config.py b/plugins/modules/network/sros/sros_config.py deleted file mode 100644 index a3450d5c18..0000000000 --- a/plugins/modules/network/sros/sros_config.py +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - - -DOCUMENTATION = ''' ---- -module: sros_config -author: "Peter Sprygada (@privateip)" -short_description: Manage Nokia SR OS device configuration -description: - - Nokia SR OS configurations use a simple block indent file syntax - for segmenting configuration into sections. This module provides - an implementation for working with SR OS configuration sections in - a deterministic way. -extends_documentation_fragment: -- community.general.sros - -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. The I(lines) argument only supports current - context lines. See EXAMPLES - 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. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - 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. - 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. - 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(none), the - module will not attempt to compare the source configuration with - the running configuration on the remote device. - default: line - choices: ['line', '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. - default: line - choices: ['line', 'block'] - force: - description: - - The force argument instructs the module to not consider the - current devices running-config. When set to true, this will - cause the module to push the contents of I(src) into the device - without first checking if already configured. - - Note this argument should be considered deprecated. To achieve - the equivalent, set the C(match=none) which is idempotent. This argument - will be removed in a future release. - type: bool - 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' - config: - description: - - The C(config) argument allows the playbook designer to supply - the base configuration to be used to validate configuration - changes necessary. If this argument is provided, the module - will not download the running-config from the remote node. - defaults: - description: - - This argument specifies whether or not to collect all defaults - when getting the remote device running config. When enabled, - the module will get the current config by issuing the command - C(admin display-config detail). - type: bool - default: 'no' - aliases: ['detail'] - 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' - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. ---- -vars: - cli: - host: "{{ inventory_hostname }}" - username: admin - password: admin - transport: cli - ---- -- name: enable rollback location - sros_config: - lines: configure system rollback rollback-location "cf3:/ansible" - provider: "{{ cli }}" - -- name: set system name to {{ inventory_hostname }} using one line - sros_config: - lines: - - configure system name "{{ inventory_hostname }}" - provider: "{{ cli }}" - -- name: set system name to {{ inventory_hostname }} using parents - sros_config: - lines: - - 'name "{{ inventory_hostname }}"' - parents: - - configure - - system - provider: "{{ cli }}" - backup: yes - -- name: load config from file - sros_config: - src: "{{ inventory_hostname }}.cfg" - provider: "{{ cli }}" - save: yes - -- name: invalid use of lines - sros_config: - lines: - - service - - vpls 1000 customer foo 1 create - - description "invalid lines example" - provider: "{{ cli }}" - -- name: valid use of lines - sros_config: - lines: - - description "invalid lines example" - parents: - - service - - vpls 1000 customer foo 1 create - provider: "{{ cli }}" - -- name: configurable backup path - sros_config: - 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: ['config system name "sros01"'] -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['config system name "sros01"'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/sros_config.2016-07-16@22:28:34 -""" -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps -from ansible_collections.community.general.plugins.module_utils.network.sros.sros import sros_argument_spec, check_args -from ansible_collections.community.general.plugins.module_utils.network.sros.sros import load_config, run_commands, get_config - - -def get_active_config(module): - contents = module.params['config'] - if not contents: - flags = [] - if module.params['defaults']: - flags = ['detail'] - return get_config(module, flags) - return contents - - -def get_candidate(module): - candidate = NetworkConfig(indent=4) - if module.params['src']: - candidate.load(module.params['src']) - elif module.params['lines']: - parents = module.params['parents'] or list() - candidate.add(module.params['lines'], parents=parents) - return candidate - - -def run(module, result): - match = module.params['match'] - - candidate = get_candidate(module) - - if match != 'none': - config_text = get_active_config(module) - config = NetworkConfig(indent=4, contents=config_text) - configobjs = candidate.difference(config) - else: - configobjs = candidate.items - - if configobjs: - commands = dumps(configobjs, 'commands') - commands = commands.split('\n') - - result['commands'] = commands - result['updates'] = commands - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - load_config(module, commands) - result['changed'] = True - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - match=dict(default='line', choices=['line', 'none']), - - config=dict(), - defaults=dict(type='bool', default=False, aliases=['detail']), - - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - save=dict(type='bool', default=False), - ) - - argument_spec.update(sros_argument_spec) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True) - - result = dict(changed=False, warnings=list()) - - warnings = list() - check_args(module, warnings) - if warnings: - result['warnings'] = warnings - - if module.params['backup']: - result['__backup__'] = get_config(module) - - run(module, result) - - if module.params['save']: - if not module.check_mode: - run_commands(module, ['admin save']) - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/sros/sros_rollback.py b/plugins/modules/network/sros/sros_rollback.py deleted file mode 100644 index 6168c61bf1..0000000000 --- a/plugins/modules/network/sros/sros_rollback.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/python -# -# Copyright: Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - - -DOCUMENTATION = ''' ---- -module: sros_rollback -author: "Peter Sprygada (@privateip)" -short_description: Configure Nokia SR OS rollback -description: - - Configure the rollback feature on remote Nokia devices running - the SR OS operating system. this module provides a stateful - implementation for managing the configuration of the rollback - feature -extends_documentation_fragment: -- community.general.sros - -options: - rollback_location: - description: - - The I(rollback_location) specifies the location and filename - of the rollback checkpoint files. This argument supports any - valid local or remote URL as specified in SR OS - remote_max_checkpoints: - description: - - The I(remote_max_checkpoints) argument configures the maximum - number of rollback files that can be transferred and saved to - a remote location. Valid values for this argument are in the - range of 1 to 50 - local_max_checkpoints: - description: - - The I(local_max_checkpoints) argument configures the maximum - number of rollback files that can be saved on the devices local - compact flash. Valid values for this argument are in the range - of 1 to 50 - rescue_location: - description: - - The I(rescue_location) specifies the location of the - rescue file. This argument supports any valid local - or remote URL as specified in SR OS - state: - description: - - The I(state) argument specifies the state of the configuration - entries in the devices active configuration. When the state - value is set to C(true) the configuration is present in the - devices active configuration. When the state value is set to - C(false) the configuration values are removed from the devices - active configuration. - default: present - choices: ['present', 'absent'] -''' - -EXAMPLES = """ -# Note: examples below use the following provider dict to handle -# transport and authentication to the node. ---- -vars: - cli: - host: "{{ inventory_hostname }}" - username: admin - password: admin - transport: cli - ---- -- name: configure rollback location - sros_rollback: - rollback_location: "cb3:/ansible" - provider: "{{ cli }}" - -- name: remove all rollback configuration - sros_rollback: - state: absent - provider: "{{ cli }}" -""" - -RETURN = """ -updates: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['...', '...'] -""" -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps -from ansible_collections.community.general.plugins.module_utils.network.sros.sros import load_config, get_config, sros_argument_spec, check_args - - -def invoke(name, *args, **kwargs): - func = globals().get(name) - if func: - return func(*args, **kwargs) - - -def sanitize_config(lines): - commands = list() - for line in lines: - for index, entry in enumerate(commands): - if line.startswith(entry): - del commands[index] - break - commands.append(line) - return commands - - -def present(module, commands): - setters = set() - for key, value in module.argument_spec.items(): - if module.params[key] is not None: - setter = value.get('setter') or 'set_%s' % key - if setter not in setters: - setters.add(setter) - invoke(setter, module, commands) - - -def absent(module, commands): - config = get_config(module) - if 'rollback-location' in config: - commands.append('configure system rollback no rollback-location') - if 'rescue-location' in config: - commands.append('configure system rollback no rescue-location') - if 'remote-max-checkpoints' in config: - commands.append('configure system rollback no remote-max-checkpoints') - if 'local-max-checkpoints' in config: - commands.append('configure system rollback no remote-max-checkpoints') - - -def set_rollback_location(module, commands): - value = module.params['rollback_location'] - commands.append('configure system rollback rollback-location "%s"' % value) - - -def set_local_max_checkpoints(module, commands): - value = module.params['local_max_checkpoints'] - if not 1 <= value <= 50: - module.fail_json(msg='local_max_checkpoints must be between 1 and 50') - commands.append('configure system rollback local-max-checkpoints %s' % value) - - -def set_remote_max_checkpoints(module, commands): - value = module.params['remote_max_checkpoints'] - if not 1 <= value <= 50: - module.fail_json(msg='remote_max_checkpoints must be between 1 and 50') - commands.append('configure system rollback remote-max-checkpoints %s' % value) - - -def set_rescue_location(module, commands): - value = module.params['rescue_location'] - commands.append('configure system rollback rescue-location "%s"' % value) - - -def get_device_config(module): - contents = get_config(module) - return NetworkConfig(indent=4, contents=contents) - - -def main(): - """ main entry point for module execution - """ - argument_spec = dict( - rollback_location=dict(), - - local_max_checkpoints=dict(type='int'), - remote_max_checkpoints=dict(type='int'), - - rescue_location=dict(), - - state=dict(default='present', choices=['present', 'absent']) - ) - - argument_spec.update(sros_argument_spec) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - state = module.params['state'] - - result = dict(changed=False) - - commands = list() - invoke(state, module, commands) - - candidate = NetworkConfig(indent=4, contents='\n'.join(commands)) - config = get_device_config(module) - configobjs = candidate.difference(config) - - if configobjs: - # commands = dumps(configobjs, 'lines') - commands = dumps(configobjs, 'commands') - commands = sanitize_config(commands.split('\n')) - - result['updates'] = commands - result['commands'] = commands - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - load_config(module, commands) - - result['changed'] = True - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/voss/voss_command.py b/plugins/modules/network/voss/voss_command.py deleted file mode 100644 index 551fabb1b0..0000000000 --- a/plugins/modules/network/voss/voss_command.py +++ /dev/null @@ -1,239 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = """ ---- -module: voss_command -author: "Lindsay Hill (@LindsayHill)" -short_description: Run commands on remote devices running Extreme VOSS -description: - - Sends arbitrary commands to an Extreme VSP device running VOSS, 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(voss_config) to configure VOSS devices. -notes: - - Tested against VOSS 7.0.0 -options: - commands: - description: - - List of commands to send to the remote VOSS device. The - resulting output from the command is returned. If the - I(wait_for) argument is provided, the module is not returned - until the condition is satisfied or the number of retries has - expired. If a command sent to the device requires answering a - prompt, it is possible to pass a dict containing I(command), - I(answer) and I(prompt). Common answers are 'y' or "\\r" - (carriage return, must be double quotes). See examples. - required: true - wait_for: - description: - - List of conditions to evaluate against the output of the - command. The task will wait for each condition to be true - before moving forward. If the conditional is not true - within the configured number of retries, the task fails. - See examples. - 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. - default: all - choices: ['any', 'all'] - retries: - description: - - Specifies the number of retries a command should by tried - before it is considered failed. The command is run on the - target device every retry and evaluated against the - I(wait_for) conditions. - 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. - default: 1 -""" - -EXAMPLES = r""" -tasks: - - name: run show sys software on remote devices - voss_command: - commands: show sys software - - - name: run show sys software and check to see if output contains VOSS - voss_command: - commands: show sys software - wait_for: result[0] contains VOSS - - - name: run multiple commands on remote nodes - voss_command: - commands: - - show sys software - - show interfaces vlan - - - name: run multiple commands and evaluate the output - voss_command: - commands: - - show sys software - - show interfaces vlan - wait_for: - - result[0] contains Version - - result[1] contains Basic - - - name: run command that requires answering a prompt - voss_command: - commands: - - command: 'reset' - prompt: 'Are you sure you want to reset the switch? (y/n)' - answer: 'y' -""" - -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: ['...', '...'] -""" -import re -import time - -from ansible_collections.community.general.plugins.module_utils.network.voss.voss import run_commands -from ansible.module_utils.basic import AnsibleModule -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 item in list(commands): - configure_type = re.match(r'conf(?:\w*)(?:\s+(\w+))?', item['command']) - if module.check_mode: - if configure_type and configure_type.group(1) not in ('confirm', 'replace', 'revert', 'network'): - module.fail_json( - msg='voss_command does not support running config mode ' - 'commands. Please use voss_config instead' - ) - if not item['command'].startswith('show'): - warnings.append( - 'only show commands are supported when using check mode, not ' - 'executing `%s`' % item['command'] - ) - commands.remove(item) - return commands - - -def main(): - """main entry point for module execution - """ - argument_spec = dict( - commands=dict(type='list', required=True), - - wait_for=dict(type='list'), - match=dict(default='all', choices=['all', 'any']), - - retries=dict(default=10, type='int'), - interval=dict(default=1, type='int') - ) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - result = {'changed': False} - - warnings = list() - 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/plugins/modules/network/voss/voss_config.py b/plugins/modules/network/voss/voss_config.py deleted file mode 100644 index cbdf2b32b9..0000000000 --- a/plugins/modules/network/voss/voss_config.py +++ /dev/null @@ -1,456 +0,0 @@ -#!/usr/bin/python - -# Copyright: (c) 2018, Extreme Networks 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: voss_config -author: "Lindsay Hill (@LindsayHill)" -short_description: Manage Extreme VOSS configuration sections -description: - - Extreme VOSS configurations use a simple flat text file syntax. - This module provides an implementation for working with EXOS - configuration lines in a deterministic way. -notes: - - Tested against VOSS 7.0.0 - - Abbreviated commands are NOT idempotent, see - L(Network FAQ,../network/user_guide/faq.html#why-do-the-config-modules-always-return-changed-true-with-abbreviated-commands). -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. - aliases: ['commands'] - parents: - description: - - The parent line that uniquely identifies the section the commands - should be checked against. If this argument is omitted, the commands - are checked against the set of top level or global commands. Note - that VOSS configurations only support one level of nested commands. - src: - description: - - Specifies the source path to the file that contains the configuration - or configuration template to load. The path to the source file can - either be the full path on the Ansible control host or a relative - path from the playbook or role root directory. This argument is mutually - exclusive with I(lines), I(parents). - 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. - 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. - 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. - choices: ['line', 'strict', 'exact', 'none'] - default: line - replace: - description: - - Instructs the module on the way to perform the configuration - on the device. If the replace argument is set to I(line) then - the modified lines are pushed to the device in configuration - mode. If the replace argument is set to I(block) then the entire - command block is pushed to the device in configuration mode if any - line is not correct. - default: line - choices: ['line', 'block'] - 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 or role root directory, if playbook is part of an - ansible role. If the directory does not exist, it is created. - type: bool - default: 'no' - running_config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(running_config) argument allows the - implementer to pass in the configuration to use as the base - config for comparison. - aliases: ['config'] - defaults: - description: - - This argument specifies whether or not to collect all defaults - when getting the remote device running config. When enabled, - the module will get the current config by issuing the command - C(show running-config verbose). - type: bool - default: 'no' - save_when: - description: - - When changes are made to the device running-configuration, the - changes are not copied to non-volatile storage by default. Using - this argument will change that behavior. If the argument is set to - I(always), then the running-config will always be saved and the - I(modified) flag will always be set to True. If the argument is set - to I(modified), then the running-config will only be saved if it - has changed since the last save to startup-config. If the argument - is set to I(never), the running-config will never be saved. - If the argument is set to I(changed), then the running-config - will only be saved if the task has made a change. - default: never - choices: ['always', 'never', 'modified', 'changed'] - diff_against: - description: - - When using the C(ansible-playbook --diff) command line argument - the module can generate diffs against different sources. - - When this option is configure as I(startup), the module will return - the diff of the running-config against the startup-config. - - When this option is configured as I(intended), the module will - return the diff of the running-config against the configuration - provided in the C(intended_config) argument. - - When this option is configured as I(running), the module will - return the before and after diff of the running-config with respect - to any changes made to the device configuration. - choices: ['running', 'startup', 'intended'] - diff_ignore_lines: - description: - - Use this argument to specify one or more lines that should be - ignored during the diff. This is used for lines in the configuration - that are automatically updated by the system. This argument takes - a list of regular expressions or exact line matches. - intended_config: - description: - - The C(intended_config) provides the master configuration that - the node should conform to and is used to check the final - running-config against. This argument will not modify any settings - on the remote device and is strictly used to check the compliance - of the current device's configuration against. When specifying this - argument, the task should also modify the C(diff_against) value and - set it to I(intended). - 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 filename - is not given it will be generated based on the hostname, current time and date - in format defined by _config.@ - 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 = """ -- name: configure system name - voss_config: - lines: prompt "{{ inventory_hostname }}" - -- name: configure interface settings - voss_config: - lines: - - name "ServerA" - backup: yes - parents: interface GigabitEthernet 1/1 - -- name: check the running-config against master config - voss_config: - diff_against: intended - intended_config: "{{ lookup('file', 'master.cfg') }}" - -- name: check the startup-config against the running-config - voss_config: - diff_against: startup - diff_ignore_lines: - - qos queue-profile .* - -- name: save running to startup when modified - voss_config: - save_when: modified - -- name: configurable backup path - voss_config: - 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: ['prompt "VSP200"'] -commands: - description: The set of commands that will be pushed to the remote device - returned: always - type: list - sample: ['interface GigabitEthernet 1/1', 'name "ServerA"', 'exit'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/vsp200_config.2018-08-21@15:00:21 -""" -from ansible.module_utils._text import to_text -from ansible.module_utils.connection import ConnectionError -from ansible_collections.community.general.plugins.module_utils.network.voss.voss import run_commands, get_config -from ansible_collections.community.general.plugins.module_utils.network.voss.voss import get_defaults_flag, get_connection -from ansible_collections.community.general.plugins.module_utils.network.voss.voss import get_sublevel_config, VossNetworkConfig -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import dumps - - -def get_candidate_config(module): - candidate = VossNetworkConfig(indent=0) - 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, current_config=None, flags=None): - running = module.params['running_config'] - if not running: - if not module.params['defaults'] and current_config: - running = current_config - else: - running = get_config(module, flags=flags) - - return running - - -def save_config(module, result): - result['changed'] = True - if not module.check_mode: - run_commands(module, 'save config\r') - else: - module.warn('Skipping command `save config` ' - 'due to check_mode. Configuration not copied to ' - 'non-volatile storage') - - -def main(): - """ main entry point for module execution - """ - backup_spec = dict( - filename=dict(), - dir_path=dict(type='path') - ) - argument_spec = dict( - src=dict(type='path'), - - lines=dict(aliases=['commands'], type='list'), - parents=dict(type='list'), - - before=dict(type='list'), - after=dict(type='list'), - - match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), - replace=dict(default='line', choices=['line', 'block']), - - running_config=dict(aliases=['config']), - intended_config=dict(), - - defaults=dict(type='bool', default=False), - backup=dict(type='bool', default=False), - backup_options=dict(type='dict', options=backup_spec), - - save_when=dict(choices=['always', 'never', 'modified', 'changed'], default='never'), - - diff_against=dict(choices=['startup', 'intended', 'running']), - diff_ignore_lines=dict(type='list'), - ) - - mutually_exclusive = [('lines', 'src'), - ('parents', 'src')] - - required_if = [('match', 'strict', ['lines']), - ('match', 'exact', ['lines']), - ('replace', 'block', ['lines']), - ('diff_against', 'intended', ['intended_config'])] - - module = AnsibleModule(argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - required_if=required_if, - supports_check_mode=True) - - result = {'changed': False} - - parents = module.params['parents'] or list() - - match = module.params['match'] - replace = module.params['replace'] - - warnings = list() - result['warnings'] = warnings - - diff_ignore_lines = module.params['diff_ignore_lines'] - - config = None - contents = None - flags = get_defaults_flag(module) if module.params['defaults'] else [] - connection = get_connection(module) - - if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'): - contents = get_config(module, flags=flags) - config = VossNetworkConfig(indent=0, contents=contents) - if module.params['backup']: - result['__backup__'] = contents - - if any((module.params['lines'], module.params['src'])): - candidate = get_candidate_config(module) - if match != 'none': - config = get_running_config(module) - config = VossNetworkConfig(contents=config, indent=0) - - if parents: - config = get_sublevel_config(config, module) - configobjs = candidate.difference(config, match=match, replace=replace) - else: - configobjs = candidate.items - - if configobjs: - commands = dumps(configobjs, 'commands') - commands = commands.split('\n') - - if module.params['before']: - commands[:0] = module.params['before'] - - if module.params['after']: - commands.extend(module.params['after']) - - result['commands'] = commands - result['updates'] = commands - - # send the configuration commands to the device and merge - # them with the current running config - if not module.check_mode: - if commands: - try: - connection.edit_config(candidate=commands) - except ConnectionError as exc: - module.fail_json(msg=to_text(commands, errors='surrogate_then_replace')) - - result['changed'] = True - - running_config = module.params['running_config'] - startup = None - - if module.params['save_when'] == 'always': - save_config(module, result) - elif module.params['save_when'] == 'modified': - match = module.params['match'] - replace = module.params['replace'] - try: - # Note we need to re-retrieve running config, not use cached version - running = connection.get_config(source='running') - startup = connection.get_config(source='startup') - response = connection.get_diff(candidate=startup, running=running, diff_match=match, - diff_ignore_lines=diff_ignore_lines, path=None, - diff_replace=replace) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - config_diff = response['config_diff'] - if config_diff: - save_config(module, result) - elif module.params['save_when'] == 'changed' and result['changed']: - save_config(module, result) - - if module._diff: - if not running_config: - try: - # Note we need to re-retrieve running config, not use cached version - contents = connection.get_config(source='running') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - else: - contents = running_config - - # recreate the object in order to process diff_ignore_lines - running_config = VossNetworkConfig(indent=0, contents=contents, - ignore_lines=diff_ignore_lines) - - if module.params['diff_against'] == 'running': - if module.check_mode: - module.warn("unable to perform diff against running-config due to check mode") - contents = None - else: - contents = config.config_text - - elif module.params['diff_against'] == 'startup': - if not startup: - try: - contents = connection.get_config(source='startup') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - else: - contents = startup - - elif module.params['diff_against'] == 'intended': - contents = module.params['intended_config'] - - if contents is not None: - base_config = VossNetworkConfig(indent=0, contents=contents, - ignore_lines=diff_ignore_lines) - - if running_config.sha1 != base_config.sha1: - if module.params['diff_against'] == 'intended': - before = running_config - after = base_config - elif module.params['diff_against'] in ('startup', 'running'): - before = base_config - after = running_config - - result.update({ - 'changed': True, - 'diff': {'before': str(before), 'after': str(after)} - }) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/network/voss/voss_facts.py b/plugins/modules/network/voss/voss_facts.py deleted file mode 100644 index 9b796648cd..0000000000 --- a/plugins/modules/network/voss/voss_facts.py +++ /dev/null @@ -1,508 +0,0 @@ -#!/usr/bin/python -# -# 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 . -# - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - -DOCUMENTATION = ''' ---- -module: voss_facts -author: "Lindsay Hill (@LindsayHill)" -short_description: Collect facts from remote devices running Extreme VOSS -description: - - Collects a base set of device facts from a remote device that - is running VOSS. This module prepends all of the base network fact - keys with C(ansible_net_). The facts module will always collect - a base set of facts from the device and can enable or disable - collection of additional facts. -notes: - - Tested against VOSS 7.0.0 -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected - to a given subset. Possible values for this argument include - all, hardware, config, and interfaces. Can specify a list of - values to include a larger subset. Values can also be used - with an initial C(M(!)) to specify that a specific subset should - not be collected. - required: false - default: '!config' -''' - -EXAMPLES = """ -# Collect all facts from the device -- voss_facts: - gather_subset: all - -# Collect only the config and default facts -- voss_facts: - gather_subset: - - config - -# Do not collect hardware facts -- voss_facts: - gather_subset: - - "!hardware" -""" - -RETURN = """ -ansible_net_gather_subset: - description: The list of fact subsets collected from the device - returned: always - type: list - -# default -ansible_net_model: - description: The model name returned from the device - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the remote device - returned: always - type: str -ansible_net_version: - description: The operating system version running on the remote device - returned: always - type: str -ansible_net_hostname: - description: The configured hostname of the device - returned: always - type: str - -# hardware -ansible_net_memfree_mb: - description: The available free memory on the remote device in Mb - returned: when hardware is configured - type: int -ansible_net_memtotal_mb: - description: The total memory on the remote device in Mb - returned: when hardware is configured - type: int - -# config -ansible_net_config: - description: The current active config from the device - returned: when config is configured - type: str - -# interfaces -ansible_net_all_ipv4_addresses: - description: All IPv4 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_all_ipv6_addresses: - description: All IPv6 addresses configured on the device - returned: when interfaces is configured - type: list -ansible_net_interfaces: - description: A hash of all interfaces running on the system - returned: when interfaces is configured - type: dict -ansible_net_neighbors: - description: The list of LLDP neighbors from the remote device - returned: when interfaces is configured - type: dict -""" -import re - -from ansible_collections.community.general.plugins.module_utils.network.voss.voss import run_commands -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems - - -class FactsBase(object): - - COMMANDS = list() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, commands=self.COMMANDS, check_rc=False) - - def run(self, cmd): - return run_commands(self.module, commands=cmd, check_rc=False) - - -class Default(FactsBase): - - COMMANDS = ['show sys-info'] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - if data: - self.facts['version'] = self.parse_version(data) - self.facts['serialnum'] = self.parse_serialnum(data) - self.facts['model'] = self.parse_model(data) - self.facts['hostname'] = self.parse_hostname(data) - - def parse_version(self, data): - match = re.search(r'SysDescr\s+: \S+ \((\S+)\)', data) - if match: - return match.group(1) - return '' - - def parse_hostname(self, data): - match = re.search(r'SysName\s+: (\S+)', data, re.M) - if match: - return match.group(1) - return '' - - def parse_model(self, data): - match = re.search(r'Chassis\s+: (\S+)', data, re.M) - if match: - return match.group(1) - return '' - - def parse_serialnum(self, data): - match = re.search(r'Serial#\s+: (\S+)', data) - if match: - return match.group(1) - return '' - - -class Hardware(FactsBase): - - COMMANDS = [ - 'show khi performance memory' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - - if data: - match = re.search(r'Free:\s+(\d+)\s+\(KB\)', data, re.M) - if match: - self.facts['memfree_mb'] = int(round(int(match.group(1)) / 1024, 0)) - match = re.search(r'Used:\s+(\d+)\s+\(KB\)', data, re.M) - if match: - memused_mb = int(round(int(match.group(1)) / 1024, 0)) - self.facts['memtotal_mb'] = self.facts.get('memfree_mb', 0) + memused_mb - - -class Config(FactsBase): - - COMMANDS = ['show running-config'] - - def populate(self): - super(Config, self).populate() - data = self.responses[0] - if data: - self.facts['config'] = data - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'show interfaces gigabitEthernet interface', - 'show interfaces gigabitEthernet name', - 'show ip interface', - 'show ipv6 address interface', - 'show lldp neighbor | include Port|SysName' - ] - - def populate(self): - super(Interfaces, self).populate() - - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - data = self.responses[0] - if data: - interfaces = self.parse_interfaces(data) - self.facts['interfaces'] = self.populate_interfaces_eth(interfaces) - - data = self.responses[1] - if data: - data = self.parse_interfaces(data) - self.populate_interfaces_eth_additional(data) - - data = self.responses[2] - if data: - data = self.parse_interfaces(data) - self.populate_ipv4_interfaces(data) - - data = self.responses[3] - if data: - self.populate_ipv6_interfaces(data) - - data = self.responses[4] - if data: - self.facts['neighbors'] = self.parse_neighbors(data) - - def populate_interfaces_eth(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - match = re.match(r'^\d+\s+(\S+)\s+\w+\s+\w+\s+(\d+)\s+([a-f\d:]+)\s+(\w+)\s+(\w+)$', value) - if match: - intf['mediatype'] = match.group(1) - intf['mtu'] = match.group(2) - intf['macaddress'] = match.group(3) - intf['adminstatus'] = match.group(4) - intf['operstatus'] = match.group(5) - intf['type'] = 'Ethernet' - facts[key] = intf - return facts - - def populate_interfaces_eth_additional(self, interfaces): - for key, value in iteritems(interfaces): - # This matches when no description is set - match = re.match(r'^\w+\s+\w+\s+(\w+)\s+(\d+)\s+\w+$', value) - if match: - self.facts['interfaces'][key]['description'] = '' - self.facts['interfaces'][key]['duplex'] = match.group(1) - self.facts['interfaces'][key]['bandwidth'] = match.group(2) - else: - # This matches when a description is set - match = re.match(r'^(.+)\s+\w+\s+\w+\s+(\w+)\s+(\d+)\s+\w+$', value) - if match: - self.facts['interfaces'][key]['description'] = match.group(1).strip() - self.facts['interfaces'][key]['duplex'] = match.group(2) - self.facts['interfaces'][key]['bandwidth'] = match.group(3) - - def populate_ipv4_interfaces(self, data): - for key, value in data.items(): - if key not in self.facts['interfaces']: - if re.match(r'Vlan\d+', key): - self.facts['interfaces'][key] = dict() - self.facts['interfaces'][key]['type'] = 'VLAN' - elif re.match(r'Clip\d+', key): - self.facts['interfaces'][key] = dict() - self.facts['interfaces'][key]['type'] = 'Loopback' - if re.match(r'Port(\d+/\d+)', key): - key = re.split('Port', key)[1] - self.facts['interfaces'][key]['ipv4'] = list() - match = re.match(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', value, re.M) - if match: - addr = match.group(1) - subnet = match.group(2) - ipv4 = dict(address=addr, subnet=subnet) - self.add_ip_address(addr, 'ipv4') - self.facts['interfaces'][key]['ipv4'].append(ipv4) - - def populate_ipv6_interfaces(self, data): - addresses = re.split(r'-{3,}', data)[1].lstrip() - for line in addresses.split('\n'): - if not line: - break - - match = re.match(r'^([\da-f:]+)/(\d+)\s+([CV])-(\d+)\s+.+$', line) - if match: - address = match.group(1) - subnet = match.group(2) - interface_short_name = match.group(3) - interface_id = match.group(4) - if interface_short_name == 'C': - intf_type = 'Loopback' - interface_name = 'Clip' + interface_id - elif interface_short_name == 'V': - intf_type = 'VLAN' - interface_name = 'Vlan' + interface_id - else: - # Unknown interface type, better to gracefully ignore it for now - break - ipv6 = dict(address=address, subnet=subnet) - self.add_ip_address(address, 'ipv6') - try: - self.facts['interfaces'][interface_name].setdefault('ipv6', []).append(ipv6) - self.facts['interfaces'][interface_name]['type'] = intf_type - except KeyError: - self.facts['interfaces'][interface_name] = dict() - self.facts['interfaces'][interface_name]['type'] = intf_type - self.facts['interfaces'][interface_name].setdefault('ipv6', []).append(ipv6) - else: - break - - def add_ip_address(self, address, family): - if family == 'ipv4': - self.facts['all_ipv4_addresses'].append(address) - else: - self.facts['all_ipv6_addresses'].append(address) - - def parse_neighbors(self, neighbors): - facts = dict() - lines = neighbors.split('Port: ') - if not lines: - return facts - for line in lines: - match = re.search(r'^(\w.*?)\s+Index.*IfName\s+(\w.*)$\s+SysName\s+:\s(\S+)', line, (re.M | re.S)) - if match: - intf = match.group(1) - if intf not in facts: - facts[intf] = list() - fact = dict() - fact['host'] = match.group(3) - fact['port'] = match.group(2) - facts[intf].append(fact) - return facts - - def parse_interfaces(self, data): - parsed = dict() - interfaces = re.split(r'-{3,}', data)[1].lstrip() - for line in interfaces.split('\n'): - if not line or re.match('^All', line): - break - else: - match = re.split(r'^(\S+)\s+', line) - key = match[1] - parsed[key] = match[2].strip() - return parsed - - def parse_description(self, data): - match = re.search(r'Description: (.+)$', data, re.M) - if match: - return match.group(1) - return '' - - def parse_macaddress(self, data): - match = re.search(r'Hardware is (?:.*), address is (\S+)', data) - if match: - return match.group(1) - return '' - - def parse_mtu(self, data): - match = re.search(r'MTU (\d+)', data) - if match: - return int(match.group(1)) - return '' - - def parse_bandwidth(self, data): - match = re.search(r'BW (\d+)', data) - if match: - return int(match.group(1)) - return '' - - def parse_duplex(self, data): - match = re.search(r'(\w+) Duplex', data, re.M) - if match: - return match.group(1) - return '' - - def parse_mediatype(self, data): - match = re.search(r'media type is (.+)$', data, re.M) - if match: - return match.group(1) - return '' - - def parse_type(self, data): - match = re.search(r'Hardware is (.+),', data, re.M) - if match: - return match.group(1) - return '' - - def parse_lineprotocol(self, data): - match = re.search(r'line protocol is (.+)$', data, re.M) - if match: - return match.group(1) - return '' - - def parse_operstatus(self, data): - match = re.search(r'^(?:.+) is (.+),', data, re.M) - if match: - return match.group(1) - return '' - - -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') - ) - - 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() - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/nos_command.py b/plugins/modules/nos_command.py deleted file mode 120000 index 509f7ae14d..0000000000 --- a/plugins/modules/nos_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/nos/nos_command.py \ No newline at end of file diff --git a/plugins/modules/nos_config.py b/plugins/modules/nos_config.py deleted file mode 120000 index 64274df2c0..0000000000 --- a/plugins/modules/nos_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/nos/nos_config.py \ No newline at end of file diff --git a/plugins/modules/nos_facts.py b/plugins/modules/nos_facts.py deleted file mode 120000 index 58a42c9f32..0000000000 --- a/plugins/modules/nos_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/nos/nos_facts.py \ No newline at end of file diff --git a/plugins/modules/nso_action.py b/plugins/modules/nso_action.py deleted file mode 120000 index 4349a3bedf..0000000000 --- a/plugins/modules/nso_action.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_action.py \ No newline at end of file diff --git a/plugins/modules/nso_config.py b/plugins/modules/nso_config.py deleted file mode 120000 index f40cddd6c0..0000000000 --- a/plugins/modules/nso_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_config.py \ No newline at end of file diff --git a/plugins/modules/nso_query.py b/plugins/modules/nso_query.py deleted file mode 120000 index 1f16373264..0000000000 --- a/plugins/modules/nso_query.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_query.py \ No newline at end of file diff --git a/plugins/modules/nso_show.py b/plugins/modules/nso_show.py deleted file mode 120000 index 8628427156..0000000000 --- a/plugins/modules/nso_show.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_show.py \ No newline at end of file diff --git a/plugins/modules/nso_verify.py b/plugins/modules/nso_verify.py deleted file mode 120000 index e412c9649c..0000000000 --- a/plugins/modules/nso_verify.py +++ /dev/null @@ -1 +0,0 @@ -./network/nso/nso_verify.py \ No newline at end of file diff --git a/plugins/modules/nuage_vspk.py b/plugins/modules/nuage_vspk.py deleted file mode 120000 index 28e89d5d3e..0000000000 --- a/plugins/modules/nuage_vspk.py +++ /dev/null @@ -1 +0,0 @@ -./network/nuage/nuage_vspk.py \ No newline at end of file diff --git a/plugins/modules/onyx_aaa.py b/plugins/modules/onyx_aaa.py deleted file mode 120000 index 341c36c000..0000000000 --- a/plugins/modules/onyx_aaa.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_aaa.py \ No newline at end of file diff --git a/plugins/modules/onyx_bfd.py b/plugins/modules/onyx_bfd.py deleted file mode 120000 index b66e6b45f4..0000000000 --- a/plugins/modules/onyx_bfd.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_bfd.py \ No newline at end of file diff --git a/plugins/modules/onyx_bgp.py b/plugins/modules/onyx_bgp.py deleted file mode 120000 index 5c3bf2d6cd..0000000000 --- a/plugins/modules/onyx_bgp.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_bgp.py \ No newline at end of file diff --git a/plugins/modules/onyx_buffer_pool.py b/plugins/modules/onyx_buffer_pool.py deleted file mode 120000 index c9cf364475..0000000000 --- a/plugins/modules/onyx_buffer_pool.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_buffer_pool.py \ No newline at end of file diff --git a/plugins/modules/onyx_command.py b/plugins/modules/onyx_command.py deleted file mode 120000 index 8558620896..0000000000 --- a/plugins/modules/onyx_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_command.py \ No newline at end of file diff --git a/plugins/modules/onyx_config.py b/plugins/modules/onyx_config.py deleted file mode 120000 index 730174552b..0000000000 --- a/plugins/modules/onyx_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_config.py \ No newline at end of file diff --git a/plugins/modules/onyx_facts.py b/plugins/modules/onyx_facts.py deleted file mode 120000 index bcfa2fa330..0000000000 --- a/plugins/modules/onyx_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_facts.py \ No newline at end of file diff --git a/plugins/modules/onyx_igmp.py b/plugins/modules/onyx_igmp.py deleted file mode 120000 index c44ac0367b..0000000000 --- a/plugins/modules/onyx_igmp.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_igmp.py \ No newline at end of file diff --git a/plugins/modules/onyx_igmp_interface.py b/plugins/modules/onyx_igmp_interface.py deleted file mode 120000 index 00afb1ca3e..0000000000 --- a/plugins/modules/onyx_igmp_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_igmp_interface.py \ No newline at end of file diff --git a/plugins/modules/onyx_igmp_vlan.py b/plugins/modules/onyx_igmp_vlan.py deleted file mode 120000 index 726e83d809..0000000000 --- a/plugins/modules/onyx_igmp_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_igmp_vlan.py \ No newline at end of file diff --git a/plugins/modules/onyx_interface.py b/plugins/modules/onyx_interface.py deleted file mode 120000 index a8cd963f6e..0000000000 --- a/plugins/modules/onyx_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_interface.py \ No newline at end of file diff --git a/plugins/modules/onyx_l2_interface.py b/plugins/modules/onyx_l2_interface.py deleted file mode 120000 index 9c5d066b8f..0000000000 --- a/plugins/modules/onyx_l2_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_l2_interface.py \ No newline at end of file diff --git a/plugins/modules/onyx_l3_interface.py b/plugins/modules/onyx_l3_interface.py deleted file mode 120000 index 66f22b459a..0000000000 --- a/plugins/modules/onyx_l3_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_l3_interface.py \ No newline at end of file diff --git a/plugins/modules/onyx_linkagg.py b/plugins/modules/onyx_linkagg.py deleted file mode 120000 index c40e5508f1..0000000000 --- a/plugins/modules/onyx_linkagg.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_linkagg.py \ No newline at end of file diff --git a/plugins/modules/onyx_lldp.py b/plugins/modules/onyx_lldp.py deleted file mode 120000 index 5ba91cc612..0000000000 --- a/plugins/modules/onyx_lldp.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_lldp.py \ No newline at end of file diff --git a/plugins/modules/onyx_lldp_interface.py b/plugins/modules/onyx_lldp_interface.py deleted file mode 120000 index 8e3cd5db39..0000000000 --- a/plugins/modules/onyx_lldp_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_lldp_interface.py \ No newline at end of file diff --git a/plugins/modules/onyx_magp.py b/plugins/modules/onyx_magp.py deleted file mode 120000 index e6065e891b..0000000000 --- a/plugins/modules/onyx_magp.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_magp.py \ No newline at end of file diff --git a/plugins/modules/onyx_mlag_ipl.py b/plugins/modules/onyx_mlag_ipl.py deleted file mode 120000 index 317f2af4da..0000000000 --- a/plugins/modules/onyx_mlag_ipl.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_mlag_ipl.py \ No newline at end of file diff --git a/plugins/modules/onyx_mlag_vip.py b/plugins/modules/onyx_mlag_vip.py deleted file mode 120000 index 8a74fff87a..0000000000 --- a/plugins/modules/onyx_mlag_vip.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_mlag_vip.py \ No newline at end of file diff --git a/plugins/modules/onyx_ntp.py b/plugins/modules/onyx_ntp.py deleted file mode 120000 index b981284b07..0000000000 --- a/plugins/modules/onyx_ntp.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_ntp.py \ No newline at end of file diff --git a/plugins/modules/onyx_ntp_servers_peers.py b/plugins/modules/onyx_ntp_servers_peers.py deleted file mode 120000 index 192cdf20ed..0000000000 --- a/plugins/modules/onyx_ntp_servers_peers.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_ntp_servers_peers.py \ No newline at end of file diff --git a/plugins/modules/onyx_ospf.py b/plugins/modules/onyx_ospf.py deleted file mode 120000 index 8c6bd54a74..0000000000 --- a/plugins/modules/onyx_ospf.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_ospf.py \ No newline at end of file diff --git a/plugins/modules/onyx_pfc_interface.py b/plugins/modules/onyx_pfc_interface.py deleted file mode 120000 index 6bffce2a2b..0000000000 --- a/plugins/modules/onyx_pfc_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_pfc_interface.py \ No newline at end of file diff --git a/plugins/modules/onyx_protocol.py b/plugins/modules/onyx_protocol.py deleted file mode 120000 index cb4f4522cd..0000000000 --- a/plugins/modules/onyx_protocol.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_protocol.py \ No newline at end of file diff --git a/plugins/modules/onyx_ptp_global.py b/plugins/modules/onyx_ptp_global.py deleted file mode 120000 index 684a7f490d..0000000000 --- a/plugins/modules/onyx_ptp_global.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_ptp_global.py \ No newline at end of file diff --git a/plugins/modules/onyx_ptp_interface.py b/plugins/modules/onyx_ptp_interface.py deleted file mode 120000 index 7abb78294e..0000000000 --- a/plugins/modules/onyx_ptp_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_ptp_interface.py \ No newline at end of file diff --git a/plugins/modules/onyx_qos.py b/plugins/modules/onyx_qos.py deleted file mode 120000 index b0404bd942..0000000000 --- a/plugins/modules/onyx_qos.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_qos.py \ No newline at end of file diff --git a/plugins/modules/onyx_snmp.py b/plugins/modules/onyx_snmp.py deleted file mode 120000 index e943bd5d5a..0000000000 --- a/plugins/modules/onyx_snmp.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_snmp.py \ No newline at end of file diff --git a/plugins/modules/onyx_snmp_hosts.py b/plugins/modules/onyx_snmp_hosts.py deleted file mode 120000 index 2864846890..0000000000 --- a/plugins/modules/onyx_snmp_hosts.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_snmp_hosts.py \ No newline at end of file diff --git a/plugins/modules/onyx_snmp_users.py b/plugins/modules/onyx_snmp_users.py deleted file mode 120000 index 1577de921b..0000000000 --- a/plugins/modules/onyx_snmp_users.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_snmp_users.py \ No newline at end of file diff --git a/plugins/modules/onyx_syslog_files.py b/plugins/modules/onyx_syslog_files.py deleted file mode 120000 index eb2c513fcf..0000000000 --- a/plugins/modules/onyx_syslog_files.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_syslog_files.py \ No newline at end of file diff --git a/plugins/modules/onyx_syslog_remote.py b/plugins/modules/onyx_syslog_remote.py deleted file mode 120000 index 5b99c8297d..0000000000 --- a/plugins/modules/onyx_syslog_remote.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_syslog_remote.py \ No newline at end of file diff --git a/plugins/modules/onyx_traffic_class.py b/plugins/modules/onyx_traffic_class.py deleted file mode 120000 index 96c7613ed1..0000000000 --- a/plugins/modules/onyx_traffic_class.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_traffic_class.py \ No newline at end of file diff --git a/plugins/modules/onyx_username.py b/plugins/modules/onyx_username.py deleted file mode 120000 index 06c0110f30..0000000000 --- a/plugins/modules/onyx_username.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_username.py \ No newline at end of file diff --git a/plugins/modules/onyx_vlan.py b/plugins/modules/onyx_vlan.py deleted file mode 120000 index 681ec40665..0000000000 --- a/plugins/modules/onyx_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_vlan.py \ No newline at end of file diff --git a/plugins/modules/onyx_vxlan.py b/plugins/modules/onyx_vxlan.py deleted file mode 120000 index 72dbb9f85f..0000000000 --- a/plugins/modules/onyx_vxlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_vxlan.py \ No newline at end of file diff --git a/plugins/modules/onyx_wjh.py b/plugins/modules/onyx_wjh.py deleted file mode 120000 index 9d4d6bc4c0..0000000000 --- a/plugins/modules/onyx_wjh.py +++ /dev/null @@ -1 +0,0 @@ -./network/onyx/onyx_wjh.py \ No newline at end of file diff --git a/plugins/modules/opx_cps.py b/plugins/modules/opx_cps.py deleted file mode 120000 index 2f1c3ea0b5..0000000000 --- a/plugins/modules/opx_cps.py +++ /dev/null @@ -1 +0,0 @@ -./network/opx/opx_cps.py \ No newline at end of file diff --git a/plugins/modules/ordnance_config.py b/plugins/modules/ordnance_config.py deleted file mode 120000 index 3c5f0fc422..0000000000 --- a/plugins/modules/ordnance_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/ordnance/ordnance_config.py \ No newline at end of file diff --git a/plugins/modules/ordnance_facts.py b/plugins/modules/ordnance_facts.py deleted file mode 120000 index cc177d3cd7..0000000000 --- a/plugins/modules/ordnance_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/ordnance/ordnance_facts.py \ No newline at end of file diff --git a/plugins/modules/panos_admin.py b/plugins/modules/panos_admin.py deleted file mode 120000 index 03f1dfe303..0000000000 --- a/plugins/modules/panos_admin.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_admin.py \ No newline at end of file diff --git a/plugins/modules/panos_admpwd.py b/plugins/modules/panos_admpwd.py deleted file mode 120000 index 602a794ebd..0000000000 --- a/plugins/modules/panos_admpwd.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_admpwd.py \ No newline at end of file diff --git a/plugins/modules/panos_cert_gen_ssh.py b/plugins/modules/panos_cert_gen_ssh.py deleted file mode 120000 index ff23e5d7f0..0000000000 --- a/plugins/modules/panos_cert_gen_ssh.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_cert_gen_ssh.py \ No newline at end of file diff --git a/plugins/modules/panos_check.py b/plugins/modules/panos_check.py deleted file mode 120000 index 213384f99c..0000000000 --- a/plugins/modules/panos_check.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_check.py \ No newline at end of file diff --git a/plugins/modules/panos_commit.py b/plugins/modules/panos_commit.py deleted file mode 120000 index effa4d89d7..0000000000 --- a/plugins/modules/panos_commit.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_commit.py \ No newline at end of file diff --git a/plugins/modules/panos_dag.py b/plugins/modules/panos_dag.py deleted file mode 120000 index a8c23bd368..0000000000 --- a/plugins/modules/panos_dag.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_dag.py \ No newline at end of file diff --git a/plugins/modules/panos_dag_tags.py b/plugins/modules/panos_dag_tags.py deleted file mode 120000 index 5dbc7c8f67..0000000000 --- a/plugins/modules/panos_dag_tags.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_dag_tags.py \ No newline at end of file diff --git a/plugins/modules/panos_import.py b/plugins/modules/panos_import.py deleted file mode 120000 index cccb29a842..0000000000 --- a/plugins/modules/panos_import.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_import.py \ No newline at end of file diff --git a/plugins/modules/panos_interface.py b/plugins/modules/panos_interface.py deleted file mode 120000 index bc9f4542c2..0000000000 --- a/plugins/modules/panos_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_interface.py \ No newline at end of file diff --git a/plugins/modules/panos_lic.py b/plugins/modules/panos_lic.py deleted file mode 120000 index 41a6f06fb4..0000000000 --- a/plugins/modules/panos_lic.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_lic.py \ No newline at end of file diff --git a/plugins/modules/panos_loadcfg.py b/plugins/modules/panos_loadcfg.py deleted file mode 120000 index 380a24ee9f..0000000000 --- a/plugins/modules/panos_loadcfg.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_loadcfg.py \ No newline at end of file diff --git a/plugins/modules/panos_match_rule.py b/plugins/modules/panos_match_rule.py deleted file mode 120000 index 29a7c7ac75..0000000000 --- a/plugins/modules/panos_match_rule.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_match_rule.py \ No newline at end of file diff --git a/plugins/modules/panos_mgtconfig.py b/plugins/modules/panos_mgtconfig.py deleted file mode 120000 index 62639cb455..0000000000 --- a/plugins/modules/panos_mgtconfig.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_mgtconfig.py \ No newline at end of file diff --git a/plugins/modules/panos_nat_rule.py b/plugins/modules/panos_nat_rule.py deleted file mode 120000 index ffaa6827b7..0000000000 --- a/plugins/modules/panos_nat_rule.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_nat_rule.py \ No newline at end of file diff --git a/plugins/modules/panos_object.py b/plugins/modules/panos_object.py deleted file mode 120000 index 652b1474e2..0000000000 --- a/plugins/modules/panos_object.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_object.py \ No newline at end of file diff --git a/plugins/modules/panos_op.py b/plugins/modules/panos_op.py deleted file mode 120000 index 75db322285..0000000000 --- a/plugins/modules/panos_op.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_op.py \ No newline at end of file diff --git a/plugins/modules/panos_pg.py b/plugins/modules/panos_pg.py deleted file mode 120000 index 2bae9ed1e9..0000000000 --- a/plugins/modules/panos_pg.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_pg.py \ No newline at end of file diff --git a/plugins/modules/panos_query_rules.py b/plugins/modules/panos_query_rules.py deleted file mode 120000 index eda85e3d74..0000000000 --- a/plugins/modules/panos_query_rules.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_query_rules.py \ No newline at end of file diff --git a/plugins/modules/panos_restart.py b/plugins/modules/panos_restart.py deleted file mode 120000 index 980993b9b1..0000000000 --- a/plugins/modules/panos_restart.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_restart.py \ No newline at end of file diff --git a/plugins/modules/panos_sag.py b/plugins/modules/panos_sag.py deleted file mode 120000 index 241b50a98b..0000000000 --- a/plugins/modules/panos_sag.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_sag.py \ No newline at end of file diff --git a/plugins/modules/panos_security_rule.py b/plugins/modules/panos_security_rule.py deleted file mode 120000 index 3db5bd0301..0000000000 --- a/plugins/modules/panos_security_rule.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_security_rule.py \ No newline at end of file diff --git a/plugins/modules/panos_set.py b/plugins/modules/panos_set.py deleted file mode 120000 index a120b896f4..0000000000 --- a/plugins/modules/panos_set.py +++ /dev/null @@ -1 +0,0 @@ -./network/panos/panos_set.py \ No newline at end of file diff --git a/plugins/modules/pn_access_list.py b/plugins/modules/pn_access_list.py deleted file mode 120000 index 2ffff34e09..0000000000 --- a/plugins/modules/pn_access_list.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_access_list.py \ No newline at end of file diff --git a/plugins/modules/pn_access_list_ip.py b/plugins/modules/pn_access_list_ip.py deleted file mode 120000 index bcb9d53c54..0000000000 --- a/plugins/modules/pn_access_list_ip.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_access_list_ip.py \ No newline at end of file diff --git a/plugins/modules/pn_admin_service.py b/plugins/modules/pn_admin_service.py deleted file mode 120000 index 703f573b04..0000000000 --- a/plugins/modules/pn_admin_service.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_admin_service.py \ No newline at end of file diff --git a/plugins/modules/pn_admin_session_timeout.py b/plugins/modules/pn_admin_session_timeout.py deleted file mode 120000 index 61a22c5a74..0000000000 --- a/plugins/modules/pn_admin_session_timeout.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_admin_session_timeout.py \ No newline at end of file diff --git a/plugins/modules/pn_admin_syslog.py b/plugins/modules/pn_admin_syslog.py deleted file mode 120000 index f038347ed6..0000000000 --- a/plugins/modules/pn_admin_syslog.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_admin_syslog.py \ No newline at end of file diff --git a/plugins/modules/pn_cluster.py b/plugins/modules/pn_cluster.py deleted file mode 120000 index 0c9b5db26c..0000000000 --- a/plugins/modules/pn_cluster.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_cluster.py \ No newline at end of file diff --git a/plugins/modules/pn_connection_stats_settings.py b/plugins/modules/pn_connection_stats_settings.py deleted file mode 120000 index e9eb9be714..0000000000 --- a/plugins/modules/pn_connection_stats_settings.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_connection_stats_settings.py \ No newline at end of file diff --git a/plugins/modules/pn_cpu_class.py b/plugins/modules/pn_cpu_class.py deleted file mode 120000 index 0a39c792f3..0000000000 --- a/plugins/modules/pn_cpu_class.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_cpu_class.py \ No newline at end of file diff --git a/plugins/modules/pn_cpu_mgmt_class.py b/plugins/modules/pn_cpu_mgmt_class.py deleted file mode 120000 index 6fb2804639..0000000000 --- a/plugins/modules/pn_cpu_mgmt_class.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_cpu_mgmt_class.py \ No newline at end of file diff --git a/plugins/modules/pn_dhcp_filter.py b/plugins/modules/pn_dhcp_filter.py deleted file mode 120000 index 160126fa1b..0000000000 --- a/plugins/modules/pn_dhcp_filter.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_dhcp_filter.py \ No newline at end of file diff --git a/plugins/modules/pn_dscp_map.py b/plugins/modules/pn_dscp_map.py deleted file mode 120000 index 7678b4e448..0000000000 --- a/plugins/modules/pn_dscp_map.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_dscp_map.py \ No newline at end of file diff --git a/plugins/modules/pn_dscp_map_pri_map.py b/plugins/modules/pn_dscp_map_pri_map.py deleted file mode 120000 index a806846ec5..0000000000 --- a/plugins/modules/pn_dscp_map_pri_map.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_dscp_map_pri_map.py \ No newline at end of file diff --git a/plugins/modules/pn_fabric_local.py b/plugins/modules/pn_fabric_local.py deleted file mode 120000 index 5c62bf8d6a..0000000000 --- a/plugins/modules/pn_fabric_local.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_fabric_local.py \ No newline at end of file diff --git a/plugins/modules/pn_igmp_snooping.py b/plugins/modules/pn_igmp_snooping.py deleted file mode 120000 index 2817f49f2e..0000000000 --- a/plugins/modules/pn_igmp_snooping.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_igmp_snooping.py \ No newline at end of file diff --git a/plugins/modules/pn_ipv6security_raguard.py b/plugins/modules/pn_ipv6security_raguard.py deleted file mode 120000 index 4ca6d89875..0000000000 --- a/plugins/modules/pn_ipv6security_raguard.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_ipv6security_raguard.py \ No newline at end of file diff --git a/plugins/modules/pn_ipv6security_raguard_port.py b/plugins/modules/pn_ipv6security_raguard_port.py deleted file mode 120000 index f7bab39151..0000000000 --- a/plugins/modules/pn_ipv6security_raguard_port.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_ipv6security_raguard_port.py \ No newline at end of file diff --git a/plugins/modules/pn_ipv6security_raguard_vlan.py b/plugins/modules/pn_ipv6security_raguard_vlan.py deleted file mode 120000 index e770fb7561..0000000000 --- a/plugins/modules/pn_ipv6security_raguard_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_ipv6security_raguard_vlan.py \ No newline at end of file diff --git a/plugins/modules/pn_log_audit_exception.py b/plugins/modules/pn_log_audit_exception.py deleted file mode 120000 index 03c1b26c27..0000000000 --- a/plugins/modules/pn_log_audit_exception.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_log_audit_exception.py \ No newline at end of file diff --git a/plugins/modules/pn_ospf.py b/plugins/modules/pn_ospf.py deleted file mode 120000 index bf8d48791e..0000000000 --- a/plugins/modules/pn_ospf.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_ospf.py \ No newline at end of file diff --git a/plugins/modules/pn_ospfarea.py b/plugins/modules/pn_ospfarea.py deleted file mode 120000 index df1d2a6cbf..0000000000 --- a/plugins/modules/pn_ospfarea.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_ospfarea.py \ No newline at end of file diff --git a/plugins/modules/pn_port_config.py b/plugins/modules/pn_port_config.py deleted file mode 120000 index ef254981fd..0000000000 --- a/plugins/modules/pn_port_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_port_config.py \ No newline at end of file diff --git a/plugins/modules/pn_port_cos_bw.py b/plugins/modules/pn_port_cos_bw.py deleted file mode 120000 index 4a6e756ce9..0000000000 --- a/plugins/modules/pn_port_cos_bw.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_port_cos_bw.py \ No newline at end of file diff --git a/plugins/modules/pn_port_cos_rate_setting.py b/plugins/modules/pn_port_cos_rate_setting.py deleted file mode 120000 index 4c3f3c9f59..0000000000 --- a/plugins/modules/pn_port_cos_rate_setting.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_port_cos_rate_setting.py \ No newline at end of file diff --git a/plugins/modules/pn_prefix_list.py b/plugins/modules/pn_prefix_list.py deleted file mode 120000 index 1e8b8a08b1..0000000000 --- a/plugins/modules/pn_prefix_list.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_prefix_list.py \ No newline at end of file diff --git a/plugins/modules/pn_prefix_list_network.py b/plugins/modules/pn_prefix_list_network.py deleted file mode 120000 index 3824c654d5..0000000000 --- a/plugins/modules/pn_prefix_list_network.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_prefix_list_network.py \ No newline at end of file diff --git a/plugins/modules/pn_role.py b/plugins/modules/pn_role.py deleted file mode 120000 index f39dbd03fc..0000000000 --- a/plugins/modules/pn_role.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_role.py \ No newline at end of file diff --git a/plugins/modules/pn_show.py b/plugins/modules/pn_show.py deleted file mode 120000 index 1d6f969bdc..0000000000 --- a/plugins/modules/pn_show.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_show.py \ No newline at end of file diff --git a/plugins/modules/pn_snmp_community.py b/plugins/modules/pn_snmp_community.py deleted file mode 120000 index e9ab9e2e6d..0000000000 --- a/plugins/modules/pn_snmp_community.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_snmp_community.py \ No newline at end of file diff --git a/plugins/modules/pn_snmp_trap_sink.py b/plugins/modules/pn_snmp_trap_sink.py deleted file mode 120000 index d137acffde..0000000000 --- a/plugins/modules/pn_snmp_trap_sink.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_snmp_trap_sink.py \ No newline at end of file diff --git a/plugins/modules/pn_snmp_vacm.py b/plugins/modules/pn_snmp_vacm.py deleted file mode 120000 index a7ff2ca5e7..0000000000 --- a/plugins/modules/pn_snmp_vacm.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_snmp_vacm.py \ No newline at end of file diff --git a/plugins/modules/pn_stp.py b/plugins/modules/pn_stp.py deleted file mode 120000 index 6fc036da66..0000000000 --- a/plugins/modules/pn_stp.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_stp.py \ No newline at end of file diff --git a/plugins/modules/pn_stp_port.py b/plugins/modules/pn_stp_port.py deleted file mode 120000 index 64884184f2..0000000000 --- a/plugins/modules/pn_stp_port.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_stp_port.py \ No newline at end of file diff --git a/plugins/modules/pn_switch_setup.py b/plugins/modules/pn_switch_setup.py deleted file mode 120000 index dc9d91a3a0..0000000000 --- a/plugins/modules/pn_switch_setup.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_switch_setup.py \ No newline at end of file diff --git a/plugins/modules/pn_trunk.py b/plugins/modules/pn_trunk.py deleted file mode 120000 index a2c03678c1..0000000000 --- a/plugins/modules/pn_trunk.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_trunk.py \ No newline at end of file diff --git a/plugins/modules/pn_user.py b/plugins/modules/pn_user.py deleted file mode 120000 index bc086bb5ec..0000000000 --- a/plugins/modules/pn_user.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_user.py \ No newline at end of file diff --git a/plugins/modules/pn_vflow_table_profile.py b/plugins/modules/pn_vflow_table_profile.py deleted file mode 120000 index 3245aeef07..0000000000 --- a/plugins/modules/pn_vflow_table_profile.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vflow_table_profile.py \ No newline at end of file diff --git a/plugins/modules/pn_vlag.py b/plugins/modules/pn_vlag.py deleted file mode 120000 index d5b98ecdb7..0000000000 --- a/plugins/modules/pn_vlag.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vlag.py \ No newline at end of file diff --git a/plugins/modules/pn_vlan.py b/plugins/modules/pn_vlan.py deleted file mode 120000 index aa2c098b19..0000000000 --- a/plugins/modules/pn_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vlan.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter.py b/plugins/modules/pn_vrouter.py deleted file mode 120000 index fe01fb146b..0000000000 --- a/plugins/modules/pn_vrouter.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter_bgp.py b/plugins/modules/pn_vrouter_bgp.py deleted file mode 120000 index e7d0568436..0000000000 --- a/plugins/modules/pn_vrouter_bgp.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter_bgp.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter_bgp_network.py b/plugins/modules/pn_vrouter_bgp_network.py deleted file mode 120000 index 27f25f411c..0000000000 --- a/plugins/modules/pn_vrouter_bgp_network.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter_bgp_network.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter_interface_ip.py b/plugins/modules/pn_vrouter_interface_ip.py deleted file mode 120000 index cd669092d6..0000000000 --- a/plugins/modules/pn_vrouter_interface_ip.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter_interface_ip.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter_loopback_interface.py b/plugins/modules/pn_vrouter_loopback_interface.py deleted file mode 120000 index e188dc6326..0000000000 --- a/plugins/modules/pn_vrouter_loopback_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter_loopback_interface.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter_ospf.py b/plugins/modules/pn_vrouter_ospf.py deleted file mode 120000 index 5f0fbb373d..0000000000 --- a/plugins/modules/pn_vrouter_ospf.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter_ospf.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter_ospf6.py b/plugins/modules/pn_vrouter_ospf6.py deleted file mode 120000 index 183352cc36..0000000000 --- a/plugins/modules/pn_vrouter_ospf6.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter_ospf6.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter_packet_relay.py b/plugins/modules/pn_vrouter_packet_relay.py deleted file mode 120000 index e168cb31bb..0000000000 --- a/plugins/modules/pn_vrouter_packet_relay.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter_packet_relay.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouter_pim_config.py b/plugins/modules/pn_vrouter_pim_config.py deleted file mode 120000 index 30386f5613..0000000000 --- a/plugins/modules/pn_vrouter_pim_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouter_pim_config.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouterbgp.py b/plugins/modules/pn_vrouterbgp.py deleted file mode 120000 index 2c985cd336..0000000000 --- a/plugins/modules/pn_vrouterbgp.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouterbgp.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouterif.py b/plugins/modules/pn_vrouterif.py deleted file mode 120000 index 74da3eabd4..0000000000 --- a/plugins/modules/pn_vrouterif.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouterif.py \ No newline at end of file diff --git a/plugins/modules/pn_vrouterlbif.py b/plugins/modules/pn_vrouterlbif.py deleted file mode 120000 index 2fcbd596b6..0000000000 --- a/plugins/modules/pn_vrouterlbif.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vrouterlbif.py \ No newline at end of file diff --git a/plugins/modules/pn_vtep.py b/plugins/modules/pn_vtep.py deleted file mode 120000 index 66fab46f01..0000000000 --- a/plugins/modules/pn_vtep.py +++ /dev/null @@ -1 +0,0 @@ -./network/netvisor/pn_vtep.py \ No newline at end of file diff --git a/plugins/modules/routeros_command.py b/plugins/modules/routeros_command.py deleted file mode 120000 index 81f40e6622..0000000000 --- a/plugins/modules/routeros_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/routeros/routeros_command.py \ No newline at end of file diff --git a/plugins/modules/routeros_facts.py b/plugins/modules/routeros_facts.py deleted file mode 120000 index d4aca7cad7..0000000000 --- a/plugins/modules/routeros_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/routeros/routeros_facts.py \ No newline at end of file diff --git a/plugins/modules/slxos_command.py b/plugins/modules/slxos_command.py deleted file mode 120000 index 61dd0d94b4..0000000000 --- a/plugins/modules/slxos_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_command.py \ No newline at end of file diff --git a/plugins/modules/slxos_config.py b/plugins/modules/slxos_config.py deleted file mode 120000 index 0f31e749dd..0000000000 --- a/plugins/modules/slxos_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_config.py \ No newline at end of file diff --git a/plugins/modules/slxos_facts.py b/plugins/modules/slxos_facts.py deleted file mode 120000 index 6f6c8023b3..0000000000 --- a/plugins/modules/slxos_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_facts.py \ No newline at end of file diff --git a/plugins/modules/slxos_interface.py b/plugins/modules/slxos_interface.py deleted file mode 120000 index fe1c7b6e7f..0000000000 --- a/plugins/modules/slxos_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_interface.py \ No newline at end of file diff --git a/plugins/modules/slxos_l2_interface.py b/plugins/modules/slxos_l2_interface.py deleted file mode 120000 index da30a532d0..0000000000 --- a/plugins/modules/slxos_l2_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_l2_interface.py \ No newline at end of file diff --git a/plugins/modules/slxos_l3_interface.py b/plugins/modules/slxos_l3_interface.py deleted file mode 120000 index 46e0c44a5d..0000000000 --- a/plugins/modules/slxos_l3_interface.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_l3_interface.py \ No newline at end of file diff --git a/plugins/modules/slxos_linkagg.py b/plugins/modules/slxos_linkagg.py deleted file mode 120000 index 14756b3211..0000000000 --- a/plugins/modules/slxos_linkagg.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_linkagg.py \ No newline at end of file diff --git a/plugins/modules/slxos_lldp.py b/plugins/modules/slxos_lldp.py deleted file mode 120000 index 7329b2e4f1..0000000000 --- a/plugins/modules/slxos_lldp.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_lldp.py \ No newline at end of file diff --git a/plugins/modules/slxos_vlan.py b/plugins/modules/slxos_vlan.py deleted file mode 120000 index 686b4437a2..0000000000 --- a/plugins/modules/slxos_vlan.py +++ /dev/null @@ -1 +0,0 @@ -./network/slxos/slxos_vlan.py \ No newline at end of file diff --git a/plugins/modules/sros_command.py b/plugins/modules/sros_command.py deleted file mode 120000 index dcaa32ef62..0000000000 --- a/plugins/modules/sros_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/sros/sros_command.py \ No newline at end of file diff --git a/plugins/modules/sros_config.py b/plugins/modules/sros_config.py deleted file mode 120000 index da6d4cb1e8..0000000000 --- a/plugins/modules/sros_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/sros/sros_config.py \ No newline at end of file diff --git a/plugins/modules/sros_rollback.py b/plugins/modules/sros_rollback.py deleted file mode 120000 index 68dc2ff762..0000000000 --- a/plugins/modules/sros_rollback.py +++ /dev/null @@ -1 +0,0 @@ -./network/sros/sros_rollback.py \ No newline at end of file diff --git a/plugins/modules/vdirect_commit.py b/plugins/modules/vdirect_commit.py deleted file mode 120000 index 77ced1e80c..0000000000 --- a/plugins/modules/vdirect_commit.py +++ /dev/null @@ -1 +0,0 @@ -./network/radware/vdirect_commit.py \ No newline at end of file diff --git a/plugins/modules/vdirect_file.py b/plugins/modules/vdirect_file.py deleted file mode 120000 index 24486fe858..0000000000 --- a/plugins/modules/vdirect_file.py +++ /dev/null @@ -1 +0,0 @@ -./network/radware/vdirect_file.py \ No newline at end of file diff --git a/plugins/modules/vdirect_runnable.py b/plugins/modules/vdirect_runnable.py deleted file mode 120000 index d7e4882489..0000000000 --- a/plugins/modules/vdirect_runnable.py +++ /dev/null @@ -1 +0,0 @@ -./network/radware/vdirect_runnable.py \ No newline at end of file diff --git a/plugins/modules/voss_command.py b/plugins/modules/voss_command.py deleted file mode 120000 index 885d65137f..0000000000 --- a/plugins/modules/voss_command.py +++ /dev/null @@ -1 +0,0 @@ -./network/voss/voss_command.py \ No newline at end of file diff --git a/plugins/modules/voss_config.py b/plugins/modules/voss_config.py deleted file mode 120000 index 6557f1bd87..0000000000 --- a/plugins/modules/voss_config.py +++ /dev/null @@ -1 +0,0 @@ -./network/voss/voss_config.py \ No newline at end of file diff --git a/plugins/modules/voss_facts.py b/plugins/modules/voss_facts.py deleted file mode 120000 index a9cf7ac21f..0000000000 --- a/plugins/modules/voss_facts.py +++ /dev/null @@ -1 +0,0 @@ -./network/voss/voss_facts.py \ No newline at end of file diff --git a/plugins/netconf/__init__.py b/plugins/netconf/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/netconf/ce.py b/plugins/netconf/ce.py deleted file mode 100644 index 14317681c9..0000000000 --- a/plugins/netconf/ce.py +++ /dev/null @@ -1,247 +0,0 @@ -# -# (c) 2017 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -netconf: ce -short_description: Use ce netconf plugin to run netconf commands on Huawei Cloudengine platform -description: - - This ce plugin provides low level abstraction apis for - sending and receiving netconf commands from Huawei Cloudengine network devices. -options: - ncclient_device_handler: - type: str - default: huawei - description: - - Specifies the ncclient device handler name for Huawei Cloudengine. - To identify the ncclient device handler name refer ncclient library documentation. -''' - -import json -import re - -from ansible.module_utils._text import to_text, to_bytes -from ansible.errors import AnsibleConnectionFailure -from ansible.plugins.netconf import NetconfBase, ensure_ncclient - -try: - from ncclient import manager - from ncclient.operations import RPCError - from ncclient.transport.errors import SSHUnknownHostError - from ncclient.xml_ import to_ele, to_xml, new_ele - HAS_NCCLIENT = True -except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError - HAS_NCCLIENT = False - -try: - from lxml.etree import fromstring -except ImportError: - from xml.etree.ElementTree import fromstring - - -class Netconf(NetconfBase): - - @ensure_ncclient - def get_text(self, ele, tag): - try: - return to_text(ele.find(tag).text, errors='surrogate_then_replace').strip() - except AttributeError: - pass - - @ensure_ncclient - def get_device_info(self): - device_info = dict() - device_info['network_os'] = 'ce' - filter_xml = ''' - - - - - - - - - - ''' - data = self.get(filter_xml) - data = re.sub(r'xmlns=".+?"', r'', data) - reply = fromstring(to_bytes(data, errors='surrogate_or_strict')) - sw_info = reply.find('.//systemInfo') - - device_info['network_os_version'] = self.get_text(sw_info, 'productVer') - device_info['network_os_hostname'] = self.get_text(sw_info, 'sysName') - device_info['network_os_platform_version'] = self.get_text(sw_info, 'platformVer') - device_info['network_os_platform'] = self.get_text(sw_info, 'productName') - - return device_info - - def execute_rpc(self, name): - """RPC to be execute on remote device - :name: Name of rpc in string format""" - return self.rpc(name) - - @ensure_ncclient - def load_configuration(self, *args, **kwargs): - """Loads given configuration on device - :format: Format of configuration (xml, text, set) - :action: Action to be performed (merge, replace, override, update) - :target: is the name of the configuration datastore being edited - :config: is the configuration in string format.""" - if kwargs.get('config'): - kwargs['config'] = to_bytes(kwargs['config'], errors='surrogate_or_strict') - if kwargs.get('format', 'xml') == 'xml': - kwargs['config'] = to_ele(kwargs['config']) - - try: - return self.m.load_configuration(*args, **kwargs).data_xml - except RPCError as exc: - raise Exception(to_xml(exc.xml)) - - def get_capabilities(self): - result = dict() - result['rpc'] = self.get_base_rpc() + ['execute_rpc', 'load_configuration', 'get_configuration', 'compare_configuration', - 'execute_action', 'halt', 'reboot', 'execute_nc_cli', 'dispatch_rpc'] - result['network_api'] = 'netconf' - result['device_info'] = self.get_device_info() - result['server_capabilities'] = [c for c in self.m.server_capabilities] - result['client_capabilities'] = [c for c in self.m.client_capabilities] - result['session_id'] = self.m.session_id - return json.dumps(result) - - @staticmethod - @ensure_ncclient - def guess_network_os(obj): - try: - m = manager.connect( - host=obj._play_context.remote_addr, - port=obj._play_context.port or 830, - username=obj._play_context.remote_user, - password=obj._play_context.password, - key_filename=obj.key_filename, - hostkey_verify=obj.get_option('host_key_checking'), - look_for_keys=obj.get_option('look_for_keys'), - allow_agent=obj._play_context.allow_agent, - timeout=obj.get_option('persistent_connect_timeout'), - # We need to pass in the path to the ssh_config file when guessing - # the network_os so that a jumphost is correctly used if defined - ssh_config=obj._ssh_config - ) - except SSHUnknownHostError as exc: - raise AnsibleConnectionFailure(to_text(exc)) - - guessed_os = None - for c in m.server_capabilities: - if re.search('huawei', c): - guessed_os = 'ce' - break - - m.close_session() - return guessed_os - - def get_configuration(self, *args, **kwargs): - """Retrieve all or part of a specified configuration. - :format: format in configuration should be retrieved - :filter: specifies the portion of the configuration to retrieve - (by default entire configuration is retrieved)""" - return self.m.get_configuration(*args, **kwargs).data_xml - - def compare_configuration(self, *args, **kwargs): - """Compare configuration - :rollback: rollback id""" - return self.m.compare_configuration(*args, **kwargs).data_xml - - @ensure_ncclient - def execute_action(self, xml_str): - """huawei execute-action""" - con_obj = None - try: - con_obj = self.m.action(action=xml_str) - except RPCError as exc: - raise Exception(to_xml(exc.xml)) - - return con_obj.xml - - def halt(self): - """reboot the device""" - return self.m.halt().data_xml - - def reboot(self): - """reboot the device""" - return self.m.reboot().data_xml - - @ensure_ncclient - def get(self, *args, **kwargs): - try: - if_rpc_reply = kwargs.pop('if_rpc_reply', False) - if if_rpc_reply: - return self.m.get(*args, **kwargs).xml - return self.m.get(*args, **kwargs).data_xml - except RPCError as exc: - raise Exception(to_xml(exc.xml)) - - @ensure_ncclient - def get_config(self, *args, **kwargs): - try: - return self.m.get_config(*args, **kwargs).data_xml - except RPCError as exc: - raise Exception(to_xml(exc.xml)) - - @ensure_ncclient - def edit_config(self, *args, **kwargs): - try: - return self.m.edit_config(*args, **kwargs).xml - except RPCError as exc: - raise Exception(to_xml(exc.xml)) - - @ensure_ncclient - def execute_nc_cli(self, *args, **kwargs): - try: - return self.m.cli(*args, **kwargs).xml - except RPCError as exc: - raise Exception(to_xml(exc.xml)) - - @ensure_ncclient - def commit(self, *args, **kwargs): - try: - return self.m.commit(*args, **kwargs).data_xml - except RPCError as exc: - raise Exception(to_xml(exc.xml)) - - def validate(self, *args, **kwargs): - return self.m.validate(*args, **kwargs).data_xml - - def discard_changes(self, *args, **kwargs): - return self.m.discard_changes(*args, **kwargs).data_xml - - @ensure_ncclient - def dispatch_rpc(self, rpc_command=None, source=None, filter=None): - """ - Execute rpc on the remote device eg. dispatch('get-next') - :param rpc_command: specifies rpc command to be dispatched either in plain text or in xml element format (depending on command) - :param source: name of the configuration datastore being queried - :param filter: specifies the portion of the configuration to retrieve (by default entire configuration is retrieved) - :return: Returns xml string containing the rpc-reply response received from remote host - """ - if rpc_command is None: - raise ValueError('rpc_command value must be provided') - resp = self.m.dispatch(fromstring(rpc_command), source=source, filter=filter) - # just return rpc-reply xml - return resp.xml diff --git a/plugins/netconf/sros.py b/plugins/netconf/sros.py deleted file mode 100644 index 1b25d32166..0000000000 --- a/plugins/netconf/sros.py +++ /dev/null @@ -1,119 +0,0 @@ -# -# (c) 2018 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -DOCUMENTATION = ''' ---- -netconf: sros -short_description: Use Nokia SROS netconf plugin to run netconf commands on Nokia SROS platform -deprecated: - why: This plugin moved in 'nokia.sros' collection - removed_in: '2.13' - alternative: "Use the netconf plugin in 'nokia.sros' collection within Ansible galaxy" -description: - - This sros plugin provides low level abstraction apis for - sending and receiving netconf commands from Nokia sros network devices. -options: - ncclient_device_handler: - type: str - default: default - description: - - Specifies the ncclient device handler name for Nokia sros network os. To - identify the ncclient device handler name refer ncclient library documentation. -''' - -import json -import re - -from ansible.module_utils._text import to_text, to_native -from ansible.errors import AnsibleConnectionFailure -from ansible.plugins.netconf import NetconfBase -from ansible.plugins.netconf import ensure_ncclient - -try: - from ncclient import manager - from ncclient.transport.errors import SSHUnknownHostError - from ncclient.xml_ import to_ele - HAS_NCCLIENT = True -except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError - HAS_NCCLIENT = False - - -class Netconf(NetconfBase): - def get_text(self, ele, tag): - try: - return to_text(ele.find(tag).text, errors='surrogate_then_replace').strip() - except AttributeError: - pass - - @ensure_ncclient - def get_device_info(self): - device_info = dict() - device_info['network_os'] = 'sros' - - xmlns = "urn:nokia.com:sros:ns:yang:sr:state" - f = '' % xmlns - reply = to_ele(self.m.get(filter=('subtree', f)).data_xml) - - device_info['network_os_hostname'] = reply.findtext('.//{%s}state/{*}system/{*}lldp/{*}system-name' % xmlns) - device_info['network_os_version'] = reply.findtext('.//{%s}state/{*}system/{*}version/{*}version-number' % xmlns) - device_info['network_os_model'] = reply.findtext('.//{%s}state/{*}system/{*}platform' % xmlns) - device_info['network_os_platform'] = 'Nokia 7x50' - return device_info - - def get_capabilities(self): - result = dict() - result['rpc'] = self.get_base_rpc() - result['network_api'] = 'netconf' - result['device_info'] = self.get_device_info() - result['server_capabilities'] = [c for c in self.m.server_capabilities] - result['client_capabilities'] = [c for c in self.m.client_capabilities] - result['session_id'] = self.m.session_id - result['device_operations'] = self.get_device_operations(result['server_capabilities']) - return json.dumps(result) - - @staticmethod - @ensure_ncclient - def guess_network_os(obj): - try: - m = manager.connect( - host=obj._play_context.remote_addr, - port=obj._play_context.port or 830, - username=obj._play_context.remote_user, - password=obj._play_context.password, - key_filename=obj.key_filename, - hostkey_verify=obj.get_option('host_key_checking'), - look_for_keys=obj.get_option('look_for_keys'), - allow_agent=obj._play_context.allow_agent, - timeout=obj.get_option('persistent_connect_timeout'), - # We need to pass in the path to the ssh_config file when guessing - # the network_os so that a jumphost is correctly used if defined - ssh_config=obj._ssh_config - ) - except SSHUnknownHostError as exc: - raise AnsibleConnectionFailure(to_native(exc)) - - guessed_os = None - for c in m.server_capabilities: - if re.search('urn:nokia.com:sros:ns:yang:sr', c): - guessed_os = 'sros' - - m.close_session() - return guessed_os diff --git a/plugins/terminal/__init__.py b/plugins/terminal/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/terminal/aireos.py b/plugins/terminal/aireos.py deleted file mode 100644 index 240b1dff3f..0000000000 --- a/plugins/terminal/aireos.py +++ /dev/null @@ -1,59 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re -import time - -from ansible.errors import AnsibleConnectionFailure -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]?[\w]*\(.+\)?[>#\$](?:\s*)$"), - re.compile(br"User:") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"% ?Bad secret"), - re.compile(br"invalid input", re.I), - re.compile(br"incorrect usage", 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: - commands = ('{"command": "' + self._connection._play_context.remote_user + '", "prompt": "Password:", "answer": "' + - self._connection._play_context.password + '"}', - '{"command": "config paging disable"}') - for cmd in commands: - self._exec_cli_command(cmd) - except AnsibleConnectionFailure: - try: - self._exec_cli_command(b'config paging disable') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') diff --git a/plugins/terminal/apconos.py b/plugins/terminal/apconos.py deleted file mode 100644 index 0cbd74f649..0000000000 --- a/plugins/terminal/apconos.py +++ /dev/null @@ -1,35 +0,0 @@ -# (C) 2017 Red Hat Inc. -# Copyright (C) 2019 APCON. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Contains terminal Plugin methods for apconos Config Module -# Apcon Networking -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br'>>\ |#\ |\$\ ') - ] - - terminal_stderr_re = [ - re.compile(br"connection timed out", re.I), - ] diff --git a/plugins/terminal/aruba.py b/plugins/terminal/aruba.py deleted file mode 100644 index 79bce7a2e1..0000000000 --- a/plugins/terminal/aruba.py +++ /dev/null @@ -1,68 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - ansi_re = [ - # check ECMA-48 Section 5.4 (Control Sequences) - re.compile(br'(\x1b\[\?1h\x1b=)'), - re.compile(br'((?:\x9b|\x1b\x5b)[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e])'), - re.compile(br'\x08.') - ] - - terminal_stdout_re = [ - re.compile(br"[\r\n]?[\w]*\(.+\)\s*[\^\*]?(?:\[.+\])? ?#(?:\s*)$"), - re.compile(br"[pP]assword:$"), - re.compile(br"(?<=\s)[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\s*#\s*$"), - re.compile(br"[\r\n]?[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$"), - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"Error:", re.M), - re.compile(br"^% \w+", re.M), - re.compile(br"% ?Bad secret"), - 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+"), - ] - - terminal_initial_prompt = b'Press any key to continue' - - terminal_initial_answer = b'\r' - - terminal_inital_prompt_newline = False - - def on_open_shell(self): - try: - self._exec_cli_command(b'no pag') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') diff --git a/plugins/terminal/ce.py b/plugins/terminal/ce.py deleted file mode 100644 index 67936e8fef..0000000000 --- a/plugins/terminal/ce.py +++ /dev/null @@ -1,60 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re - -from ansible.plugins.terminal import TerminalBase -from ansible.errors import AnsibleConnectionFailure - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br'[\r\n]?<.+>(?:\s*)$'), - re.compile(br'[\r\n]?\[.+\](?:\s*)$'), - ] - #: terminal initial prompt - #: The password needs to be changed. Change now? [Y/N]: - terminal_initial_prompt = br'Change\s*now\s*\?\s*\[Y\/N\]\s*:' - - #: terminal initial answer - #: do not change password when it is asked to change with initial connection. - terminal_initial_answer = b'N' - terminal_stderr_re = [ - re.compile(br"% ?Error: "), - re.compile(br"^% \w+", re.M), - re.compile(br"% ?Bad secret"), - 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+"), - re.compile(br"syntax error"), - re.compile(br"unknown command"), - re.compile(br"Error\[\d+\]: ", re.I), - re.compile(br"Error:", re.I) - ] - - def on_open_shell(self): - try: - self._exec_cli_command('screen-length 0 temporary') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') diff --git a/plugins/terminal/cnos.py b/plugins/terminal/cnos.py deleted file mode 100644 index 6162b84cff..0000000000 --- a/plugins/terminal/cnos.py +++ /dev/null @@ -1,83 +0,0 @@ -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Contains terminal Plugin methods for CNOS Config Module -# Lenovo Networking -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), - re.compile(br"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$"), - re.compile(br">[\r\n]?") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"% ?Bad secret"), - 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.compile(br"'[^']' +returned error code: ?\d+"), - ] - - def on_open_shell(self): - try: - for cmd in (b'\n', b'terminal length 0\n'): - self._exec_cli_command(cmd) - 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: - # Note: python-3.5 cannot combine u"" and r"" together. Thus make - # an r string and use to_text to ensure it's text - # on both py2 and py3. - 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: - msg = 'unable to elevate privilege to enable mode' - raise AnsibleConnectionFailure(msg) - - 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 b'(config' in prompt: - self._exec_cli_command(b'end') - self._exec_cli_command(b'disable') - - elif prompt.endswith(b'#'): - self._exec_cli_command(b'disable') diff --git a/plugins/terminal/edgeos.py b/plugins/terminal/edgeos.py deleted file mode 100644 index 50f5901663..0000000000 --- a/plugins/terminal/edgeos.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright: (c) 2018, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import os -import re - -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\-\.]+:\S+?[>#\$] ?$") - ] - - terminal_stderr_re = [ - re.compile(br"\n\s*command not found"), - re.compile(br"\nInvalid command"), - re.compile(br"\nCommit failed"), - re.compile(br"\n\s*Set failed"), - ] - - terminal_length = os.getenv('ANSIBLE_EDGEOS_TERMINAL_LENGTH', 10000) - - def on_open_shell(self): - try: - self._exec_cli_command('export VYATTA_PAGER=cat') - self._exec_cli_command('stty rows %s' % self.terminal_length) - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') diff --git a/plugins/terminal/edgeswitch.py b/plugins/terminal/edgeswitch.py deleted file mode 100644 index 27ac674d85..0000000000 --- a/plugins/terminal/edgeswitch.py +++ /dev/null @@ -1,87 +0,0 @@ -# -# (c) 2018 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"\(([^\(\)]+)\) [>#]$"), - re.compile(br"\(([^\(\)]+)\) \(([^\(\)]+)\)#$") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"% ?Bad secret"), - 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.compile(br"'[^']' +returned error code: ?\d+"), - re.compile(br"An invalid") - ] - - def on_open_shell(self): - return - - def on_become(self, passwd=None): - prompt = self._get_prompt() - if prompt and prompt.endswith(b'#'): - return - - cmd = {u'command': u'enable'} - if passwd: - cmd[u'prompt'] = to_text(r"[\r\n]?[Pp]assword: ?$", errors='surrogate_or_strict') - cmd[u'answer'] = passwd - try: - self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict')) - prompt = self._get_prompt() - if prompt is None or not prompt.endswith(b'#'): - raise AnsibleConnectionFailure('failed to elevate privilege to enable mode still at prompt [%s]' % prompt) - - cmd = {u'command': u'terminal length 0'} - self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict')) - prompt = self._get_prompt() - if prompt is None or not prompt.endswith(b'#'): - raise AnsibleConnectionFailure('failed to setup terminal in enable mode') - - except AnsibleConnectionFailure as e: - prompt = self._get_prompt() - raise AnsibleConnectionFailure('unable to elevate privilege to enable mode, at prompt [%s] with error: %s' % (prompt, e.message)) - - 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 b'(Config' in prompt: - self._exec_cli_command(b'end') - self._exec_cli_command(b'exit') - - elif prompt.endswith(b'#'): - self._exec_cli_command(b'exit') diff --git a/plugins/terminal/enos.py b/plugins/terminal/enos.py deleted file mode 100644 index e1b82d2562..0000000000 --- a/plugins/terminal/enos.py +++ /dev/null @@ -1,83 +0,0 @@ -# (C) 2017 Red Hat Inc. -# Copyright (C) 2017 Lenovo. -# -# GNU General Public License v3.0+ -# -# This program 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. -# -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Contains terminal Plugin methods for ENOS Config Module -# Lenovo Networking -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), - re.compile(br"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$"), - re.compile(br">[\r\n]?") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"% ?Bad secret"), - 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.compile(br"'[^']' +returned error code: ?\d+"), - ] - - def on_open_shell(self): - try: - for cmd in (b'\n', b'terminal-length 0\n'): - self._exec_cli_command(cmd) - 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: - # Note: python-3.5 cannot combine u"" and r"" together. Thus make - # an r string and use to_text to ensure it's text - # on both py2 and py3. - 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: - msg = 'unable to elevate privilege to enable mode' - raise AnsibleConnectionFailure(msg) - - 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 b'(config' in prompt: - self._exec_cli_command(b'end') - self._exec_cli_command(b'disable') - - elif prompt.endswith(b'#'): - self._exec_cli_command(b'disable') diff --git a/plugins/terminal/eric_eccli.py b/plugins/terminal/eric_eccli.py deleted file mode 100644 index d162203265..0000000000 --- a/plugins/terminal/eric_eccli.py +++ /dev/null @@ -1,59 +0,0 @@ -# -# Copyright (c) 2019 Ericsson AB. -# -# 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 . -# - - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible import constants as C -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase -from ansible.utils.display import Display -from ansible.module_utils.six import PY3 - -display = Display() - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]?\[.*\][a-zA-Z0-9_.-]*[>\#] ?$"), - re.compile(br"[\r\n]?[a-zA-Z0-9_.-]*(?:\([^\)]+\))(?:[>#]) ?$"), - re.compile(br"bash\-\d\.\d(?:[$#]) ?"), - re.compile(br"[a-zA-Z0-9_.-]*\@[a-zA-Z0-9_.-]*\[\]\:\/flash\>") - ] - - terminal_stderr_re = [ - re.compile(br"[\r\n]+syntax error: .*"), - re.compile(br"Aborted: .*"), - re.compile(br"[\r\n]+Error: .*"), - re.compile(br"[\r\n]+% Error:.*"), - re.compile(br"[\r\n]+% Invalid input.*"), - re.compile(br"[\r\n]+% Incomplete command:.*") - ] - - def on_open_shell(self): - - try: - for cmd in (b'screen-length 0', b'screen-width 512'): - self._exec_cli_command(cmd) - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') diff --git a/plugins/terminal/exos.py b/plugins/terminal/exos.py deleted file mode 100644 index 6836cdb8f4..0000000000 --- a/plugins/terminal/exos.py +++ /dev/null @@ -1,59 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n](?:! )?(?:\* )?(?:\(.*\) )?(?:Slot-\d+ )?(?:VPEX )?\S+\.\d+ (?:[>#]) ?$") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"% ?Bad secret"), - re.compile(br"[\r\n%] Bad passwords"), - 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.compile(br"'[^']' +returned error code: ?\d+"), - re.compile(br"Bad mask", re.I), - re.compile(br"% ?(\S+) ?overlaps with ?(\S+)", re.I), - re.compile(br"[%\S] ?Error: ?[\s]+", re.I), - re.compile(br"[%\S] ?Informational: ?[\s]+", re.I), - re.compile(br"%% Invalid .* at '\^' marker.", re.I), - ] - - def on_open_shell(self): - try: - self._exec_cli_command(b'disable clipaging') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') - - try: - self._exec_cli_command(b'configure cli columns 256') - except AnsibleConnectionFailure: - self._connection.queue_message('warning', 'Unable to configure cli columns, command responses may be truncated') diff --git a/plugins/terminal/icx.py b/plugins/terminal/icx.py deleted file mode 100644 index e7d55549d8..0000000000 --- a/plugins/terminal/icx.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright: (c) 2019, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re - -from ansible.plugins.terminal import TerminalBase -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -import json - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]?[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"% ?Bad secret"), - re.compile(br"[\r\n%] Bad passwords"), - 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.compile(br"'[^']' +returned error code: ?\d+"), - re.compile(br"Bad mask", re.I), - re.compile(br"% ?(\S+) ?overlaps with ?(\S+)", re.I), - re.compile(br"[%\S] ?Error: ?[\s]+", re.I), - re.compile(br"[%\S] ?Informational: ?[\s]+", re.I), - re.compile(br"Command authorization failed"), - re.compile(br"Error - *"), - re.compile(br"Error - Incorrect username or password."), - re.compile(br"Invalid input"), - re.compile(br"Already a http operation is in progress"), - re.compile(br"Flash access in progress. Please try later"), - re.compile(br"Error: .*"), - re.compile(br"^Error: .*", re.I), - re.compile(br"^Ambiguous input"), - re.compile(br"Errno") - ] - - def on_open_shell(self): - pass - - def __del__(self): - try: - self.close() - 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'} - cmd[u'prompt'] = to_text(r"[\r\n](?:Local_)?[Pp]assword: ?$", errors='surrogate_or_strict') - cmd[u'answer'] = passwd - cmd[u'prompt_retry_check'] = True - try: - self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict')) - prompt = self._get_prompt() - if prompt is None or not prompt.endswith(b'#'): - raise AnsibleConnectionFailure('failed to elevate privilege to enable mode still at prompt [%s]' % prompt) - except AnsibleConnectionFailure as e: - prompt = self._get_prompt() - raise AnsibleConnectionFailure('unable to elevate privilege to enable mode, at prompt [%s] with error: %s' % (prompt, e.message)) - - def on_unbecome(self): - prompt = self._get_prompt() - if prompt is None: - return - - if b'(config' in prompt: - self._exec_cli_command(b'exit') - - elif prompt.endswith(b'#'): - self._exec_cli_command(b'exit') diff --git a/plugins/terminal/ironware.py b/plugins/terminal/ironware.py deleted file mode 100644 index 3651cbd114..0000000000 --- a/plugins/terminal/ironware.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re -import json - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]?(?:\w+@)?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$") - ] - - terminal_stderr_re = [ - re.compile(br"[\r\n]Error - "), - re.compile(br"[\r\n](?:incomplete|ambiguous|unrecognised|invalid) (?:command|input)", re.I) - ] - - def on_open_shell(self): - self.disable_pager() - - def disable_pager(self): - cmd = {u'command': u'terminal length 0'} - try: - self._exec_cli_command(u'terminal length 0') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to disable terminal pager') - - def on_become(self, passwd=None): - if self._get_prompt().strip().endswith(b'#'): - return - - cmd = {u'command': u'enable'} - if passwd: - # Note: python-3.5 cannot combine u"" and r"" together. Thus make - # an r string and use to_text to ensure it's text on both py2 and py3. - 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 b'(config' in prompt: - self._exec_cli_command(b'end') - self._exec_cli_command(b'exit') - - elif prompt.endswith(b'#'): - self._exec_cli_command(b'exit') diff --git a/plugins/terminal/netvisor.py b/plugins/terminal/netvisor.py deleted file mode 100644 index 27dffb5947..0000000000 --- a/plugins/terminal/netvisor.py +++ /dev/null @@ -1,39 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br">.*[\r\n]?(.*)") - - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error: (?!\bdoes not exist\b)(?!\balready exists\b)") - ] diff --git a/plugins/terminal/nos.py b/plugins/terminal/nos.py deleted file mode 100644 index 245189468f..0000000000 --- a/plugins/terminal/nos.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"([\r\n]|(\x1b\[\?7h))[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - # re.compile(br"^% \w+", re.M), - re.compile(br"% ?Bad secret"), - re.compile(br"[\r\n%] Bad passwords"), - 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.compile(br"'[^']' +returned error code: ?\d+"), - re.compile(br"Bad mask", re.I), - re.compile(br"% ?(\S+) ?overlaps with ?(\S+)", re.I), - re.compile(br"[%\S] ?Informational: ?[\s]+", re.I), - re.compile(br"syntax error: unknown argument.", re.I) - ] - - def on_open_shell(self): - try: - self._exec_cli_command(u'terminal length 0') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') diff --git a/plugins/terminal/onyx.py b/plugins/terminal/onyx.py deleted file mode 100644 index 52d630b9fd..0000000000 --- a/plugins/terminal/onyx.py +++ /dev/null @@ -1,80 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"(?P(.*)( > | # )\Z)"), - ] - - terminal_stderr_re = [ - re.compile(br"\A%|\r\n%|\n%"), - ] - - init_commands = [b'no cli session paging enable', ] - - def on_open_shell(self): - try: - for cmd in self.init_commands: - self._exec_cli_command(cmd) - 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: - # Note: python-3.5 cannot combine u"" and r"" together. Thus make - # an r string and use to_text to ensure it's text on both py2 and - # py3. - 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 b'(config' in prompt: - self._exec_cli_command(b'exit') - self._exec_cli_command(b'disable') - - elif prompt.endswith(b'#'): - self._exec_cli_command(b'disable') diff --git a/plugins/terminal/routeros.py b/plugins/terminal/routeros.py deleted file mode 100644 index 78996f28aa..0000000000 --- a/plugins/terminal/routeros.py +++ /dev/null @@ -1,69 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase -from ansible.utils.display import Display - -display = Display() - - -class TerminalModule(TerminalBase): - - ansi_re = [ - # check ECMA-48 Section 5.4 (Control Sequences) - re.compile(br'(\x1b\[\?1h\x1b=)'), - re.compile(br'((?:\x9b|\x1b\x5b)[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e])'), - re.compile(br'\x08.') - ] - - terminal_initial_prompt = [ - br'\x1bZ', - ] - - terminal_initial_answer = b'\x1b/Z' - - terminal_stdout_re = [ - re.compile(br"\x1b<"), - re.compile(br"\[[\w\.]+\@[\w\s\-\.]+\] ?> ?$"), - re.compile(br"Please press \"Enter\" to continue!"), - re.compile(br"Do you want to see the software license\? \[Y\/n\]: ?"), - ] - - terminal_stderr_re = [ - re.compile(br"\nbad command name"), - re.compile(br"\nno such item"), - re.compile(br"\ninvalid value for"), - ] - - def on_open_shell(self): - prompt = self._get_prompt() - try: - if prompt.strip().endswith(b':'): - self._exec_cli_command(b' ') - if prompt.strip().endswith(b'!'): - self._exec_cli_command(b'\n') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to bypass license prompt') diff --git a/plugins/terminal/slxos.py b/plugins/terminal/slxos.py deleted file mode 100644 index 245189468f..0000000000 --- a/plugins/terminal/slxos.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"([\r\n]|(\x1b\[\?7h))[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$") - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - # re.compile(br"^% \w+", re.M), - re.compile(br"% ?Bad secret"), - re.compile(br"[\r\n%] Bad passwords"), - 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.compile(br"'[^']' +returned error code: ?\d+"), - re.compile(br"Bad mask", re.I), - re.compile(br"% ?(\S+) ?overlaps with ?(\S+)", re.I), - re.compile(br"[%\S] ?Informational: ?[\s]+", re.I), - re.compile(br"syntax error: unknown argument.", re.I) - ] - - def on_open_shell(self): - try: - self._exec_cli_command(u'terminal length 0') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') diff --git a/plugins/terminal/sros.py b/plugins/terminal/sros.py deleted file mode 100644 index 77085a383e..0000000000 --- a/plugins/terminal/sros.py +++ /dev/null @@ -1,43 +0,0 @@ -# -# (c) 2016 Red Hat 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re - -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:"), - ] - - def on_open_shell(self): - try: - self._exec_cli_command(b'environment no more') - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure('unable to set terminal parameters') diff --git a/plugins/terminal/voss.py b/plugins/terminal/voss.py deleted file mode 100644 index f540be9b4e..0000000000 --- a/plugins/terminal/voss.py +++ /dev/null @@ -1,89 +0,0 @@ -# -# (c) 2018 Extreme Networks 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 . -# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import json -import re - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text, to_bytes -from ansible.plugins.terminal import TerminalBase - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]+[^\s#>]+(?:[>#])$", re.M) - ] - - terminal_stderr_re = [ - re.compile(br"% ?Error"), - re.compile(br"% ?Bad secret"), - re.compile(br"[\r\n%] Bad passwords"), - 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.compile(br"'[^']' +returned error code: ?\d+"), - re.compile(br"Discontiguous Subnet Mask"), - re.compile(br"Conflicting IP address"), - re.compile(br"[\r\n]Error: ?[\S]+"), - re.compile(br"[%\S] ?Informational: ?[\s]+", re.I), - re.compile(br"Command authorization failed") - ] - - def on_open_shell(self): - try: - self._exec_cli_command(u'terminal more disable') - 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: - # Note: python-3.5 cannot combine u"" and r"" together. Thus make - # an r string and use to_text to ensure it's text on both py2 and py3. - cmd[u'prompt'] = to_text(r"[\r\n](?:Local_)?[Pp]assword: ?$", errors='surrogate_or_strict') - cmd[u'answer'] = passwd - cmd[u'prompt_retry_check'] = True - try: - self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict')) - prompt = self._get_prompt() - if prompt is None or not prompt.endswith(b'#'): - raise AnsibleConnectionFailure('failed to elevate privilege to enable mode still at prompt [%s]' % prompt) - except AnsibleConnectionFailure as e: - prompt = self._get_prompt() - raise AnsibleConnectionFailure('unable to elevate privilege to enable mode, at prompt [%s] with error: %s' % (prompt, e.message)) - - 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.endswith(b')#'): - self._exec_cli_command(b'end') - self._exec_cli_command(b'disable') - - elif prompt.endswith(b'#'): - self._exec_cli_command(b'disable') diff --git a/tests/integration/targets/ce_is_is_instance/defaults/main.yaml b/tests/integration/targets/ce_is_is_instance/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_is_is_instance/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_is_is_instance/meta/main.yml b/tests/integration/targets/ce_is_is_instance/meta/main.yml deleted file mode 100644 index 8b13789179..0000000000 --- a/tests/integration/targets/ce_is_is_instance/meta/main.yml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/integration/targets/ce_is_is_instance/tasks/main.yaml b/tests/integration/targets/ce_is_is_instance/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_is_is_instance/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_is_is_instance/tasks/netconf.yaml b/tests/integration/targets/ce_is_is_instance/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_is_is_instance/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_is_is_instance/tests/netconf/ce_is_is_instance.yaml b/tests/integration/targets/ce_is_is_instance/tests/netconf/ce_is_is_instance.yaml deleted file mode 100644 index d7c5a7c6e1..0000000000 --- a/tests/integration/targets/ce_is_is_instance/tests/netconf/ce_is_is_instance.yaml +++ /dev/null @@ -1,85 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_instance merged integration tests on connection={{ ansible_connection }}" - -- block: - - name: berfore merged, there should be no isis 100. - ce_is_is_instance: &delete - instance_id: 100 - state: absent - - - name: Merge the provided configuration with the exisiting running configuration - ce_is_is_instance: &merged - instance_id: 100 - vpn_name: '__public__' - register: result - - - name: change ansible_connection to network_cli - ce_netconf: - rpc: get - cfg_xml: " - - - - - - - - - " - register: result_xml - - - name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - - "'100' in result_xml.end_state.result" - - "'__public__' in result_xml.end_state.result" - - - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_instance: *merged - register: result - - - name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - - - name: delete the provided configuration with the exisiting running configuration - ce_is_is_instance: *delete - register: result - - - name: change ansible_connection to network_cli - ce_netconf: - rpc: get - cfg_xml: " - - - - - - - - - " - register: result_xml - - - name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - - "'100' not in result_xml.end_state.result" - - "'__public__' not in result_xml.end_state.result" - - - name: delete the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_instance: *delete - register: result - - - name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - - -- debug: - msg: "END ce_is_is_instance merged integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_interface/defaults/main.yaml b/tests/integration/targets/ce_is_is_interface/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_is_is_interface/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_is_is_interface/meta/main.yml b/tests/integration/targets/ce_is_is_interface/meta/main.yml deleted file mode 100644 index 8b13789179..0000000000 --- a/tests/integration/targets/ce_is_is_interface/meta/main.yml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/integration/targets/ce_is_is_interface/tasks/main.yaml b/tests/integration/targets/ce_is_is_interface/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_is_is_interface/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_is_is_interface/tasks/netconf.yaml b/tests/integration/targets/ce_is_is_interface/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_is_is_interface/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_is_is_interface/tests/netconf/ce_is_is_interface.yaml b/tests/integration/targets/ce_is_is_interface/tests/netconf/ce_is_is_interface.yaml deleted file mode 100644 index 75afbbe899..0000000000 --- a/tests/integration/targets/ce_is_is_interface/tests/netconf/ce_is_is_interface.yaml +++ /dev/null @@ -1,133 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_interface merged integration tests on connection={{ ansible_connection }}" - -- block: - - - name: Merge the provided configuration with the exisiting running configuration - ce_is_is_interface: &delete - instance_id: 100 - ifname: 10GE1/0/1 - leveltype: level_1 - level1dispriority: 10 - silentenable: true - silentcost: true - typep2penable: true - snpacheck: true - p2pnegotiationmode: 2_way - p2ppeeripignore: true - ppposicpcheckenable: true - level2cost: 10 - state: absent - register: result - - - name: Merge the provided configuration with the exisiting running configuration - ce_is_is_interface: &merged - instance_id: 100 - ifname: 10GE1/0/1 - leveltype: level_1 - level1dispriority: 10 - silentenable: true - silentcost: true - typep2penable: true - snpacheck: true - p2pnegotiationmode: 2_way - p2ppeeripignore: true - ppposicpcheckenable: true - level2cost: 10 - register: result - - - name: use ce_netconf to get configuration - ce_netconf: - rpc: get - cfg_xml: " - - - - 100 - - - - - - - - " - register: result_xml - - - name: Assert the configuration is reflected on host - assert: - that: - - "'10GE1/0/1' in result_xml.end_state.result" - - "'level_1' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'2_way' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - - - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_interface: *merged - register: result - - - name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - - - name: delete the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_interface: *delete - register: result - - - name: use ce_netconf to get configuration - ce_netconf: - rpc: get - cfg_xml: " - - - - 100 - - - - - - - - " - register: result_xml - - - name: Assert the configuration is reflected on host - assert: - that: - - "'10GE1/0/1' not in result_xml.end_state.result" - - "'level_1' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'2_way' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" - - - name: delete the provided configuration with the existing running configuration (REPEAT) - ce_is_is_interface: *delete - register: result - - - name: Assert that the previous task was REPEAT - assert: - that: - - "result['changed'] == false" - -- debug: - msg: "END ce_is_is_interface merged integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/defaults/main.yaml b/tests/integration/targets/ce_is_is_view/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_is_is_view/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_is_is_view/tasks/main.yaml b/tests/integration/targets/ce_is_is_view/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_is_is_view/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_is_is_view/tasks/netconf.yaml b/tests/integration/targets/ce_is_is_view/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_is_is_view/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/cleanup.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/cleanup.yaml deleted file mode 100644 index 03c0f0ee94..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/cleanup.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -- debug: - msg: "CLEANUP ce_is_is_view, deleted integration tests on connection={{ ansible_connection }}" - -- name: Get lacp config by ce_netconf. - ce_netconf: - rpc: get - cfg_xml: " - - - - 100 - _public_ - ISIS - - - - " - -- debug: - msg: "END CLEANUP ce_is_is_view, deleted integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/setup.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/setup.yaml deleted file mode 100644 index 92ec370c28..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/setup.yaml +++ /dev/null @@ -1,22 +0,0 @@ ---- -- debug: - msg: "SETUP ce_is_is_view integration tests on connection={{ ansible_connection }}" -# create ISIS 100 - -- name: Get lacp config by ce_netconf. - ce_netconf: - rpc: get - cfg_xml: " - - - - 100 - _public_ - ISIS - - - - " - -- debug: - msg: "END SETUP ce_is_is_view integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_is_is_view_entity .yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_is_is_view_entity .yaml deleted file mode 100644 index 1c81c2ca7b..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_is_is_view_entity .yaml +++ /dev/null @@ -1,78 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view entity presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - netentity: isis_net_entity - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'isis_net_entity' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - netentity: isis_net_entity - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' not in result_xml.end_state.result" - - "''isis_net_entity' not in result_xml.end_state.result" - # after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view pentity resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_bfd.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_bfd.yaml deleted file mode 100644 index 6aa200f6e8..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_bfd.yaml +++ /dev/null @@ -1,92 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view EXPORTROUTE route policy presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - bfd_min_rx: 100 - bfd_min_tx: 100 - bfd_multiplier_num: 10 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'100' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - defaultmode: always - cost: 10 - mode_tag: 10 - level_type: level_1 - avoid_learning: true - mode_routepolicyname: routepolicy_name - tag: 100 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' not in result_xml.end_state.result" - - "'100' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" -# after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view EXPORTROUTE route policy resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_export.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_export.yaml deleted file mode 100644 index f83a07c2cf..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_export.yaml +++ /dev/null @@ -1,96 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view EXPORTROUTE route policy presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - export_protocol: ospf - export_policytype: aclNumOrName - export_processid: 100 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'ospf' in result_xml.end_state.result" - - "'100' in result_xml.end_state.result" - - "'level_1' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - defaultmode: always - cost: 10 - mode_tag: 10 - level_type: level_1 - avoid_learning: true - mode_routepolicyname: routepolicy_name - tag: 100 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'ospf' not in result_xml.end_state.result" - - "'100' not in result_xml.end_state.result" - - "'level_1' in result_xml.end_state.result" -# after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view EXPORTROUTE route policy resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_import.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_import.yaml deleted file mode 100644 index c5b4e60df4..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_import.yaml +++ /dev/null @@ -1,124 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view import route policy presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - protocol: ospf - processid: 100 - cost_type: external - import_cost: 10 - import_tag: 10 - import_route_policy: routepolicy_name - impotr_leveltype: level_1 - inheritcost: true - permitibgp: true - tag: 100 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - - - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'ospf' in result_xml.end_state.result" - - "'100' in result_xml.end_state.result" - - "'external' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - - "'level_1' in result_xml.end_state.result" - - "'routepolicy_name' in result_xml.end_state.result" - - "'level_1' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - defaultmode: always - cost: 10 - mode_tag: 10 - level_type: level_1 - avoid_learning: true - mode_routepolicyname: routepolicy_name - tag: 100 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'ospf' not in result_xml.end_state.result" - - "'100' not in result_xml.end_state.result" - - "'external' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" - - "'level_1' not in result_xml.end_state.result" - - "'routepolicy_name' not in result_xml.end_state.result" - - "'level_1' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" -# after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view import route policy resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_l1tol2.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_l1tol2.yaml deleted file mode 100644 index f12b5034bf..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_l1tol2.yaml +++ /dev/null @@ -1,109 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view import route policy presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - allow_filter: true - allow_up_down: true - ip_prefix_name: prefix_name - aclnum_or_name: 3001 - penetration_direct: level1-level2 - import_routepolicy_name: routepolicy_name - tag: 100 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'routepolicy_name' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'3001' in result_xml.end_state.result" - - "'prefix_name' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - allow_filter: true - allow_up_down: true - ip_prefix_name: prefix_name - aclnum_or_name: 3001 - penetration_direct: level1-level2 - import_routepolicy_name: routepolicy_name - tag: 100 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'routepolicy_name' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'3001' not in result_xml.end_state.result" - - "'prefix_name' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - # after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view import route policy resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_l2tol1.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_l2tol1.yaml deleted file mode 100644 index 3af5111fb4..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_l2tol1.yaml +++ /dev/null @@ -1,109 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view import route policy presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - penetration_direct: level2-level1 - allow_filter: true - allow_up_down: true - ip_prefix_name: prefix_name - aclnum_or_name: 3001 - import_routepolicy_name: routepolicy_name - tag: 100 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'routepolicy_name' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'3001' in result_xml.end_state.result" - - "'prefix_name' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - penetration_direct: level2-level1 - allow_filter: true - allow_up_down: true - ip_prefix_name: prefix_name - aclnum_or_name: 3001 - import_routepolicy_name: routepolicy_name - tag: 100 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'routepolicy_name' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'3001' not in result_xml.end_state.result" - - "'prefix_name' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - # after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view import route policy resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_max_load.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_max_load.yaml deleted file mode 100644 index 0629d3415c..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_max_load.yaml +++ /dev/null @@ -1,80 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view maxLoadBalancing presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - max_load: 30 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'30' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - max_load: 30 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' not in result_xml.end_state.result" - - "'30' not in result_xml.end_state.result" - # after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view maxLoadBalancing resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_preferences.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_preferences.yaml deleted file mode 100644 index 97d232feca..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_preferences.yaml +++ /dev/null @@ -1,87 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view preferences presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - preference_value: 100 - route_policy_name: route - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'100' in result_xml.end_state.result" - - "'route' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - preference_value: 100 - route_policy_name: route - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' not in result_xml.end_state.result" - - "''100' not in result_xml.end_state.result" - - "''route' not in result_xml.end_state.result" - # after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view Preference resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_view_basic.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_view_basic.yaml deleted file mode 100644 index ca95d0e059..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_isis_view_basic.yaml +++ /dev/null @@ -1,115 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - description: ISIS - islevel: level_1 - coststyle: narrow - relaxSpfLimit: true - stdlevel1cost: 60 - stdlevel2cost: 60 - stdbandwidth: 100 - autocostenable: true - autocostenablecompatible: true - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'_public_' in result_xml.end_state.result" - - "'ISIS' in result_xml.end_state.result" - - "'level_1' in result_xml.end_state.result" - - "'narrow' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'60' in result_xml.end_state.result" - - "'60' in result_xml.end_state.result" - - "'100' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - description: ISIS - islevel: level_1 - coststyle: narrow - relaxSpfLimit: true - stdlevel1cost: 60 - stdlevel2cost: 60 - stdbandwidth: 100 - autocostenable: true - autocostenablecompatible: true - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' not in result_xml.end_state.result" - - "'_public_' not in result_xml.end_state.result" - - "'ISIS' not in result_xml.end_state.result" - - "'level_1' not in result_xml.end_state.result" - - "'narrow' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'60' not in result_xml.end_state.result" - - "'60' not in result_xml.end_state.result" - - "'100' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" -# after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view presentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_issi_default.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_issi_default.yaml deleted file mode 100644 index 1059054dce..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_ce_issi_default.yaml +++ /dev/null @@ -1,111 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view import route policy presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - defaultmode: always - cost: 10 - mode_tag: 10 - level_type: level_1 - avoid_learning: true - mode_routepolicyname: routepolicy_name - tag: 100 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' in result_xml.end_state.result" - - "'always' in result_xml.end_state.result" - - "'routepolicy_name' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - - "'10' in result_xml.end_state.result" - - "'level_1' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - defaultmode: always - cost: 10 - mode_tag: 10 - level_type: level_1 - avoid_learning: true - mode_routepolicyname: routepolicy_name - tag: 100 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'100' not in result_xml.end_state.result" - - "'always' not in result_xml.end_state.result" - - "'routepolicy_name' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" - - "'10' not in result_xml.end_state.result" - - "'level_1' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" -# after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view import route policy resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_isis_filter_import.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_isis_filter_import.yaml deleted file mode 100644 index 7a97d982d6..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_isis_filter_import.yaml +++ /dev/null @@ -1,97 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view EXPORTROUTE route policy presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - import_aclnumorname: 301 - import_ipprefix: ipprefix - import_routepolicyname: routepolicyname - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'3001' in result_xml.end_state.result" - - "'ipprefix' in result_xml.end_state.result" - - "'routepolicyname' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - defaultmode: always - cost: 10 - mode_tag: 10 - level_type: level_1 - avoid_learning: true - mode_routepolicyname: routepolicy_name - tag: 100 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'3001' not in result_xml.end_state.result" - - "'ipprefix' not in result_xml.end_state.result" - - "'routepolicyname' not in result_xml.end_state.result" - -# after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view EXPORTROUTE route policy resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_is_is_view/tests/netconf/test_isis_next_hop.yaml b/tests/integration/targets/ce_is_is_view/tests/netconf/test_isis_next_hop.yaml deleted file mode 100644 index f18eeaf844..0000000000 --- a/tests/integration/targets/ce_is_is_view/tests/netconf/test_isis_next_hop.yaml +++ /dev/null @@ -1,87 +0,0 @@ ---- -- debug: - msg: "START ce_is_is_view next hop presented integration tests on connection={{ ansible_connection }}" -- include_tasks: setup.yaml - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &present - instance_id: 100 - ip_address: 1.1.1.1 - weight: 100 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 100 - - - afIpv4 - 0 - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'1.1.1.1' in result_xml.end_state.result" - - "'100' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_is_is_view: &absent - instance_id: 100 - ip_address: 1.1.1.1 - weight: 100 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_is_is_view: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'1.1.1.1' not in result_xml.end_state.result" - - "'100' not in result_xml.end_state.result" - # after present, isis 100 should be deleted -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_is_is_view next hop resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_lacp/defaults/main.yaml b/tests/integration/targets/ce_lacp/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_lacp/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_lacp/tasks/main.yaml b/tests/integration/targets/ce_lacp/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_lacp/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_lacp/tasks/netconf.yaml b/tests/integration/targets/ce_lacp/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_lacp/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_lacp/tests/netconf/absent.yaml b/tests/integration/targets/ce_lacp/tests/netconf/absent.yaml deleted file mode 100644 index 2c32e574d4..0000000000 --- a/tests/integration/targets/ce_lacp/tests/netconf/absent.yaml +++ /dev/null @@ -1,95 +0,0 @@ ---- -- debug: - msg: "START ce_lacp merged integration tests on connection={{ ansible_connection }}" -# befor removing, it should be merged -- include_tasks: merge.yaml - -- name: Merge the provided configuration with the exisiting running configuration - ce_lacp: &absent - mode: Dynamic - trunk_id: 10 - preempt_enable: True - state_flapping: True - port_id_extension_enable: True - unexpected_mac_disable: True - system_id: 1111-2222-3333 - timeout_type: Fast - fast_timeout: 12 - mixed_rate_link_enable: True - preempt_delay: 12 - collector_delay: 12 - max_active_linknumber: 2 - select: Prority - priority: 23 - global_priority: 123 - state: absent - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get lacp config by ce_netconf. - ce_netconf: - rpc: get - cfg_xml: " - - - - Eth-Trunk10 - - - - - - - - - - - - - - - - - - " - register: result_ifs_merged - -- name: Get lacp config by ce_netconf. - ce_netconf: - rpc: get - cfg_xml: " - - - - - - " - register: result_global_merged - - -- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) - ce_lacp: *absent - register: result_re_merged - -- name: Assert that the previous task was idempotent, some become ot default values, others depend on devices. - assert: - that: - - "result_re_merged.changed == false" - - "'false' == result_ifs_merged.end_state.result" - - "'Slow' == result_ifs_merged.end_state.result" - - "'90' == result_ifs_merged.end_state.result" - - "'Prority' == result_ifs_merged.end_state.result" - - "'30' == result_ifs_merged.end_state.result" - - "'0' in result_ifs_merged.end_state.result" - - "'false' in result_ifs_merged.end_state.result" - - "'false' in result_ifs_merged.end_state.result" - - "'false' in result_ifs_merged.end_state.result" - - "'false' in result_ifs_merged.end_state.result" - - "'32768' in result_global_merged.end_state.result" - -- debug: - msg: "END ce_lacp merged integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_lacp/tests/netconf/delete.yaml b/tests/integration/targets/ce_lacp/tests/netconf/delete.yaml deleted file mode 100644 index a3ce81723a..0000000000 --- a/tests/integration/targets/ce_lacp/tests/netconf/delete.yaml +++ /dev/null @@ -1,32 +0,0 @@ ---- -- debug: - msg: "START ce_lacp deleted integration tests on connection={{ ansible_connection }}" - -- name: Merge the provided configuration with the exisiting running configuration - ce_lacp: - mode: Dynamic - trunk_id: 10 - preempt_enable: True - state_flapping: True - port_id_extension_enable: True - unexpected_mac_disable: True - system_id: 1111-2222-3333 - timeout_type: Fast - fast_timeout: 12 - mixed_rate_link_enable: True - preempt_delay: 12 - collector_delay: 12 - max_active_linknumber: 2 - select: Prority - priority: 23 - global_priority: 123 - state: absent - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- debug: - msg: "END ce_lacp deleted integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_lacp/tests/netconf/merge.yaml b/tests/integration/targets/ce_lacp/tests/netconf/merge.yaml deleted file mode 100644 index eef3956eeb..0000000000 --- a/tests/integration/targets/ce_lacp/tests/netconf/merge.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -- debug: - msg: "START ce_lacp merged integration tests on connection={{ ansible_connection }}" - -- name: Merge the provided configuration with the exisiting running configuration - ce_lacp: - mode: Dynamic - trunk_id: 10 - preempt_enable: True - state_flapping: True - port_id_extension_enable: True - unexpected_mac_disable: True - system_id: 1111-2222-3333 - timeout_type: Fast - fast_timeout: 12 - mixed_rate_link_enable: True - preempt_delay: 12 - collector_delay: 12 - max_active_linknumber: 2 - select: Prority - priority: 23 - global_priority: 123 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- debug: - msg: "END ce_lacp merged integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_lacp/tests/netconf/present.yaml b/tests/integration/targets/ce_lacp/tests/netconf/present.yaml deleted file mode 100644 index ee696c9e1c..0000000000 --- a/tests/integration/targets/ce_lacp/tests/netconf/present.yaml +++ /dev/null @@ -1,103 +0,0 @@ ---- -- debug: - msg: "START ce_lacp presented integration tests on connection={{ ansible_connection }}" - -- name: present the provided configuration with the exisiting running configuration - ce_lacp: &present - mode: Dynamic - trunk_id: 10 - preempt_enable: True - state_flapping: True - port_id_extension_enable: True - unexpected_mac_disable: True - system_id: 1111-2222-3333 - timeout_type: Fast - fast_timeout: 12 - mixed_rate_link_enable: True - preempt_delay: 12 - collector_delay: 12 - max_active_linknumber: 2 - select: Prority - priority: 23 - global_priority: 123 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get lacp config by ce_netconf. - ce_netconf: - rpc: get - cfg_xml: " - - - - Eth-Trunk10 - - - - - - - - - - - - - - - - - - - " - register: result_ifs_presentd - -- name: Get global lacp config by ce_netconf. - ce_netconf: - rpc: get - cfg_xml: " - - - - - - - - - - " - register: result_global_presentd - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_lacp: *present - register: result_re_presentd - -- name: Assert that the previous task was idempotent - assert: - that: - - "result_re_presentd.changed == false" - - "'Dynamic' == result_ifs_presentd.end_state.result" - - "'true' == result_ifs_presentd.end_state.result" - - "'Fast' == result_ifs_presentd.end_state.result" - - "'12' == result_ifs_presentd.end_state.result" - - "'Prority' == result_ifs_presentd.end_state.result" - - "'12' == result_ifs_presentd.end_state.result" - - "'2' == result_ifs_presentd.end_state.result" - - "'12' in result_ifs_presentd.end_state.result" - - "'true' in result_ifs_presentd.end_state.result" - - "'true' in result_ifs_presentd.end_state.result" - - "'true' in result_ifs_presentd.end_state.result" - - "'true' in result_ifs_presentd.end_state.result" - - "'true' in result_ifs_presentd.end_state.result" - - "'1111-2222-3333' in result_global_presentd.end_state.result" - - "'123' in result_global_presentd.end_state.result" - -# after present, it should be deleted -- include_tasks: delete.yaml -- debug: - msg: "END ce_lacp presentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_lldp/defaults/main.yaml b/tests/integration/targets/ce_lldp/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_lldp/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_lldp/meta/main.yml b/tests/integration/targets/ce_lldp/meta/main.yml deleted file mode 100644 index 8b13789179..0000000000 --- a/tests/integration/targets/ce_lldp/meta/main.yml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/integration/targets/ce_lldp/tasks/main.yaml b/tests/integration/targets/ce_lldp/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_lldp/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_lldp/tasks/netconf.yaml b/tests/integration/targets/ce_lldp/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_lldp/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_lldp/tests/netconf/absent.yaml b/tests/integration/targets/ce_lldp/tests/netconf/absent.yaml deleted file mode 100644 index 1a14890677..0000000000 --- a/tests/integration/targets/ce_lldp/tests/netconf/absent.yaml +++ /dev/null @@ -1,108 +0,0 @@ ---- -- debug: - msg: "START ce_lldp absent integration tests on connection={{ ansible_connection }}" - -- block: - - - name: present the provided configuration befor absent - ce_lldp: - lldpenable: enabled - mdnstatus: rxOnly - interval: 35 - hold_multiplier: 5 - restart_delay: 3 - transmit_delay: 5 - notification_interval: 6 - fast_count: 5 - mdn_notification_interval: 10.1.1.1 - management_address: 10.10.10.1 - bind_name: vlanif100 - register: result - - - name: change ansible_connection to network_cli - set_fact: - ansible_connection: network_cli - - - name: display lldp - ce_command: - commands: - - display current-configuration | include lldp - register: result_display - - - name: change ansible_connection to netconf - set_fact: - ansible_connection: netconf - -# There should be some configuration(LLDP) on host befor absent - - name: Assert the configuration is reflected on host - assert: - that: - - "'lldp enable' in result_display.stdout[0]" - - "'undo lldp mdn disable' in result_display.stdout[0]" - - "'lldp transmit interval 35' in result_display.stdout[0]" - - "'lldp transmit multiplier 5' in result_display.stdout[0]" - - "'lldp restart 3' in result_display.stdout[0]" - - "'lldp transmit delay 5' in result_display.stdout[0]" - - "'lldp fast-count 5' in result_display.stdout[0]" - - "'lldp management-address 10.10.10.1' in result_display.stdout[0]" - - "'lldp mdn trap-interval 6' in result_display.stdout[0]" - - "'lldp trap-interval 6' in result_display.stdout[0]" - - "'lldp management-address bind interface vlanif100' in result_display.stdout[0]" - - - name: absent the provided configuration with the exisiting running configuration - ce_lldp: &absent - lldpenable: enabled - mdnstatus: rxOnly - interval: 35 - hold_multiplier: 5 - restart_delay: 3 - transmit_delay: 5 - notification_interval: 6 - fast_count: 5 - mdn_notification_interval: 10.1.1.1 - management_address: 10.10.10.1 - bind_name: vlanif100 - state: absent - register: result - - - name: change ansible_connection to network_cli - set_fact: - ansible_connection: network_cli - - - name: display lldp - ce_command: - commands: - - display current-configuration | include lldp - register: result_display - - - name: change ansible_connection to netconf - set_fact: - ansible_connection: netconf - - - name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - - "'lldp enable' not in result_display.stdout[0]" - - "'undo lldp mdn disable' not in result_display.stdout[0]" - - "'lldp transmit interval 35' not in result_display.stdout[0]" - - "'lldp transmit multiplier 5' not in result_display.stdout[0]" - - "'lldp restart 3' not in result_display.stdout[0]" - - "'lldp transmit delay 5' not in result_display.stdout[0]" - - "'lldp fast-count 5' not in result_display.stdout[0]" - - "'lldp management-address 10.10.10.1' not in result_display.stdout[0]" - - "'lldp mdn trap-interval 6' not in result_display.stdout[0]" - - "'lldp trap-interval 6' not in result_display.stdout[0]" - - "'lldp management-address bind interface vlanif100' not in result_display.stdout[0]" - - - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) - ce_lldp: *absent - register: result - - - name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - -- debug: - msg: "END ce_lldp absent integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_lldp/tests/netconf/clean.yaml b/tests/integration/targets/ce_lldp/tests/netconf/clean.yaml deleted file mode 100644 index 8e12f61ea2..0000000000 --- a/tests/integration/targets/ce_lldp/tests/netconf/clean.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -- debug: - msg: "Start ce_lldp deleted remove interface config ansible_connection={{ ansible_connection }}" - -- name: change ansible_connection to network_cli - set_fact: - ansible_connection: network_cli -# After the global LLDP function is disabled, all LLDP configuration restore defaults except the LLDP alarm function. -- name: display lldp - ce_command: - commands: - - undo lldp enable - - lldp mdn disable - -- name: change ansible_connection to netconf - set_fact: - ansible_connection: netconf - -- debug: - msg: "End ce_lldp deleted remove interface config ansible_connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_lldp/tests/netconf/present.yaml b/tests/integration/targets/ce_lldp/tests/netconf/present.yaml deleted file mode 100644 index f523d32dd7..0000000000 --- a/tests/integration/targets/ce_lldp/tests/netconf/present.yaml +++ /dev/null @@ -1,66 +0,0 @@ ---- -- debug: - msg: "START ce_lldp merged integration tests on connection={{ ansible_connection }}" - -- block: - - - include_tasks: cleanup.yaml - - - name: Merge the provided configuration with the exisiting running configuration - ce_lldp: &merged - lldpenable: enabled - mdnstatus: rxOnly - interval: 35 - hold_multiplier: 5 - restart_delay: 3 - transmit_delay: 5 - notification_interval: 6 - fast_count: 5 - mdn_notification_interval: 10.1.1.1 - management_address: 10.10.10.1 - bind_name: vlanif100 - register: result - - - name: change ansible_connection to network_cli - set_fact: - ansible_connection: network_cli - - - name: display lldp - ce_command: - commands: - - display current-configuration | include lldp - register: result_display - - - name: change ansible_connection to netconf - set_fact: - ansible_connection: netconf - - - name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - - "'lldp enable' in result_display.stdout[0]" - - "'undo lldp mdn disable' in result_display.stdout[0]" - - "'lldp transmit interval 35' in result_display.stdout[0]" - - "'lldp transmit multiplier 5' in result_display.stdout[0]" - - "'lldp restart 3' in result_display.stdout[0]" - - "'lldp transmit delay 5' in result_display.stdout[0]" - - "'lldp fast-count 5' in result_display.stdout[0]" - - "'lldp management-address 10.10.10.1' in result_display.stdout[0]" - - "'lldp mdn trap-interval 6' in result_display.stdout[0]" - - "'lldp trap-interval 6' in result_display.stdout[0]" - - "'lldp management-address bind interface vlanif100' in result_display.stdout[0]" - - - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) - ce_lldp: *merged - register: result - - - name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - - - include_tasks: cleanup.yaml - -- debug: - msg: "END ce_lldp merged integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_lldp_interface/defaults/main.yaml b/tests/integration/targets/ce_lldp_interface/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_lldp_interface/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_lldp_interface/meta/main.yml b/tests/integration/targets/ce_lldp_interface/meta/main.yml deleted file mode 100644 index 8b13789179..0000000000 --- a/tests/integration/targets/ce_lldp_interface/meta/main.yml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/integration/targets/ce_lldp_interface/tasks/main.yaml b/tests/integration/targets/ce_lldp_interface/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_lldp_interface/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_lldp_interface/tasks/netconf.yaml b/tests/integration/targets/ce_lldp_interface/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_lldp_interface/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_lldp_interface/tests/netconf/lldp_interface.yaml b/tests/integration/targets/ce_lldp_interface/tests/netconf/lldp_interface.yaml deleted file mode 100644 index d5551a84f8..0000000000 --- a/tests/integration/targets/ce_lldp_interface/tests/netconf/lldp_interface.yaml +++ /dev/null @@ -1,155 +0,0 @@ ---- -- debug: - msg: "START ce_lldp_interface merged integration tests on connection={{ ansible_connection }}" - -- block: - - name: Merge the provided configuration with the exisiting running configuration - basic-tlv - ce_lldp_interface: &merged1 - config: - msg_interval: 8 - ifname: 10GE 1/0/1 - admin_status: txandrx - basic_tlv: - management_addr: true - port_desc: true - system_capability: true - system_description: true - system_name: true - register: result1 - - name: Merge the provided configuration with the existing running configuration (REPEAT) - ce_lldp_interface: *merged1 - register: result2 - - name: "Netconf get operation" - ce_netconf: - rpc: get - cfg_xml: ' - - - - 10GE1/0/1 - - - - - - - - - - - - - ' - register: result3 - - name: Assert the configuration is reflected on host - assert: - that: - - "result1['changed'] == true" - - "result2['changed'] == false" - - "'8' in result3.endstate.result" - - "'txAndRx' in result3.endstate.result" - - "'true' in result3.endstate.result" - - "'true' in result3.endstate.result" - - "'true' in result3.endstate.result" - - - name: Merge the provided configuration with the exisiting running configuration - dot1-tlv - ce_lldp_interface: &merged2 - config: - msg_interval: 8 - ifname: 10GE 1/0/1 - dot1_tlv: - port_vlan_enable: true - port_desc: true - prot_vlan_enable: true - prot_vlan_id: 123 - vlan_name: 234 - vlan_name_enable: true - register: result1 - - name: Merge the provided configuration with the existing running configuration (REPEAT) - ce_lldp_interface: *merged2 - register: result2 - - name: "Netconf get operation" - ce_netconf: - rpc: get - cfg_xml: ' - - - - 10GE1/0/1 - - - - - - - - - - - - - - - - ' - register: result3 - - name: Assert the configuration is reflected on host - assert: - that: - - "result1['changed'] == true" - - "result2['changed'] == false" - - "'true' in result3.endstate.result" - - "'true' in result3.endstate.result" - - "'123' in result3.endstate.result" - - "'true' in result3.endstate.result" - - "'true' in result3.endstate.result" - - "'true' in result3.endstate.result" - - - name: Merge the provided configuration with the exisiting running configuration - dot3-tlv - ce_lldp_interface: &merged - config: - msg_interval: 8 - ifname: 10GE 1/0/1 - dot3_tlv: - eee: true - link_aggregation: true - mac_physic: true - max_frame_size: true - register: result1 - - name: Merge the provided configuration with the existing running configuration (REPEAT) - ce_lldp_interface: *merged - register: result2 - - name: "Netconf get operation" - ce_netconf: - rpc: get - cfg_xml: ' - - - - 10GE1/0/1 - - - - - - - - - - - - - - ' - register: result3 - - name: Assert the configuration is reflected on host - assert: - that: - - "result1['changed'] == true" - - "result2['changed'] == false" - - "'true' in result3.endstate.result" - - "'true' in result3.endstate.result" - - "'123' in result3.endstate.result" - -- debug: - msg: "END ce_lldp_interface merged integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_mdn_interface/defaults/main.yaml b/tests/integration/targets/ce_mdn_interface/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_mdn_interface/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_mdn_interface/tasks/main.yaml b/tests/integration/targets/ce_mdn_interface/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_mdn_interface/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_mdn_interface/tasks/netconf.yaml b/tests/integration/targets/ce_mdn_interface/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_mdn_interface/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_mdn_interface/tests/netconf/ce_mdn_interface.yaml b/tests/integration/targets/ce_mdn_interface/tests/netconf/ce_mdn_interface.yaml deleted file mode 100644 index 4aec853fef..0000000000 --- a/tests/integration/targets/ce_mdn_interface/tests/netconf/ce_mdn_interface.yaml +++ /dev/null @@ -1,97 +0,0 @@ ---- -- debug: - msg: "START ce_mdn_interface presented integration tests on connection={{ ansible_connection }}" -# set up default before test -- name: clean up default configuration with the exisiting running configuration - ce_mdn_interface: - lldpenable: disabled - mdnstatus: disabled - ifname: 10GE1/0/1 - -- name: present the provided configuration with the exisiting running configuration - ce_mdn_interface: &present - lldpenable: enabled - mdnstatus: rxOnly - ifname: 10GE1/0/1 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get mdnInterface config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - 10GE1/0/1 - - - - - " - register: result_xml - -- name: Get lldp enabled config by ce_netconf. - ce_netconf: &get_config_lldp - rpc: get - cfg_xml: " - - - - - - /filter>" - register: result_xml_lldp - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_mdn_interface: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'rxOnly' in result_xml.end_state.result" - - "'enabled' in result_xml_lldp.end_state.result" - -- name: absent the provided configuration with the exisiting running configuration - ce_mdn_interface: &absent - lldpenable: disabled - mdnstatus: disabled - ifname: 10GE1/0/1 - state: absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: absent the provided configuration with the existing running configuration (REPEAT) - ce_mdn_interface: *absent - register: repeat - -- name: Get mdnInterface config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Get lldp enabled config by ce_netconf. - ce_netconf: *get_config - register: result_xml_lldp - -- name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - - "'disabled' not in result_xml.end_state.result" - - "'disabled' in result_xml_lldp.end_state.result" -# after present, isis 100 should be deleted - -- debug: - msg: "END ce_mdn_interface resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_multicast_global/defaults/main.yaml b/tests/integration/targets/ce_multicast_global/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_multicast_global/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_multicast_global/tasks/main.yaml b/tests/integration/targets/ce_multicast_global/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_multicast_global/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_multicast_global/tasks/netconf.yaml b/tests/integration/targets/ce_multicast_global/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_multicast_global/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_multicast_global/tests/netconf/test_ce_multicast_global.yaml b/tests/integration/targets/ce_multicast_global/tests/netconf/test_ce_multicast_global.yaml deleted file mode 100644 index 69b1f5f26b..0000000000 --- a/tests/integration/targets/ce_multicast_global/tests/netconf/test_ce_multicast_global.yaml +++ /dev/null @@ -1,73 +0,0 @@ ---- -- debug: - msg: "START ce_multicast_global presented integration tests on connection={{ ansible_connection }}" - -- name: present the provided configuration with the exisiting running configuration - ce_multicast_global: &present - aftype: v4 - vrf: vpna - weight: 100 - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_multicast_global: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'vpna' in result_xml.end_state.result" - - "'vpna' in result_xml.end_state.result" - -- name: present the provided configuration with the exisiting running configuration - ce_multicast_global: &absent - aftype: v4 - vrf: vpna - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_multicast_global: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'vpna' not in result_xml.end_state.result" - - "'vpna' not in result_xml.end_state.result" -# after present, isis 100 should be deleted - -- debug: - msg: "END ce_multicast_global resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_multicast_igmp_enable/defaults/main.yaml b/tests/integration/targets/ce_multicast_igmp_enable/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_multicast_igmp_enable/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_multicast_igmp_enable/tasks/main.yaml b/tests/integration/targets/ce_multicast_igmp_enable/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_multicast_igmp_enable/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_multicast_igmp_enable/tasks/netconf.yaml b/tests/integration/targets/ce_multicast_igmp_enable/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_multicast_igmp_enable/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_multicast_igmp_enable/tests/netconf/ce_multicast_igmp_enable.yaml b/tests/integration/targets/ce_multicast_igmp_enable/tests/netconf/ce_multicast_igmp_enable.yaml deleted file mode 100644 index 2a94b0a352..0000000000 --- a/tests/integration/targets/ce_multicast_igmp_enable/tests/netconf/ce_multicast_igmp_enable.yaml +++ /dev/null @@ -1,95 +0,0 @@ ---- -- debug: - msg: "START ce_multicast_igmp_enable presented integration tests on connection={{ ansible_connection }}" -# clean up before test -- name: clean up configuration with the exisiting running configuration - ce_multicast_igmp_enable: &absent - aftype: v4 - features: vlan - vlan_id: 100 - igmp: true - version: 2 - proxy: true - - -- name: present the provided configuration with the exisiting running configuration - ce_multicast_igmp_enable: &present - aftype: v4 - features: vlan - vlan_id: 100 - igmp: true - version: 2 - proxy: true - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: Get basic config by ce_netconf. - ce_netconf: &get_config - rpc: get - cfg_xml: " - - - - - - - - - - - - - - " - register: result_xml - - -- name: present the provided configuration with the existing running configuration (IDEMPOTENT) - ce_multicast_igmp_enable: *present - register: repeat - -- name: Assert that the previous task was idempotent - assert: - that: - - "repeat.changed == false" - - "'ipv4unicast' in result_xml.end_state.result" - - "'100' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - - "'2' in result_xml.end_state.result" - - "'true' in result_xml.end_state.result" - -- name: absent the provided configuration with the exisiting running configuration - ce_multicast_igmp_enable: *absent - register: result - - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" - -- name: absent the provided configuration with the existing running configuration (REPEAT) - ce_multicast_igmp_enable: *absent - register: repeat - -- name: Get basic config by ce_netconf. - ce_netconf: *get_config - register: result_xml - -- name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - - "'ipv4unicast' not in result_xml.end_state.result" - - "'100' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" - - "'2' not in result_xml.end_state.result" - - "'true' not in result_xml.end_state.result" -# after present, isis 100 should be deleted - -- debug: - msg: "END ce_multicast_igmp_enable resentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_static_route_bfd/defaults/main.yaml b/tests/integration/targets/ce_static_route_bfd/defaults/main.yaml deleted file mode 100644 index 164afead28..0000000000 --- a/tests/integration/targets/ce_static_route_bfd/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff --git a/tests/integration/targets/ce_static_route_bfd/tasks/main.yaml b/tests/integration/targets/ce_static_route_bfd/tasks/main.yaml deleted file mode 100644 index cc27f174fd..0000000000 --- a/tests/integration/targets/ce_static_route_bfd/tasks/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -- { include: netconf.yaml, tags: ['netconf'] } diff --git a/tests/integration/targets/ce_static_route_bfd/tasks/netconf.yaml b/tests/integration/targets/ce_static_route_bfd/tasks/netconf.yaml deleted file mode 100644 index 73b91adfaa..0000000000 --- a/tests/integration/targets/ce_static_route_bfd/tasks/netconf.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -- name: collect all netconf test cases - find: - paths: "{{ role_path }}/tests/netconf" - patterns: "{{ testcase }}.yaml" - use_regex: true - connection: local - register: test_cases - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=netconf) - include: "{{ test_case_to_run }} ansible_connection=netconf" - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff --git a/tests/integration/targets/ce_static_route_bfd/tests/netconf/ce_static_route_bfd.yaml b/tests/integration/targets/ce_static_route_bfd/tests/netconf/ce_static_route_bfd.yaml deleted file mode 100644 index e333278d91..0000000000 --- a/tests/integration/targets/ce_static_route_bfd/tests/netconf/ce_static_route_bfd.yaml +++ /dev/null @@ -1,150 +0,0 @@ ---- -- debug: - msg: "START ce_static_route_bfd presented integration tests on connection={{ ansible_connection }}" -- include_tasks: cleanup.yaml -- name: Config an ip route-static bfd 10GE1/0/1 3.3.3.3 min-rx-interval 50 min-tx-interval 50 detect-multiplier 5 - ce_static_route_bfd: &merge1 - function_flag: 'singleBFD' - nhp_interface: 10GE1/0/1 - next_hop: 3.3.3.3 - min_tx_interval: 50 - min_rx_interval: 50 - detect_multiplier: 5 - aftype: v4 - state: present - register: result1 -- name: (repeat)Config an ip route-static bfd 10GE1/0/1 3.3.3.3 min-rx-interval 50 min-tx-interval 50 detect-multiplier 5 - ce_static_route_bfd: - <<: *merge1 - register: result2 - -- name: Assert the configuration is reflected on host - assert: - that: - - "result1['changed'] == true" - - "result2['changed'] == false" - - -# ip route-static bfd [ interface-type interface-number | vpn-instance vpn-instance-name ] nexthop-address -- name: ip route-static bfd 10GE1/0/1 3.3.3.4 - ce_static_route_bfd: &merge2 - function_flag: 'singleBFD' - nhp_interface: 10GE1/0/1 - next_hop: 3.3.3.4 - aftype: v4 - register: result1 -- name: (repeat)ip route-static bfd 10GE1/0/1 3.3.3.4 - ce_static_route_bfd: - <<: *merge2 - register: result2 -- name: Assert the configuration is reflected on host - assert: - that: - - "result1['changed'] == true" - - "result2['changed'] == false" -#ip route-static default-bfd { min-rx-interval {min-rx-interval} | min-tx-interval {min-tx-interval} | detect-multiplier {multiplier}} -- name: Config an ip route-static default-bfd min-rx-interval 50 min-tx-interval 50 detect-multiplier 6 - ce_static_route_bfd: &merge3 - function_flag: 'globalBFD' - min_tx_interval: 50 - min_rx_interval: 50 - detect_multiplier: 6 - aftype: v4 - state: present - register: result1 -- name: (repeat)Config an ip route-static default-bfd min-rx-interval 50 min-tx-interval 50 detect-multiplier 6 - ce_static_route_bfd: - <<: *merge3 - register: result2 -- name: Assert the configuration is reflected on host - assert: - that: - - "result1['changed'] == true" - - "result2['changed'] == false" - -- name: undo ip route-static default-bfd - ce_static_route_bfd: &merge4 - function_flag: 'globalBFD' - aftype: v4 - state: absent - commands: 'sys,undo ip route-static default-bfd,commit' - register: result1 -- name: (repeat)undo ip route-static default-bfd - ce_static_route_bfd: - <<: *merge4 - register: result2 -- name: Assert the configuration is reflected on host - assert: - that: - - "result1['changed'] == true" - - "result2['changed'] == false" - -- name: Config an ipv4 static route 2.2.2.0/24 2.2.2.1 preference 1 tag 2 description test for staticBFD - ce_static_route_bfd: &merge5 - function_flag: 'staticBFD' - prefix: 2.2.2.2 - mask: 24 - next_hop: 2.2.2.1 - tag: 2 - description: test - pref: 1 - aftype: v4 - bfd_session_name: btoa - state: present - register: result1 -- name: (repeat) Config an ipv4 static route 2.2.2.0/24 2.2.2.1 preference 1 tag 2 description test for staticBFD - ce_static_route_bfd: - <<: *merge5 - register: result2 -- name: Assert the configuration is reflected on host - assert: - that: - - "result1['changed'] == true" - - "result2['changed'] == false" - -- name: Get lacp config by ce_netconf. - ce_netconf: - rpc: get - cfg_xml: " - - - - - - - - - - - - - - - - - - - " - register: result_present - -- name: Assert that the previous task was idempotent - assert: - that: - - "'v4' == result_present.end_state.result" - - "'10GE1/0/1' == result_present.end_state.result" - - "'Fast' == result_present.end_state.result" - - "'__publiv__' == result_present.end_state.result" - - "'Prority' == result_present.end_state.result" - - "'2.2.2.1' == result_present.end_state.result" - - "'2.2.2.2' == result_present.end_state.result" - - "'12' in result_present.end_state.result" - - "'true' in result_present.end_state.result" - - "'true' in result_present.end_state.result" - - "'true' in result_present.end_state.result" - - "'true' in result_present.end_state.result" - - "'true' in result_present.end_state.result" - - "'1111-2222-3333' in result_present.end_state.result" - - "'123' in result_present.end_state.result" -- include_tasks: cleanup.yaml -- debug: - msg: "END ce_static_route_bfd presentd integration tests on connection={{ ansible_connection }}" diff --git a/tests/integration/targets/ce_static_route_bfd/tests/netconf/cleanup.yaml b/tests/integration/targets/ce_static_route_bfd/tests/netconf/cleanup.yaml deleted file mode 100644 index 10ad3e5c18..0000000000 --- a/tests/integration/targets/ce_static_route_bfd/tests/netconf/cleanup.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -- name: Merge the provided configuration with the exisiting running configuration - ce_static_route_bfd: - function_flag: 'singleBFD' - nhp_interface: 10GE1/0/1 - next_hop: 3.3.3.3 - min_tx_interval: 50 - min_rx_interval: 50 - detect_multiplier: 5 - aftype: v4 - state: absent - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" -- name: ip route-static bfd 10GE1/0/1 3.3.3.4 - ce_static_route_bfd: &merge - function_flag: 'globalBFD' - min_tx_interval: 50 - min_rx_interval: 50 - detect_multiplier: 6 - aftype: v4 - state: absent - register: result - -- name: Assert the configuration is reflected on host - assert: - that: - - "result['changed'] == true" diff --git a/tests/integration/targets/cnos_backup/README.md b/tests/integration/targets/cnos_backup/README.md deleted file mode 100644 index 9b3c7cd8f8..0000000000 --- a/tests/integration/targets/cnos_backup/README.md +++ /dev/null @@ -1,115 +0,0 @@ -# Ansible Role: cnos_backup_sample - Saving the switch configuration to a remote server ---- - - -This role is an example of using the *cnos_backup.py* Lenovo module in the context of CNOS switch configuration. This module allows you to work with switch configurations. It provides a way to back up the running or startup configurations of a switch to a remote server. This is achieved by periodically saving a copy of the startup or running configuration of the network device to a remote server using FTP, SFTP, TFTP, or SCP. - -The results of the operation can be viewed in *results* directory. - -For more details, see [Lenovo modules for Ansible: cnos_backup](http://systemx.lenovofiles.com/help/index.jsp?topic=%2Fcom.lenovo.switchmgt.ansible.doc%2Fcnos_backup.html&cp=0_3_1_0_4_4). - - -## Requirements ---- - - -- Ansible version 2.2 or later ([Ansible installation documentation](http://docs.ansible.com/ansible/intro_installation.html)) -- Lenovo switches running CNOS version 10.2.1.0 or later -- an SSH connection to the Lenovo switch (SSH must be enabled on the network device) - - -## Role Variables ---- - - -Available variables are listed below, along with description. - -The following are mandatory inventory variables: - -Variable | Description ---- | --- -`ansible_connection` | Has to be `network_cli` -`ansible_network_os` | Has to be `cnos` -`ansible_ssh_user` | Specifies the username used to log into the switch -`ansible_ssh_pass` | Specifies the password used to log into the switch -`enablePassword` | Configures the password used to enter Global Configuration command mode on the switch (this is an optional parameter) -`hostname` | Searches the hosts file at */etc/ansible/hosts* and identifies the IP address of the switch on which the role is going to be applied -`deviceType` | Specifies the type of device from where the configuration will be backed up (**g8272_cnos** - G8272, **g8296_cnos** - G8296, **g8332_cnos** - G8332, **NE10032** - NE10032, **NE1072T** - NE1072T, **NE1032** - NE1032, **NE1032T** - NE1032T, **NE2572** - NE2572, **NE0152T** - NE0152T) - -The values of the variables used need to be modified to fit the specific scenario in which you are deploying the solution. To change the values of the variables, you need to visits the *vars* directory of each role and edit the *main.yml* file located there. The values stored in this file will be used by Ansible when the template is executed. - -The syntax of *main.yml* file for variables is the following: - -``` -